UnityShader 阴影投射接收的理解及简单应用

阴影投射的两个要点:
1、接收其他物体阴影的投射
点A在相机的深度值设为 Z1 ,点A在阴影纹理的深度值为Z2 ,如果Z1 >Z2 ,则该点处于相机的可见范围并且处于阴影之中,即该点看得到阴影
UnityShader 阴影投射接收的理解及简单应用_第1张图片
2、向其他物体投射阴影
Unity会在当前物体的Shader中检查:①是否有{“LightMode” = “ShadowCast”,有的话则可以投射阴影。②没有ShadowCast则在Fallback指定的shader中继续寻找。 如果上述两种情况均没有则不向其他物体投射阴影。

代码1: 这段代码是之前 写漫反射时候的代码,唯一不同的是在末尾加了 Fallback"Specular"

Shader "ShaderPath/ShadowShader"//shader的选择路径
{
	Properties//该Shader可控的属性
	{
		_DiffuseColor ("DiffuseColor",Color) = (1,1,1,0)//漫反射的主色调
	}
	SubShader//子着色器
	{
		// 以下均为默认值,详情可查看以往博客
		Cull Back ZWrite On ZTest LEqual

		Pass
		{
			Tags {"LightMode" = "ForwardBase"}
			//与ENDCG相照应,将CG代码包裹
			CGPROGRAM
			//顶点函数定义
			#pragma vertex vert  
			//片元函数定义
			#pragma fragment frag
			//引入必要的Unity库 如下面的UnityObjectToClipPos 就是库中函数
			#include "UnityCG.cginc"
			//引入光照库 _LightColor0需要用
			#include "Lighting.cginc"
			struct appdata
			{
				float4 vertex : POSITION;//每个顶点结构体必须有的
				float3 normal : NORMAL;//定义法线
			};

			struct v2f
			{
				fixed3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float4 pos : SV_POSITION;//每个片元结构体必须有的
			};
			
			fixed4 _DiffuseColor;


			v2f vert (appdata v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);//把顶点从模型空间转换到剪裁空间
				o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));//把法线从模型空间转换到世界空间
				o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;//模型坐标转到世界坐标
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target//返回一个RGBA到模型上
			{
				fixed3 lightDir = UnityWorldSpaceLightDir(i.worldPos);//获取光源在世界空间下的方向(光源发射出来的方向)
				fixed3 diffuse = _LightColor0.rgb * _DiffuseColor * (1+dot(lightDir,i.worldNormal))/2;
				return fixed4(diffuse,1);
			}
			ENDCG
		}
	}
	Fallback"Specular"
}

运行上面代码可以观察到如下结果:我们此时用的是Plane模型(这个是背面剪裁的,也就是从Plane的背面看过去是完全不显示的,这句话后面有用
可看到产生了阴影,因为Specluar里的Fallback “Legacy Shaders/VertexLit” 面含有ShadowCast的Pass,代码如下,PS如果去掉Fallback阴影就消失啦!

Pass {
        Name "ShadowCaster"
        Tags { "LightMode" = "ShadowCaster" }

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma target 2.0
    #pragma multi_compile_shadowcaster
    #pragma multi_compile_instancing // allow instanced shadow pass for most of the shaders
    #include "UnityCG.cginc"

    struct v2f {
        V2F_SHADOW_CASTER;
        UNITY_VERTEX_OUTPUT_STEREO
    };

    v2f vert( appdata_base v )
    {
        v2f o;
        UNITY_SETUP_INSTANCE_ID(v);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
        TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
        return o;
    }

    float4 frag( v2f i ) : SV_Target
    {
        SHADOW_CASTER_FRAGMENT(i)
    }
    ENDCG

    }

以上代码看不懂没关系,因为有很多我们没用过的宏,只要知道这个可以实现阴影即可
官方原Shader代码的下载地址,就是内置Shader源码
下图是Plane正面是朝向光源方向的,此时是会产生阴影的
UnityShader 阴影投射接收的理解及简单应用_第2张图片
当我们把Plane反向,会看到阴影消失了,因为该物体是背面剪裁的,Unity默认的投影方式(Cast Shadow)是On,因此被剪裁的部分是没有阴影的,此时只要根据下面图将Cast Shadow设为Two Sided就可以看到阴影
UnityShader 阴影投射接收的理解及简单应用_第3张图片
UnityShader 阴影投射接收的理解及简单应用_第4张图片

让物体自身能接受阴影

Shader "ShaderPath/ShadowShader"//shader的选择路径
{
	Properties//该Shader可控的属性
	{
		_DiffuseColor ("DiffuseColor",Color) = (1,1,1,0)//漫反射的主色调
	}
	SubShader//子着色器
	{
		// 以下均为默认值,详情可查看以往博客
		Cull Back ZWrite On ZTest LEqual

		Pass
		{
			Tags {"LightMode" = "ForwardBase" }
			//与ENDCG相照应,将CG代码包裹
			CGPROGRAM
			//顶点函数定义
			#pragma vertex vert  
			//片元函数定义
			#pragma fragment frag
			// 必须一定要引入这句编译 不然得不到正确的光照信息,会导致阴影不出现
			#pragma multi_compile_fwdbase	
			//引入必要的Unity库 如下面的UnityObjectToClipPos 就是库中函数
			#include "UnityCG.cginc"
			//引入光照库 _LightColor0需要用
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			struct appdata
			{
				float4 vertex : POSITION;//每个顶点结构体必须有的
				float3 normal : NORMAL;//定义法线
			};

			struct v2f
			{
				fixed3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				SHADOW_COORDS(2) //因为上面用到的最后一个寄存器是TEXCOORD1,因此这里填2,此处是声明一个对阴影纹理采样的坐标
				float4 pos : SV_POSITION;//每个片元结构体必须有的
			};
			
			fixed4 _DiffuseColor;


			v2f vert (appdata v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);//把顶点从模型空间转换到剪裁空间
				o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));//把法线从模型空间转换到世界空间
				o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;//模型坐标转到世界坐标
				TRANSFER_SHADOW(o); //计算在顶点中声明的阴影纹理的坐标
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target//返回一个RGBA到模型上
			{
				fixed3 lightDir = UnityWorldSpaceLightDir(i.worldPos);//获取光源在世界空间下的方向(光源发射出来的方向)
				fixed shadow = SHADOW_ATTENUATION(i);//计算阴影值
				fixed3 diffuse = _LightColor0.rgb * _DiffuseColor * (1+dot(lightDir,i.worldNormal))/2;
				return fixed4(diffuse *shadow,1);
			}
			ENDCG
		}
	}
	Fallback "Specular"
}

**关键代码有四句:**并且都已经在代码中做了注释
1、#pragma multi_compile_fwdbase //确保引入正确的光照信息
2、SHADOW_COORDS(2) //声明阴影纹理的采样坐标
3、TRANSFER_SHADOW(o); //计算阴影纹理的坐标
4、fixed shadow = SHADOW_ATTENUATION(i); //计算阴影值

上述过程其实和对一张图片进行纹理采样是一样的,且听我慢慢说来
2~4步骤可以跟下面三步一一对应,只是此时的阴影纹理是Unity自己生成的不是我们赋值的
1、float2 uv : TEXCOORD3;//用于存储纹理信息
2、o.uv = TRANSFORM_TEX(v.texcoord, _MainTex) ;//计算坐标值
3、fixed3 albedo = tex2D(_MainTex,i.uv).rgb //计算采样后的颜色信息

你可能感兴趣的:(UnityShader)