13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

投影テクスチャシャドウを実作してみた

Last updated at Posted at 2018-02-21

##はじめに
この記事の作成は「いけにえと雪のセツナ」グラフィック解説(第2回・グラフィック効果編)を参考させていただきます

非常に勉強になりました

今回は参考記事内容の一つ、投影テクスチャシャドウについて実作してみたいと思います

##シャドウマップ作成
まず、シャドウカメラを生成して、光源と同じ方向を設置する(光視点と考えればいい
Culling Maskを弄って、被写体だけ写せるように
そのあとシャドウカメラのtargetTextureを指定する。
その指定されたRenderTextureは、後に投影テクスチャシャドウが参照するシャドウマップです

image.png

##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
	    }
    }

}

image.png

二つの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");

結果
image.png

##シャドウマップ参照
床に影を映る、そのため
先ずシャドウマップのどこに参照すればいいを知ることが必要

シャドウマップはシャドウカメラ(光視点)で写せる結果
なら床もシャドウカメラ(光視点)で写せ、その描画された位置を参照すればいい

メインカメラでシャドウカメラに描画された位置を求めるには
シャドウカメラのビュー行列とプロジェクション行列が必要


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
	}
}

}

結果
image.png

##リポジトリ
github

13
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?