shadow acne(阴影失真)和peter panning(阴影悬浮)
代码:
https://gitee.com/yichichunshui/mvpmatrix.git
master分支
节点:
8d8cbb5cdf163a3f3b098ce5d292447d623c8bfc
参考网址:https://blog.csdn.net/qq_37484084/article/details/116093693
1、material的队列要选择shader中:
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
这四个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
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