【UnityShader】視差オクルージョンマッピング【1】 #41
前回の成果
法線マップを理解した。
今回やること
視差オクルージョンマッピングを勉強します。
事前準備
Scene上にQuadを配置します。
以下は今回の制作で使用するテクスチャです。
Main Texture
Height Map
ソースコード
Shader "Unlit/OcclusionMap_1" { Properties { _MainTex ("Main Texture", 2D) = "white" {} _HeightMap("Height Map", 2D) = "white" {} _HeightScale("Height", Float) = 0.5 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass { CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag sampler2D _MainTex; sampler2D _HeightMap; float _HeightScale; struct Vertex { float4 position : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct Vertex2Fragment { float4 position : SV_POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; float3 objectViewDir : TEXCOORD1; float3 objectPos : TEXCOORD2; }; Vertex2Fragment vert(Vertex i) { Vertex2Fragment o; o.position = mul(unity_ObjectToWorld, i.position); o.normal = i.normal; o.uv = i.uv; o.objectViewDir = o.position - _WorldSpaceCameraPos.xyz; o.objectPos = o.position; o.position = mul(UNITY_MATRIX_VP, o.position); return o; } float4 frag(Vertex2Fragment i) : SV_TARGET { float3 rayDir = normalize(i.objectViewDir); float2 uv = i.uv; float rayScale = (-_HeightScale / rayDir.y); float3 rayStep = rayDir * rayScale; uv += rayStep.xz; return tex2D(_MainTex, uv); } ENDCG } } }
一気にやると情報が多いので小分けにしていきます。
視差マッピング
視線や高さを考慮したバンプマッピングのことです。
前回行った、法線マップと比較してみます。
左が視差マッピングで右が法線マップです。
左の方がorgの文字に立体感が出ています。
視差マッピングと法線マップのイメージは以下です。
視差オクルージョンマッピング
視線の向きにあった遮蔽物の前後関係を計算して色を決める手法です。
ハイトマップ
ハイトマップは高さの情報のみを持っているテクスチャです。
黒の箇所は元ポリゴンの高さで、白くなる程高くなっていきます。
視線に応じてuv値を変更させる
まずは視点の位置によって、uv値を変化させます。
最底面との衝突
ハイトマップが黒であれば、視線の先(オレンジ色の矢印)の最底面に衝突することになり、その位置のテクスチャの色を取得します。
今回はハイトマップを使用せずに最低麺位衝突させてuvを変化させます。
以下のソースで最底面を求めています。
_HeightScaleが最底面の深さとなります。
float rayScale = (-_HeightScale / rayDir.y);
float3 rayStep = rayDir * rayScale;
uv += rayStep.xz;
rayScale
何回rayDir.yを乗算すると最底面と衝突するかを求めています。
rayStep
正規化したrayDirとrayScaleを乗算することにより、Rayのベクトルを出します。
それをuvに加算することにより最底面におけるテクスチャの色を求めることができます。
今回の場合、uvはxz平面なのでxzを加算しています。
以下の画像は最底面を求める計算のイメージです。
結果
以下のgifのようになれば成功です。
視線によって見え方が異なっているのがわかります。
ソースコードにコメントを付与
Shader "Unlit/OcclusionMap_1" { Properties { _MainTex ("Main Texture", 2D) = "white" {} _HeightMap("Height Map", 2D) = "white" {} _HeightScale("Height", Float) = 0.5 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass { CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag sampler2D _MainTex; sampler2D _HeightMap; float _HeightScale; struct Vertex { float4 position : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct Vertex2Fragment { float4 position : SV_POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; float3 objectViewDir : TEXCOORD1; float3 objectPos : TEXCOORD2; }; Vertex2Fragment vert(Vertex i) { Vertex2Fragment o; o.position = mul(unity_ObjectToWorld, i.position); o.normal = i.normal; o.uv = i.uv; // 視線ベクトル o.objectViewDir = o.position - _WorldSpaceCameraPos.xyz; o.objectPos = o.position; o.position = mul(UNITY_MATRIX_VP, o.position); return o; } float4 frag(Vertex2Fragment i) : SV_TARGET { float3 rayDir = normalize(i.objectViewDir); float2 uv = i.uv; // 何回乗算すれば最底面(_HeightScale)と衝突するか float rayScale = (-_HeightScale / rayDir.y); // 視線ベクトルと乗算してベクトルの大きさを求める float3 rayStep = rayDir * rayScale; // xz平面 uv += rayStep.xz; return tex2D(_MainTex, uv); } ENDCG } } }
今回は以上となります。
ここまでご視聴ありがとうございました。