深度纹理定义:深度纹理实际上是一种渲染纹理,纹理中的每一个像素值分别存储的是高精度的深度值,而该深度值又是由NDC(统一设备坐标系)中的z分量值来提供,具体的计算公式如下:
深度纹理实现方式:实现方式如下:
1.通过设置渲染路径为延迟渲染路径,将深度信息存储在几何缓冲区中,从而通过直接访问深度缓存来实现。
2.通过在单独的pass中利用着色器替换技术,将满足渲染类型为Opaque,渲染队列小于等于2500,LightModel为ShadowCaster的顶点的深度渲染到深度纹理中来实现。
深度纹理获取深度方式:获取方式如下:
1.使用camera.depthTextureMode = DepthTextureMode.Depth来设置为深度纹理,而在shader中对_CameraDepthTexture用SAMPLE_DEPTH_TEXTURE进行采样获取非线性深度值,然后对这个值用LinearEyeDepth函数来获取线性视角空间深度值,最后使用Linear01Depth函数来获取范围在[0,1]之间的线性深度值。
2.使用camera.depthTextureMode = DepethTextureMode.DepthNormals来设置为深度法线纹理,而在shader中对_CameraDepthNormalTexture用SAMPLE_DEPTH_TEXTURE进行采样获取非线性深度值和法线值,然后使用DecodeFloatRG来解码线性深度值,使用DecodeViewNormalStereo来解码视角空间下的法线值。当然也可以使用DecodeDepthNormal来统一解码获取线性深度值以及视角空间下的法线值。
获取像素在世界空间下位置方式:获取方式如下:
1.使用视角投影矩阵对像素进行转换,从而获取世界空间下像素的位置。
2.使用世界空间下摄像机位置加上摄像机深度纹理中当前顶点的线性深度值乘以像素距离摄像机的射线信息,从而获取世界空间下像素的位置。
鉴于以上两种方案,我们应该使用第二种方案,因为不存在矩阵变换的消耗。
使用深度纹理实现运动模糊特效:使用视角投影矩阵对像素进行变化获取世界空间下的位置,然后计算前一帧和当天帧的位置差,产生该像素的速度,利用速度映射来获取模糊效果。核心代码如下:
v2f vert(appdata_img v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
o.uv_depth = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv_depth.y = 1 - o.uv_depth.y;
#endif
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get the depth buffer value at this pixel.
float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
// H is the viewport position at this pixel in the range -1 to 1.
float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
// Transform by the view-projection inverse.
float4 D = mul(_CurrentViewProjectionInverseMatrix, H);
// Divide by w to get the world position.
float4 worldPos = D / D.w;
// Current viewport position
float4 currentPos = H;
// Use the world position, and transform by the previous view-projection matrix.
float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);
// Convert to nonhomogeneous points [-1,1] by dividing by w.
previousPos /= previousPos.w;
// Use this frame's position and last frame's to compute the pixel velocity.
float2 velocity = (currentPos.xy - previousPos.xy)/2.0f;
float2 uv = i.uv;
float4 c = tex2D(_MainTex, uv);
uv += velocity * _BlurSize;
for (int it = 1; it < 3; it++, uv += velocity * _BlurSize) {
float4 currentColor = tex2D(_MainTex, uv);
c += currentColor;
}
c /= 3;
return fixed4(c.rgb, 1.0);
}
全局雾效实现方式:利用屏幕后处理技术,将渲染完成的场景图像利用线性,指数,指数平方的计算方式进行获取,其中计算公式如下图:
其中线性方式实现雾效核心代码如下:
v2f vert(appdata_img v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
o.uv_depth = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv_depth.y = 1 - o.uv_depth.y;
#endif
int index = 0;
if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
index = 0;
} else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
index = 1;
} else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
index = 2;
} else {
index = 3;
}
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
index = 3 - index;
#endif
o.interpolatedRay = _FrustumCornersRay[index];
return o;
}
fixed4 frag(v2f i) : SV_Target {
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
fogDensity = saturate(fogDensity * _FogDensity);
fixed4 finalColor = tex2D(_MainTex, i.uv);
finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
return finalColor;
}
使用深度法线纹理实现边缘检测特效:深度法线纹理中每个像素都计算Roberts卷积核中对角方向的深度或者法线值并比较它们之间的差值,如果超过某个阈值就说明它们之间存在一个边。其中核心代码如下:
v2f vert(appdata_img v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
uv.y = 1 - uv.y;
#endif
o.uv[1] = uv + _MainTex_TexelSize.xy * half2(1,1) * _SampleDistance;
o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1,-1) * _SampleDistance;
o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1,1) * _SampleDistance;
o.uv[4] = uv + _MainTex_TexelSize.xy * half2(1,-1) * _SampleDistance;
return o;
}
half CheckSame(half4 center, half4 sample) {
half2 centerNormal = center.xy;
float centerDepth = DecodeFloatRG(center.zw);
half2 sampleNormal = sample.xy;
float sampleDepth = DecodeFloatRG(sample.zw);
// difference in normals
// do not bother decoding normals - there's no need here
half2 diffNormal = abs(centerNormal - sampleNormal) * _Sensitivity.x;
int isSameNormal = (diffNormal.x + diffNormal.y) < 0.1;
// difference in depth
float diffDepth = abs(centerDepth - sampleDepth) * _Sensitivity.y;
// scale the required threshold by the distance
int isSameDepth = diffDepth < 0.1 * centerDepth;
// return:
// 1 - if normals and depth are similar enough
// 0 - otherwise
return isSameNormal * isSameDepth ? 1.0 : 0.0;
}
fixed4 fragRobertsCrossDepthAndNormal(v2f i) : SV_Target {
half4 sample1 = tex2D(_CameraDepthNormalsTexture, i.uv[1]);
half4 sample2 = tex2D(_CameraDepthNormalsTexture, i.uv[2]);
half4 sample3 = tex2D(_CameraDepthNormalsTexture, i.uv[3]);
half4 sample4 = tex2D(_CameraDepthNormalsTexture, i.uv[4]);
half edge = 1.0;
edge *= CheckSame(sample1, sample2);
edge *= CheckSame(sample3, sample4);
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[0]), edge);
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
}