再读Internal-DeferredShading
之前在《Unity的Deferred Shading》[1]这篇文章中已经把延迟渲染中用到的默认计算光照的shader过了一遍,时隔一段时间再看,又有一些查漏补缺之处,放在这里记录一下。
两个pass
Internal-DeferredShading 中其实包含了两个pass。第二个pass只会在camera为LDR时生效,用于对_LightBuffer 中的pixel进行decode:
fixed4 frag (v2f i) : SV_Target
{
return -log2(tex2D(_LightBuffer, i.texcoord));
}
UnityComputeShadowFadeDistance
这个Unity内置的API定义如下:
float UnityComputeShadowFadeDistance(float3 wpos, float z)
{
float sphereDist = distance(wpos, unity_ShadowFadeCenterAndType.xyz);
return lerp(z, sphereDist, unity_ShadowFadeCenterAndType.w);
}
unity_ShadowFadeCenterAndType 这个值与shadow projection的设置有关。如果设置为close fit,fade与相机空间的z有关,此时unity_ShadowFadeCenterAndType 是一个零向量:
如果设置为stable fit,fade与阴影衰减的球面中心有关,此时unity_ShadowFadeCenterAndType 中保存的就是球心的世界坐标:
screen space mip-maping
在使用light cookie或者聚光灯光源的情况下,延迟渲染中相邻的两个像素,一个可能位于某个物体边缘,而另一个可能属于背景,这就导致采样light cookie texture的uv坐标差异过大。如果直接使用tex2D,硬件会去取合适的mipmap level的texture进行采样,此时相邻两个像素的uv坐标差距很大,硬件就会使用level很高的mipmap来进行匹配(如下图第4张),导致边缘锯齿。
如上图,直接使用tex2D进行采样,物体边缘的锯齿很明显。我们可以使用tex2Dbias ,指定mipmap level的偏移量:
attenuation *= tex2Dbias(_LightTexture0, float4(uvCookie.xy, 0, -8)).w;
这里偏移量设置为-8,表示就算要采样level为8的mipmap,也会被偏移到level 0。来看下这样改进后的效果:
LDR下的Light Buffer
当camera为LDR模式时,Light Buffer中保存的信息实际上是
2
?
C
2^{-C}
2?C。因此渲染每个光源时,也要以这种格式输出:
float4 color = UNITY_BRDF_PBS(
albedo, specularTint, oneMinusReflectivity, smoothness,
normal, viewDir, light, indirectLight
);
#if !defined(UNITY_HDR_ON)
color = exp2(-color);
#endif
return color;
如果有多个光源,我们希望最终得到的结果是
2
?
(
C
1
+
C
2
+
.
.
.
+
C
n
)
2^{-(C_1 + C_2 + ... + C_n)}
2?(C1?+C2?+...+Cn?)。此时Blend Mode不再是One One,而是DstColor Zero,因为
2
?
C
1
?
2
?
C
2
=
2
?
(
C
1
+
C
2
)
2^{-C_1} \cdot 2^{-C_2} = 2^{-(C_1+C_2)}
2?C1??2?C2?=2?(C1?+C2?)
点光源下的unity_WorldToLight
点光源下unity_WorldToLight 矩阵变换其实等价于在世界空间下从点光源指向物体的向量:
因此以下两种写法其实是等价的:
#if defined(POINT_COOKIE)
float3 uvCookie =
mul(unity_WorldToLight, float4(worldPos, 1)).xyz;
attenuation *=
texCUBEbias(_LightTexture0, float4(uvCookie, -8)).w;
#endif
#if defined(SHADOWS_CUBE)
shadowed = true;
shadowAttenuation = UnitySampleShadowmap(-lightVec);
#endif
Reference
[1] Unity的Deferred Shading
[2] Screenspace vs. mip-mapping
如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路)
|