RenderTextureFormat.Depth和RenderTextureFormat.Shadowmap两种格式的深度图

shadow acne(阴影失真)和peter panning(阴影悬浮)

代码:
https://gitee.com/yichichunshui/mvpmatrix.git
master分支
节点:
8d8cbb5cdf163a3f3b098ce5d292447d623c8bfc

参考网址:https://blog.csdn.net/qq_37484084/article/details/116093693

1、material的队列要选择shader中:
RenderTextureFormat.Depth和RenderTextureFormat.Shadowmap两种格式的深度图_第1张图片
2、产生深度的shader

Shader "LearningShadow/SMCaster"{
    SubShader{
        Fog{Mode Off}
        Lighting Off
        ColorMask 0
        Cull Front

        Pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct v2f {
                float4 pos : SV_POSITION;
                float2 depth : TEXCOORD0;
            };

            v2f vert(appdata_base v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                UNITY_TRANSFER_DEPTH(o.depth);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target{
                UNITY_OUTPUT_DEPTH(i.depth);
            }
            ENDCG
        }
    }
}


UNITY_TRANSFER_DEPTH和
UNITY_OUTPUT_DEPTH
在这里插入图片描述
UNITY_TRANSFER_DEPTH居然是空实现;UNITY_OUTPUT_DEPTH返回的是0

3、开深度图
shadowTexture = new RenderTexture(shadowResolution, shadowResolution, 24, RenderTextureFormat.Depth);

4、采样深度之后,要判断是否使用了UNITY_REVERSED_Z

float2 uv = i.shadowPos.xy / i.shadowPos.w;
				uv = uv * 0.5 + 0.5;
				float depth = SAMPLE_DEPTH_TEXTURE(_gShadowTexture, uv);
				#if defined(UNITY_REVERSED_Z)
				depth = 1 - depth;
				#endif

点转换到灯光空间计算深度:

//计算灯光坐标系下的片元深度
				float lightCoordDepth = i.shadowPos.z / i.shadowPos.w;
			#if defined(SHADER_TARGET_GLSL)
				lightCoordDepth = lightCoordDepth * 0.5 + 0.5;
			#elif defined(UNITY_REVERSED_Z)
				lightCoordDepth = 1 - lightCoordDepth;
			#endif

然后比较:

//偏移采样深度,避开shadow acne
				float shadow = (depth < lightCoordDepth - bias) ? 0 : 1;

当计算的深度lightCoordDepth ,减去bias之后,还大于采样的深度depth,则表明此点在阴影中。

完整的shader:

Shader "LearningShadow/SMShadow"{
	SubShader{
		Tags { "RenderType" = "Opaque" }

		Pass{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			struct v2f {
				float4 pos : SV_POSITION;
				float4 shadowPos : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float3 worldNormal : TEXCOORD2;
			};

			float4x4 _gWorldToLight;

			v2f vert(appdata_base v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				//计算顶点的世界坐标
				float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
				//计算顶点的灯光坐标系坐标
				o.shadowPos = mul(_gWorldToLight, worldPos);

				o.worldPos = worldPos;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				return o;
			}

			sampler2D _gShadowTexture;
			float _gShadowBias;

			fixed4 frag(v2f i) : SV_Target{
				//构建灯光坐标系下的NDC坐标,作为UV进行深度采样
				float2 uv = i.shadowPos.xy / i.shadowPos.w;
				uv = uv * 0.5 + 0.5;
				float depth = SAMPLE_DEPTH_TEXTURE(_gShadowTexture, uv);
				#if defined(UNITY_REVERSED_Z)
				depth = 1 - depth;
				#endif

				//计算灯光坐标系下的片元深度
				float lightCoordDepth = i.shadowPos.z / i.shadowPos.w;
			#if defined(SHADER_TARGET_GLSL)
				lightCoordDepth = lightCoordDepth * 0.5 + 0.5;
			#elif defined(UNITY_REVERSED_Z)
				lightCoordDepth = 1 - lightCoordDepth;
			#endif
				//指向灯光方向
				float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				//bias = tan(cos(n·l)),其中n为法向,l指向灯光
				//我们使用acos(n·l)来获取夹角,然后在求正交值
				float bias = _gShadowBias * tan(acos(saturate(dot(worldLightDir, i.worldNormal))));
				bias = clamp(bias, 0, 0.01);

				//偏移采样深度,避开shadow acne
				float shadow = (depth < lightCoordDepth - bias) ? 0 : 1;

				float3 worldN = normalize(i.worldNormal);
				float3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos));
				float lambert = saturate(dot(worldN, worldLight));

				float3 col = UNITY_LIGHTMODEL_AMBIENT.xyz + shadow * lambert * _LightColor0.rgb;
				return fixed4(col, 1);
			}
			ENDCG
		}
	}
}

5、bias的原因
https://blog.csdn.net/lawest/article/details/106364935

会出现阴影失真的根本原因是所生成的深度贴图的分辨率是有限的,就会造成在fragment位置处取深度图中的值发生采样就会出现问题,这里我引用知乎用户王十一的回答用的图(懒得自己画)。如图:以四个格子为四个fragment为例,由于深度图分辨率有限四个格子会采样同一个深度值(由于纹理参数设置的是临近GL_NEAREST,设置线性也会有这个问题)。
————————————————
版权声明:本文为CSDN博主「lawest」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lawest/article/details/106364935

如图假设采样这个四方格的正中心,假设这个深度值为10
RenderTextureFormat.Depth和RenderTextureFormat.Shadowmap两种格式的深度图_第2张图片
RenderTextureFormat.Depth和RenderTextureFormat.Shadowmap两种格式的深度图_第3张图片

这四个fragment分别取名a,b,c,d,由于光源的位置会导致求得的四个距离会不一样,先求a到光源的距离假设为9.8<10,b到光源的距离是11.6 >10,c到光源的距离12.1>10,d到光源的距离9.5<10,。就会导致a和d亮,b和c暗,他们本来应该都是亮的才对。然后整个光源视椎体下各个片段都会出现这个问题,就会出现上面的明暗交错的条纹。

原文说的解决方法是将fragment到光源的距离适当的缩小bias,就会避免这个问题,但是这种解决方法会带来另外一个问题,那就是peter panning(阴影悬浮),bias越大悬浮的越厉害。
————————————————
版权声明:本文为CSDN博主「lawest」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lawest/article/details/106364935

RenderTextureFormat.Depth和RenderTextureFormat.Shadowmap两种格式的深度图_第4张图片
6、如果使用RenderTextureFormat.Shadowmap
则采样的使用,要使用如下的方法采样:
shadowmap采样
https://gameinstitute.qq.com/community/detail/124214

Shader "LearningShadow/SMShadow"{
	SubShader{
		Tags { "RenderType" = "Opaque" }

		Pass{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			struct v2f {
				float4 pos : SV_POSITION;
				float4 shadowPos : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float3 worldNormal : TEXCOORD2;
			};

			float4x4 _gWorldToLight;

			v2f vert(appdata_base v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				//计算顶点的世界坐标
				float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
				//计算顶点的灯光坐标系坐标
				o.shadowPos = mul(_gWorldToLight, worldPos);

				o.worldPos = worldPos;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				return o;
			}

			//UNITY_DECLARE_SHADOWMAP(_gShadowTexture);
			//sampler2D _gShadowTexture;
			float _gShadowBias;
			//Texture2D _gShadowTexture;
			//SamplerComparisonState sampler_gShadowTexture;


			uniform Texture2D _gShadowTexture;
			uniform SamplerState sample_gShadowTexture;
			Texture2D fakePoint;
			SamplerState samplerfakePoint;

			//使用这种方式采样shadowmap格式的rt,参考:https://gameinstitute.qq.com/community/detail/124214
			float Sample(float2 uv)
			{
				float depth = fakePoint.Sample(samplerfakePoint,float2(0,0)).a; //建立采样器/  
				for (int j = 0; j < 5; j++)
				{
					depth += _gShadowTexture.Sample(samplerfakePoint, uv).r;			//对该点进行反复采样/  
				}
				depth -= fakePoint.Sample(samplerfakePoint, float2(0, 0)).a;    //去除多余量/  
				depth = depth / 5.f;                                            //对采样总和取平均值/  
				return depth;
			}


			fixed4 frag(v2f i) : SV_Target{
				//构建灯光坐标系下的NDC坐标,作为UV进行深度采样
				float2 uv = i.shadowPos.xy / i.shadowPos.w;
				uv = uv * 0.5 + 0.5;
				//float depth = SAMPLE_DEPTH_TEXTURE(_gShadowTexture, uv);
				float depth = Sample(uv);
				#if defined(UNITY_REVERSED_Z)
				depth = 1 - depth;
				#endif

				//计算灯光坐标系下的片元深度
				float lightCoordDepth = i.shadowPos.z / i.shadowPos.w;
			#if defined(SHADER_TARGET_GLSL)
				lightCoordDepth = lightCoordDepth * 0.5 + 0.5;
			#elif defined(UNITY_REVERSED_Z)
				lightCoordDepth = 1 - lightCoordDepth;
			#endif
				//指向灯光方向
				float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				//bias = tan(cos(n·l)),其中n为法向,l指向灯光
				//我们使用acos(n·l)来获取夹角,然后在求正交值
				float bias = _gShadowBias * tan(acos(saturate(dot(worldLightDir, i.worldNormal))));
				bias = clamp(bias, 0, 0.01);

				//偏移采样深度,避开shadow acne
				//float shadow = UNITY_SAMPLE_SHADOW(_gShadowTexture, i.shadowPos.xyz);
				float shadow = (depth < lightCoordDepth - bias) ? 0 : 1;
				//float shadow = _gShadowTexture.SampleCmpLevelZero(sampler_gShadowTexture, uv, lightCoordDepth);
				//return shadow;

				float3 worldN = normalize(i.worldNormal);
				float3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos));
				float lambert = saturate(dot(worldN, worldLight));

				float3 col = UNITY_LIGHTMODEL_AMBIENT.xyz + shadow * lambert * _LightColor0.rgb;
				return fixed4(col, 1);
			}
			ENDCG
		}
	}
}

代码:
https://gitee.com/yichichunshui/mvpmatrix.git
master分支
节点:
56756bd0386bbabee78f24f14e2910966557bf34

你可能感兴趣的:(Unity,unity,游戏引擎)