8、2D建筑与3D模型的遮挡问题 URP Shader

在我们的实际项目中根据实际情况,我们的建筑采用了2D面片的方式,但因为我们的英雄是3D模型,同时策划有需求,英雄可以在主城里面走动。这个时候就给我们提出了一个比较麻烦的问题,我们知道正常情况下渲染是谁的渲染队列高谁渲染在上面,我们这里英雄的Render Queue是2900,建筑面片的Render Queue是2800.渲染结果就会是下图的样子。
请添加图片描述

我们希望的是2D的建筑能正确的遮挡3D模型。
请添加图片描述

模型在建筑前面时又可以正常显示
请添加图片描述

也就是在部分区域可以遮挡模型,其它区域不遮挡模型。
但是这样的需求,单Pass是没有办法实现的。
我们只能考虑多Pass Shader来实现。
也就是一个Pass正常渲染,一个pass去实现固定区域去遮挡所有物体。
因为Shader Graph不支持多Pass shader的编写。我们这次就不再提供Shader Graph的连接图。
先直接上Shader ,后面我们再讲解一下整个的工作原理。
下面是建筑的shader,模型的shader我们使用前面的示例shader即可。

Shader "Unlit/DayNightBuildingDayNight"
{Properties{[NoScaleOffset]_MainTex("MainTexture", 2D) = "white" {}_Color("NightColor", Color) = (0, 0, 0, 0)[NoScaleOffset]_Control("Control", 2D) = "white" {}_RCut("RCut",Float)=0.01_GColor("GColor", Color) = (0, 0, 0, 0)_BColor("BColor", Color) = (0, 0, 0, 0)_Speed("Speed", Float) = 10}HLSLINCLUDE#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"struct appdata_t {float4 vertex : POSITION;float2 texcoord : TEXCOORD0;};struct v2f {float4 vertex : SV_POSITION; float2 texcoord : TEXCOORD0; };sampler2D _MainTex,_Control;  CBUFFER_START(UnityPerMaterial)half _Speed,_RCut;half4 _Color,_GColor,_BColor;float4 _MainTex_ST,_Control_ST;CBUFFER_ENDvoid Unity_Remap_half(half In, half2 InMinMax, half2 OutMinMax, out float Out){Out = OutMinMax.x + (In - InMinMax.x) * (OutMinMax.y - OutMinMax.x) / (InMinMax.y - InMinMax.x);}v2f vert (appdata_t v){v2f o;o.vertex = TransformObjectToHClip(v.vertex.xyz); o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}half4 frag (v2f i) : SV_Target{half4 finalCol = tex2D(_MainTex,i.texcoord);finalCol=finalCol*_Color;half3 controlrgb=tex2D(_Control,i.texcoord).rgb;finalCol+=_GColor*controlrgb.g;half remap;Unity_Remap_half(sin(_Time.y*_Speed),half2(-1,1),half2(0.5,1),remap);finalCol+=_BColor*controlrgb.b*remap;finalCol.a=step(_RCut,controlrgb.r);return finalCol;}half4 frag2(v2f i) : SV_Target{half4 col = tex2D(_MainTex, i.texcoord);half4 mask = tex2D(_Control, i.texcoord);half alpha=mask.r;if(alpha>0.2) alpha=0;elsealpha = min(alpha, col.a);clip(alpha -0.06);return half4(1.0f, 1.0f, 1.0f, 1.0f);}ENDHLSLSubShader{Tags {"RenderPipeline"="UniversalRenderPipeline""CanUseSpriteAtlas" = "true" "IgnoreProjector" = "true" "PreviewType" = "Plane" "Queue" = "Transparent" "RenderType" = "Transparent" }LOD 100Pass{Tags {  }ColorMask 0ZWrite OnHLSLPROGRAM#pragma vertex vert#pragma fragment frag2#pragma multi_compile_instancing#pragma target 3.0ENDHLSL}Pass{Tags{ "LightMode" = "UniversalForward" }ColorMask RGBAZWrite OffBlend SrcAlpha OneMinusSrcAlphaHLSLPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_instancing#pragma target 3.0ENDHLSL}}
}

多Pass支持

正常情况下,URP的shader第二个pass开始是都不生效的。
这里我们在第二个加入了Tags{ “LightMode” = “UniversalForward” }
这样就能正常使用多Pass了。
“LightweightForward”、“SRPDefaultUnlit” 这两个值也是可以的。具体原理大家可以自行查找相关资料。

这里出现了一些新的指令:ZWrite

设置在渲染过程中是否更新深度缓冲区内容。通常,ZWrite 对不透明对象启用,对半透明对象禁用。
禁用 ZWrite 会导致不正确的深度排序。这种情况下,您需要在 CPU 上对几何体进行排序。
ZWrite的默认值是On。
和ZWrite一起使用的指令是ZTest,ZTest的默认值是:LEqual(绘制位于现有几何体前面或相同距离的几何体。不绘制位于现有几何体后面的几何体。)。我们这这里用默认值即可,所以我们在Shader中没有单独书写。

ShaderLab 命令:ZTest

设置几何体是否通过深度测试的条件。
深度测试可使具有 “Early-Z” 功能的 GPU 在管线早期拒绝几何体,并确保几何体的正确排序。通过改变深度测试的条件,您可以实现物体遮挡等视觉效果。

参数功能
operationLess绘制位于现有几何体前面的几何体。不绘制位于现有几何体相同距离或后面的几何体。
LEqual绘制位于现有几何体前面或相同距离的几何体。不绘制位于现有几何体后面的几何体。这是默认值。
Equal绘制位于现有几何体相同距离的几何体。不绘制位于现有几何体前面的或后面的几何体。
GEqual绘制位于现有几何体后面或相同距离的几何体。不绘制位于现有几何体前面的几何体。
Greater绘制位于现有几何体后面的几何体。不绘制位于现有几何体相同距离或前面的几何体。
NotEqual绘制不位于现有几何体相同距离的几何体。不绘制位于现有几何体相同距离的几何体。
Always不进行深度测试。绘制所有几何体,无论距离如何。

ZTest分为通过和不通过两种情况,ZWrite分为开启和关闭两种情况的话,一共就是四种情况:

1.深度测试通过,深度写入开启:写入深度缓冲区,写入颜色缓冲区;
2.深度测试通过,深度写入关闭:不写深度缓冲区,写入颜色缓冲区;
3.深度测试失败,深度写入开启:不写深度缓冲区,不写颜色缓冲区;
4.深度测试失败,深度写入关闭:不写深度缓冲区,不写颜色缓冲区;

Unity的渲染顺序

先渲染不透明物体,顺序是从前到后;再渲染透明物体,顺序是从后到前。

shader渲染分析

上面第一个pass没有Blend,所以属于不透明物体渲染,是先渲染的。
而3D模型是属于半透明物体,同时3D模型离相机的距离是比建筑远的。那么在进行深度测试时,是测试失败的。
那么我们看上面的情况分析,模型的像素点就不会被渲染。会被第一个pass的渲染区域遮挡。
如果模型在建筑前面时,深度测试会成功,那么模型就会被正常渲染。并且模型能正确遮挡建筑。

而第二个pass的作用就是正常渲染建筑,和前面的相比是没有任何变化的。

第一个Pass分析

我们来看一下这个pass的渲染区域。
因为是要实现建筑的部分区域遮挡,我们就需要一个控制贴图来定义区域,但我们之前的shader RGB三个通道都已经被使用,如果我们还需要只能新建一张贴图,但这样又会增加渲染量。这里我们使用了一个小的技巧,我把控制的题图的R通道,需要遮挡的部分使用了不同的不同的颜色值。这样我们就可以用一张贴图实现了。
颜色比较深一些的区域,就是我定义的遮挡区域。
请添加图片描述

half4 frag2(v2f i) : SV_Target{half4 col = tex2D(_MainTex, i.texcoord);half4 mask = tex2D(_Control, i.texcoord);half alpha=mask.r;if(alpha>0.2) alpha=0;elsealpha = min(alpha, col.a);clip(alpha -0.06);return half4(1.0f, 1.0f, 1.0f, 1.0f);}

也就是说r的值大于0.06小于0.2的地方就是我定义的可以进行建筑遮挡的区域,也就是说这个区域就是这个pass绘制的区域。
这里我们是通过clip这个指令实现的。

clip

clip函数会将参数小于0的像素点直接丢弃掉。
这样我们就定义了pass的绘制区域就是我制定的区域。

我们这个pass的目的是该区域不渲染任何颜色。所以这里我们引入了ColorMask。

ShaderLab 命令:ColorMask

设置颜色通道写入遮罩,以防止 GPU 写入渲染目标中的通道。

默认情况下,GPU 写入所有通道 (RGBA)。对于某些效果,您可能希望不修改某些通道;例如,您可以禁用颜色渲染来渲染无色阴影。另一个常见的用例是完全禁用颜色写入,以便您在一个缓冲区中填充数据而无需写入其他缓冲区;例如,您可能需要在不写入渲染目标的情况下填充模板缓冲区。

ColorMask 0

我们这里使用的是0,也就是不渲染任何通道。
我们有需要也可以使用R、G、B任意组合来指定渲染通道。

问题

因为采用了双通道,不能再使用SPR Batcher。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部