UNITYで影にテクスチャをはる(スクリーンスペース)
やること
影にテクスチャを張っていこうと思います
具体的には、戦場のヴァルキュリアのような影部分にスクリーンスペースで ハッチングかけます
わかりいくいけど、影部分にハッチングいれてます
ソースはこちら
github.com
考えられる手法
マルチレンダーターゲットを使いポストプロセスで処理
MRTを使い影部分を別で書き出し、ポスプロでコンポーズする
何度か試したが、UNITYではレンダーターゲットを別のものを設定すると
デフォルトのレンダーターゲットが外れ、EarlyDepth等の値が失われ
Zバッファがうまくとれなかった
もう少し研究すれば うまくいくかもしれないが、挫折
ステンシルバッファを使いポスプロで処理
MRTと似たかんじではあるが、レンダーターゲットを変更する必要がない
VPOSを使い通常パスで行う
ShaderModel 3.0からある VPOSセマンティクスを使えば
フラグメントシェーダーで、スクリーンでのポジションを取得することが出来る
その座標を使えば、ポスプロを使わず 通常パスにてスクリーン座標系でのテクスチャマッピングが可能になる
ので この手法で行ってみる
VPOSでの影テクスチャマッピング
テクスチャの指定
このあたりは普通のUNITYなのでサラッと
properties{ _ShadowTex ("ShadowTexture", 2D) = "white" {} } uniform sampler2D _ShadowTex; uniform float4 _ShadowTex_ST;
VPOSの設定
VPOSを使いたいが制限が色々ある
SV_POSITIONと同時に使うことが出来ない
そのため 通常は 頂点シェーダの返却値と フラグメントシェーダの入力値に同じ構造体を使うが
それを別にしなければならない
なるべく構造体増やしたくないので、同じ構造体を使う方法もある
ただし今回は問題があり それが使えないが記しておく
struct v2f{ float4 vertexW: TEXCOORD0; float2 uv : TEXCOORD1; float3 normalWorld : TEXCOORD2; SHADOW_COORDS(3) }; v2f vert(appdata_full v, out float4 vertex: SV_POSITION){ float4 frag(v2f i, float4 pos: VPOS) : SV_Target{
同時に使えないので、構造体には SV_POSITIONもVPOSも使わない
代わりに 頂点シェーダの引数に out属性でSV_POSITION を指定し
フラグメントシェーダの引数に VPOSを指定する
この方法であれば構造体は1つでよく メンテナンスがしやすいが
UNITYでは TRANSFER_SHADOW(o); などのマクロにて
構造体のposメンバを必要とするため、問題が生じる
そのため、不本意だが構造体を2個作る方法にする
struct v2f { float4 pos : SV_POSITION; float4 vertexW: TEXCOORD0; float2 uv : TEXCOORD1; float3 normalWorld : TEXCOORD2; SHADOW_COORDS(3) }; struct v2f_in { UNITY_VPOS_TYPE vpos : VPOS; float4 vertexW: TEXCOORD0; float2 uv : TEXCOORD1; float3 normalWorld : TEXCOORD2; SHADOW_COORDS(3) };
単純に、頂点シェーダの返却値は SV_POSITION、フラグメントシェーダの入力値は VPOS
にすればいい
影の計算
まず、通常の影では濃いので通常影を薄くするため LIGHT_ATTENUATIONの影響を減らし
先程のテクスチャを乗算してみる
SHADOW_ATTENUATIONは影が0で光が1になっているので そのあたりを考慮
また、VPOSはスクリーン座標なのでそれをuv座標 0~1にするには _ScreenParamsで割らなければならない
//LightColor float3 lightCol = _LightColor0.rgb * lerp( 0.5, 1.0, LIGHT_ATTENUATION(i)); i.vpos.xy /= _ScreenParams.xy; float3 shadowTex = tex2D(_ShadowTex, i.vpos.xy) * lerp( 0.5, 0.0, SHADOW_ATTENUATION(i)); return float4( (ambient + diffuse) * tex * (1-shadowTex) + specular, 1.0);
これで 影の部分のみハッチングがかかるようになった
影のパラメータを追加
影や影テクスチャの強さを設定できるように パラメータを2つ追加する
_ShadowPower ("ShadowPower", Range(0.0, 1.0)) = 0.5 _ShadowTexPower ("ShadowTexPower", Range(0.0, 1.0)) = 0.5 //LightColor float3 lightCol = _LightColor0.rgb * lerp( (1-_ShadowPower), 1.0, LIGHT_ATTENUATION(i)); i.vpos.xy /= _ScreenParams.xy; float3 shadowTex = tex2D(_ShadowTex, i.vpos.xy) * lerp( _ShadowTexPower, 0.0, SHADOW_ATTENUATION(i)); return float4( (ambient + diffuse) * tex * (1-shadowTex) + specular, 1.0);
これで影の強さや影テクスチャの強さを変更できるようになった!