Unity3D RPG Core | 09 Shader Graph 遮挡剔除

目前画面会有个问题,人物跑到树后面既无法点击,也看不到人物。树木无法点击的处理,视频里介绍的比较简单,放在最后说明;在树后面也要看到人物,我们通过自定义着色器来实现。

1. 创建 Shader Graph 和材质

我们在 Assets 中选择 Create - Shader Graph - URP - Unlit Shader Graph,创建一个 Shader Graph。

创建的条目和视频中的版本不一样,Unity2021 在 Create - Shader Graph - URP - Unlit Shader Graph。

接着在刚才创建的 Shader Graph 上右击选择 Create - Material,创建一个对应的材质。

2. 编辑 Shader Graph

双击创建的 Shader Graph 文件,我们可以进入如图 1 所示的编辑界面。画面中有顶点着色器和片段着色器,这些概念我们在学习 OpenGL 的时候已经了解了。如果我们想让片段着色器的输入添加 Alpha 通道,可以勾选 Alpha Clipping 选项。

图1 Shader Graph 界面

人物在树的后面,目前我们想要的实现效果是大致显示出人物的轮廓即可。我们进行的设置如图 2 所示,首先我们创建 Fresnel Effect 节点,在预览效果上我们可以看到 Fresnel 的效果是边缘亮,中间暗。

接着我们想要人物有个明显的蓝边,我们创建 Multiply 节点,将 Fresnel 的输出结果和蓝色混合。

同时我们还想要人物有一个透明噪点的效果,可以创建 Dither 节点。并将 Dither 的效果作为片段着色器 Alpha 通道的输入。

图2 设置效果

为了方便有些效果可以在材质中调节,比如人物边缘的颜色,透明的程度以及 Alpha 的阈值。图 2 中可以看到,我们设置了对应的变量进行控制。变量的创建如图 3 所示,按加号可以创建对应类型的变量,然后将变量拖拽到 Shader Graph 界面上进行连接。

图3 变量

各个阶段的效果都可以通过图上的预览画面进行看到,最后的渲染效果可以在 Main Preview 界面上看到。

这节内容让我想起了之前 OpenGL 的学习:大部分的内容其实都是固定的,像是顶点和颜色的输入。变化的内容就是效果的实现,对应图上 Fresnel 等节点。

这种图和拖拽的手法很好,抽象和隐去了重复和不重要的部分,只需要关注效果部分。

3. 指定渲染材质

编辑好 Shader Graph 之后,我们在其界面上点击 Save Asset 进行保存。回到我们创建的对应材质上,可以看到如图 4 所示,材质属性上已经有了在 Shader Graph 中设置的变量,我们可以对它们进行微调,以满足预期。

图4 对应的材质

在指定渲染材质之前,我们需要修改一下人物的 Layer。Unity 中可以按层进行渲染,因为我们想要实现的人物遮挡效果是只针对人物的,所以需要为人物单独指定一个 Layer。

Layer 的创建如图 5 所示,我们点击 Add Layer,命名一个新层(这边命名为 Character)之后在进行指定。指定后会提示新层是不是要应用于子物体,我们选择是(Yes,change children)。

图5 新建层

接着我们选择之前创建的 URP 资产,这边项目是默认名字 New Universal Render Pipeline Asset_Renderer.asset。这个文件是在项目初期就创建的,时间有点久远,可以通过 《Unity3D RPG Core | 01 创建项目导入素材》 温习一下。

在 URP 渲染资产中,我们点击 Add Renderer Feature - Render Objects,首先添加一个渲染对象。如图 6 所示,我们将 Layer Mask 指定到之前创建的人物特有的层;Material 指定为前面已经实现了的遮挡效果新材质。默认进行深度测试的时候,后面遮挡的物体是不绘制的(就是之前遮挡看不见的效果),新材质就是在这种情况下生效。所以将 Depth 选项下的 Depth Test 设置为 Greater Equal,同时关闭掉 Write Depth。

只设置上述一个渲染对象时,在人物没有被遮挡的时候,人物模型本身背面被遮挡的地方也会呈现出指定材质效果,这不符合预期。我们再新建一个渲染对象,只修改 Layer Mask,其他参数不变。这时候可以看到人物没有被遮挡的情况下,渲染恢复了正常。为什么这么设置还不清楚,应该和渲染对象的选择逻辑有关。

图6 新建渲染对象

设置两个渲染对象的逻辑不清楚,留作问题。

图9 遮挡效果

4. 树木的点击

最后说明余下的另一问题:树木挡住了视线内人物的移动路径。视频中给了两种解决方案。

一种方案如图 7 所示,将所有树木的层设置为 Ignore Raycast。既然树木屏蔽掉了射线,自然射线就能打到树后的物体。

图7 屏蔽射线

另一种方案如图 8 所示,禁用所有树木的 Mesh Collider 组件。之前从射线检测碰撞的代码里了解过,射线检测到碰撞是会有对应的碰撞体的。现在树木没有碰撞体,自然射线也不会检测到碰撞。

图8 取消碰撞组件