##はじめに
この記事の作成は「いけにえと雪のセツナ」グラフィック解説(第2回・グラフィック効果編)を参考させていただきます
非常に勉強になりました
今回は参考記事内容の一つ、投影テクスチャシャドウについて実作してみたいと思います
##シャドウマップ作成
まず、シャドウカメラを生成して、光源と同じ方向を設置する(光視点と考えればいい
Culling Maskを弄って、被写体だけ写せるように
そのあとシャドウカメラのtargetTextureを指定する。
その指定されたRenderTextureは、後に投影テクスチャシャドウが参照するシャドウマップです
##SetReplacementShader
シャドウマップに描き込むカラーは影の色だけで
そのため、一々被写体のmaterialを切り替えるのも面倒な作業
なにかいい方法ありますかと調べたら
SetReplacementShader まんま願いをかなえてくれそうです
SetReplacementShader
SetReplacementShaderをシンプルに考えると、shader versionのoverrideみたいなもの
SetReplacementShader第2引数が指定したタグを判断条件
差し替えるShaderとmaterial指定したShader タグの値が同じなら
そのまま置き換える
例として シーンの中にCubeとSphereがいる
それぞれ別のshaderを使用する
DemoForShaderReplace.cginc
#ifndef _DEMO_FOR_SHADERREPLACE
#define _DEMO_FOR_SHADERREPLACE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return tex2D(_MainTex, i.uv);
}
#endif //_DEMO_FOR_SHADERREPLACE
DemoSphere.shader
Shader "Unlit/DemoSphere"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" "Name"="Sphere" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "DemoForShaderReplace.cginc"
ENDCG
}
}
DemoCube.shader
Shader "Unlit/DemoCube"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" "Name"="Cube" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "DemoForShaderReplace.cginc"
ENDCG
}
}
}
二つのshaderはほぼ同じですが、唯一の差別はカスタムタグ Name の値が違う
次は差し替えるshader
DemoReplace.shader
Shader "Unlit/DemoReplace"
{
SubShader
{
Tags { "Name"="Cube" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_Replace
#include "DemoForShaderReplace.cginc"
fixed4 frag_Replace (v2f i) : SV_Target
{
return fixed4(1,0,0,1);
}
ENDCG
}
}
SubShader
{
Tags { "Name"="Sphere" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_Replace
#include "DemoForShaderReplace.cginc"
fixed4 frag_Replace (v2f i) : SV_Target
{
return fixed4(0,1,0,1);
}
ENDCG
}
}
}
差し替えるshaderはカスタムタグ Name ごとに
別々の差し替えたい描画を定義する
例はCubeを赤に Sphereを緑に
最後は camera.SetReplacementShader 呼び出す
Camera.main.SetReplacementShader (Shader.Find("Unlit/DemoReplace"), "Name");
##シャドウマップ参照
床に影を映る、そのため
先ずシャドウマップのどこに参照すればいいを知ることが必要
シャドウマップはシャドウカメラ(光視点)で写せる結果
なら床もシャドウカメラ(光視点)で写せ、その描画された位置を参照すればいい
メインカメラでシャドウカメラに描画された位置を求めるには
シャドウカメラのビュー行列とプロジェクション行列が必要
Matrix4x4 view = shadowCamera.worldToCameraMatrix;
Matrix4x4 proj = GL.GetGPUProjectionMatrix (shadowCamera.projectionMatrix, true);
Shader.SetGlobalMatrix("_ProjectionViewProj", proj*view);
二つの行列を掛ける その結果をshaderに伝えて
最後にshaderで計算
Shader "Shadow/ProjectionShadowGround"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 uv_shadow : TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _ProjectionShadowMap;
float4x4 _ProjectionViewProj;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float4 world = mul (unity_ObjectToWorld, v.vertex);
float4 pos = mul (_ProjectionViewProj, world);
o.uv_shadow = ComputeScreenPos (pos);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 shadow = tex2D(_ProjectionShadowMap, (i.uv_shadow/i.uv_shadow.w).xy);
return lerp(col, shadow, shadow.a);
}
ENDCG
}
}
}
##リポジトリ
github