关于正交相机的屏幕空间反射
这两天一个老外要求哥的 LWRP/URP SSR Water 能够支持 正交相机。
A good asset if you use perspective camera. Unfortunately it doesn’t work correctly with orthographic one which I use in my project. Will change my rating once orthographic is also supported.
下图是我添加支持后 透视相机 和 正交相机 的反射效果对比:
透视相机:
正交相机:
主要修改点
关于深度的计算
透视相机 和 正交相机 的深度计算,最主要的差别是 非线性 和 线性 的差别。
以从 _CameraDepthTexture 获取场景深度为例,透视相机 的写法如下:
1
2
float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, screenMatchUVZ.xy);
float sceneDepth = LinearEyeDepth(rawDepth, _ZBufferParams);
其中,LinearEyeDepth 是Unity的内置函数,代码如下:
1
2
3
4
5
6
7
8
// Z buffer to linear depth.
// Does NOT correctly handle oblique view frustums.
// Does NOT work with orthographic projection.
// zBufferParam = { (f-n)/n, 1, (f-n)/n*f, 1/f }
float LinearEyeDepth(float depth, float4 zBufferParam)
{
return 1.0 / (zBufferParam.z * depth + zBufferParam.w);
}
如果是 正交相机,直接对远近平面 线性插值 就可以搞定了,代码如下:
1
2
3
4
5
6
7
8
9
10
inline float GetOrthoDepthFromZBuffer (float rawDepth)
{
#if defined(UNITY_REVERSED_Z)
#if UNITY_REVERSED_Z == 1
rawDepth = 1.0f - rawDepth;
#endif
#endif
return lerp(_ProjectionParams.y, _ProjectionParams.z, rawDepth);
}
1
2
float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, screenMatchUVZ.xy);
float sceneDepth = GetOrthoDepthFromZBuffer(rawDepth);
此外,光线步进 时的 深度比较 计算,情况也很类似:
透视相机的写法:
1
2
3
4
5
6
// 计算起点和终点
clipPos = TransformWorldToHClip(worldPos);
float k = 1.0 / clipPos.w;
screenPos.xy = ComputeScreenPos(clipPos).xy * k;
screenPos.z = k;
1
2
// 光线步进
half rayDepth = 1.0 / screenMatchUVZ.z;
正交相机的写法:
1
2
3
4
5
// 计算起点和终点
clipPos = TransformWorldToHClip(worldPos);
screenPos.xy = ComputeScreenPos(clipPos).xy;
screenPos.z = GetOrthoEyeDepth(clipPos.z);
1
2
// 光线步进
half rayDepth = screenMatchUVZ.z;
关于视线向量的计算
屏幕空间反射 依赖 视线向量 相对于 法线 的 反射向量。
为了 正交相机 下反射结果不随相机观察角度而变形,我们还需要区分 视线向量 的计算方式。
透视相机的写法:
1
lightingData.worldViewDir = normalize(_WorldSpaceCameraPos.xyz - lightingData.worldPos);
正交相机的写法:
1
lightingData.worldViewDir = normalize(UNITY_MATRIX_V[2].xyz);
参考
在编码过程中,我参考了 Lux URP/LWRP Essentials 的部分写法,再次墙裂推荐这个插件。
好了,拜拜!