在 上一篇 中试图在HDRP中使用自定义的Shader来实现贴花效果,但是在读取深度图来获取深度值时出现了问题,在HDRP中深度图的组织方式发生了改变,变成了多个lod的深度图的图集,像下图这样 。
因此使用原有的 SAMPLE_DEPTH_TEXTURE
方法获取到的深度值变的不正确了。通过在ScriptableRenderPipeline工程的源码中查找加翻阅,发现在 ShaderPassDBuffer.hlsl
中使用了 LOAD_TEXTURE2D(_CameraDepthTexture, input.positionSS.xy).x
来获取深度值,因为是hlsl代码,所以要用hlsl的方式来使用。
读取深度值
要 do it in a hlsl way, 我们需要做以下几项修改:
- 把 CGPROGRAM/ENDCG 修改成 HLSLPROGRAM/ENDHLSL,或 CGINCLUDE/ENDCG 修改成 HLSLINCLUDE/ENDHLSL。
- 在 HLSLINCLUDE/ENDHLSL 中加入
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl"
,引用其他文件。当然还有必备的#include "UnityCG.cginc"
。 sampler2D _CameraDepthTexture;
修改为TEXTURE2D(_CameraDepthTexture);
,HLSL中声明采样器的方式和CG不同,需要用TEXTURE2D
而不是sampler2D
,在D3D11.hlsl
,Metal.hlsl
,GLES2.hlsl
等这些文件中都有相应的宏定义。
从源码中可以看到只有 GLES2.hlsl
中把 TEXTURE2D
定义成了 sampler2D
,其余的平台则都定义成了 Texture2D
对象。这也是卡了我好几天的地方,当时怎么根据报错提示都没找到问题所在,还是在查看了一些源码后法线应该这么用。教训就是先别急着查报错提示,耐心看看代码的话可能这都不是问题,要引以为戒啊。 这也是卡了我好几天的地方,当时怎么根据报错提示都没找到问题所在,还是在查看了一些源码后法线应该这么用。教训就是先别急着查报错提示,耐心看看代码的话可能这都不是问题,要引以为戒啊。
- 计算屏幕坐标,而不是以前使用的纹理坐标。这里使用
ComputeScreenPos
方法的计算结果和_ScreenParams.xy
的乘积即可获得。
做好了这些修改之后就可以使用 LOAD_TEXTURE2D
或者 LOAD_TEXTURE2D_LOD
方法,对 _CameraDepthTexture
或者 _DepthPyramidTexture
对象进行采样来获取深度值了。具体有以下四种方式:
float depth = LOAD_TEXTURE2D_LOD(_CameraDepthTexture, screenPos, 0).r;
float depth = LOAD_TEXTURE2D(_CameraDepthTexture, screenPos);
float depth = LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, TexCoordStereoOffset(screenPos), 0).r;
float depth = LOAD_TEXTURE2D(_DepthPyramidTexture, screenPos).r;
现在获取到的深度值是非线性的,如果需要变换成线性的深度值则需要再加一句:
depth = Linear01Depth(depth);
_CameraDepthTexture 和 _DepthPyramidTexture
_CameraDepthTexture
图里现在是多个lod深度图的集合,而 _DepthPyramidTexture
看名字叫深度图金字塔图,感觉应该也是一个多个图片的集合。在FrameDebugger中查看渲染事件,发现 _CameraDepthTexture
和 _DepthPyramidTexture
来起来好像没啥区别,再看一下对应的buffer名字,都是 CameraDepthBufferMipChain_960x764_RFloat
,感觉应该就是同一块现存,在渲染管线中被赋值给了不同名称的图,如果有知道的同学请留言赐教哈,先感谢了。
以下是完整Shader代码:
Shader "MJ/ForwardDecal_HDRP"
{
Properties
{
_MainTex ("Decal Texture", 2D) = "white" {}
}
HLSLINCLUDE
#include "UnityCG.cginc"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl"
ENDHLSL
SubShader
{
Tags{ "Queue"="Geometry+1" }
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
HLSLPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
struct v2f
{
float4 pos : SV_POSITION;
float4 screenUV : TEXCOORD0;
float3 ray : TEXCOORD1;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos (v.vertex);
o.screenUV = ComputeScreenPos (o.pos);
o.ray = UnityObjectToViewPos(v.vertex).xyz * float3(-1,-1,1);
return o;
}
sampler2D _MainTex;
TEXTURE2D(_CameraDepthTexture);
TEXTURE2D(_DepthPyramidTexture);
SAMPLER(sampler_CameraDepthTexture);
float4 frag(v2f i) : SV_Target
{
i.ray = i.ray * (_ProjectionParams.z / i.ray.z);
float2 uv = i.screenUV.xy / i.screenUV.w;
float2 screenPos = uint2(uv * _ScreenParams.xy);
// // 方式1 //
// float depth = LOAD_TEXTURE2D_LOD(_CameraDepthTexture, screenPos, 0).r;
// // 方式2 //
// float depth = LOAD_TEXTURE2D(_CameraDepthTexture, screenPos);
// 方式3 //
// float depth = LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, TexCoordStereoOffset(screenPos), 0).r;
// 方式4 //
float depth = LOAD_TEXTURE2D(_DepthPyramidTexture, screenPos).r;
depth = Linear01Depth(depth);
float4 vpos = float4(i.ray * depth,1);
float3 wpos = mul (unity_CameraToWorld, vpos).xyz;
float3 opos = mul (unity_WorldToObject, float4(wpos,1)).xyz;
clip (float3(0.5,0.5,0.5) - abs(opos.xyz));
float2 texUV = opos.xz + 0.5;
float4 col = tex2D(_MainTex, texUV);
return col;
}
ENDHLSL
}
}
Fallback Off
}