Unity Shader总结(九)——阴影

屏幕空间的阴影投射

unity会调用lightmode为ShadowCaster的Pass来得到可投射阴影的光源的阴影投射纹理和摄像机的深度纹理,然后根据它们的纹理来得到屏幕空间的阴影图,如果摄像机的深度纹理记录的深度值大于转换到阴影投射纹理中的深度值,就证明该表面在阴影中。
如果想要物体接受投影,只需要在shader中对阴影图采样,首先把表面坐标从模型空间变换到屏幕空间中,然后使用这个坐标对阴影图进行采样。

不透明物体投射阴影

通用过Mesh Renderer组件中的Cast Shadows 和 Receive Shadows属性控制投射或接受阴影。
有的shader虽然没有使用ShadowCaster的Pass但仍然可以投射阴影,是因为fallback中最后会调用到unity内置的VertexLit,里面包含了该Pass

让物体接收阴影

需要注意的:里面的宏辉使用上下文变量来计算,如 TRANSFER_SHADOW必须保证a2v的顶点坐标变量名为vertex,v2f中的顶点位置变量名为pos

Shader "Unity Shaders Book/Chapter 9/Shadow" {
   
   
	Properties {
   
   
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
   
   
		Tags {
   
    "RenderType"="Opaque" }
		
		Pass {
   
   
			// Pass for ambient light & first pixel light (directional light)
			Tags {
   
    "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			// Apparently need to add this declaration 
			#pragma multi_compile_fwdbase	
			
			#pragma vertex vert
			#pragma fragment frag
			
			// Need these files to get built-in macros
			#include "Lighting.cginc"
			//计算阴影的宏在该文件中
			#include "AutoLight.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
   
   
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
   
   
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				//声明一个用于对阴影纹理采样的坐标,参数必须是下一个可用的插值寄存器的索引值
				SHADOW_COORDS(2)
			};
			
			v2f vert(a2v v) {
   
   
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 	
			 	o.worldNormal = UnityObjectToWorldNormal(v.normal);

			 	o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			 	
			 	// Pass shadow coordinates to pixel shader
				//计算v2f中声明的阴影纹理坐标,如果平台可以使用屏幕空间的阴影投射技术(判断是否定义UNITY_NO_SCREENSPACE_SHADOWS)
				//如果定义了,就会调用ComputerScreenPos函数来计算_ShadowCoord,把顶点坐标从模型空间变换到光源空间后存储到_ShadowCoord中
			 	TRANSFER_SHADOW(o);
			 	
			 	return o;
			}
			
			fixed4 frag(v2f i) 
### 如何在Unity中通过Shader实现阴影投影 #### 创建基础着色器 为了使对象能够投射阴影,在编写自定义Shader时,必须创建一个特定于`ShadowCaster`模式的Pass。这允许几何体影响场景中的光照计算而不渲染颜色[^2]。 ```csharp SubShader { Tags { "RenderType"="Opaque" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" } CGPROGRAM ... ENDCG } // Shadow casting pass Pass { Name "SHADOWCASTER" Tags {"LightMode" = "ShadowCaster"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; void vert (appdata v) { UNITY_SETUP_INSTANCE_ID(v); float4 pos = UnityObjectToClipPos(v.vertex); o.pos = UnityApplyLinearShadowBias(pos); } half4 frag () : SV_Target { return 0; } ENDCG } } ``` 此代码片段展示了如何设置用于生成阴影的地图数据流路径。注意这里使用了`UnityApplyLinearShadowBias()`函数来防止 peter-panning 效果——即当物体非常接近接收面时可能出现的一种伪影现象[^1]。 #### 接收阴影的对象配置 对于想要显示来自其他物体所投下阴影表面,则需为其指定专门处理接收到阴影逻辑的新材质与Shader。通常情况下会采用Unlit类型的模板作为起点并加以修改以适应具体需求[^3]。 ```csharp Shader "Custom/ShadowReceiver"{ SubShader { Tags{ "Queue" = "Geometry"} Pass{ Name "BASE" Tags{"LightMode"="Always"} CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _MainTex; struct VertexInput { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct VertexOutput { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; VertexOutput vert(VertexInput input){ VertexOutput output; output.pos = UnityObjectToClipPos(input.vertex); output.uv = TRANSFORM_TEX(input.uv, _MainTex); return output; } fixed4 frag(VertexOutput i):COLOR{ fixed4 col = tex2D(_MainTex,i.uv); // Apply shadow attenuation to the color. float shadowAtten = SHADOW_ATTENUATION(i); col.rgb *= shadowAtten; return col; } ENDCG } } } ``` 上述脚本说明了一个简单的接受者Shader的工作方式,它读取纹理贴图的同时考虑到了当前像素位置处是否有任何遮挡物造成的暗度变化,并据此调整最终输出的颜色亮度值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值