Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

时间:2024-04-04 14:10:53

目录

1 Alpha裁剪

1.1 Alpha贴图

1.2 纹理化

1.3 丢弃片段

1.4 裁剪阴影

1.5 双面渲染

1.6 给背面翻转法线

1.7 可选的裁剪

1.8 Alpha-Test渲染队列

2 半透明

2.1 混合模式

2.2 透明渲染队列

2.3 不写入深度

2.4 双面材质的半透明

2.5 制作一个双面mesh

2.6 Alpha-Clipped 阴影

2.7 接受阴影

3 Shader GUI

3.1 投射阴影

3.2 设置着色器属性

3.3 预设

本文重点:

1、执行alpha裁减

2、渲染mesh的两个面

3、支持不同的混合模式

4、生成双面mesh

5、根据预设创建ShaderGUI

这是涵盖Unity的可脚本化渲染管道的教程系列的第六部分。这是关于增加对alpha裁剪和半透明材质的支持。

本教程是CatLikeCoding系列的一部分,原文地址见文章底部。“原创”标识意为原创翻译而非原创教程。

本教程使用Unity 2018.3.0f2制作。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(混合使用不透明,裁减和淡化的材质,所有都带有投射和接收阴影)

1 Alpha裁剪

如渲染11透明度中所述,可以通过丢弃基于alpha贴图的片段来在几何图形中切孔。此技术称为Alpha裁剪,Alpha测试或 (剪切)cutout渲染。除此之外,它与渲染不透明几何体完全相同。因此,要支持Alpha裁剪,我们只需要调整着色器即可。

1.1 Alpha贴图

仅当材质的Alpha值在其整个表面变化时,Alpha裁剪才有用。最简单的方法是使用Alpha贴图。这有两种纹理,一种用于正方形几何体(例如方形和立方体),一种用于球体。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(立方体和球体的alpha贴图,空白的是原图,新标签页中查看图片)

导入这些纹理,并指示其Alpha通道代表透明度。它们的RGB通道是均匀的白色,因此不会影响材质的外观。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(Alpha是透明的)

1.2 纹理化

将一个主纹理属性添加到“ Lit”着色器。我们将使用它作为反照率和Alpha的源,默认为纯白色。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

使用适当的纹理,创建两种新材质,一种用于发光的alpha剪切球体,另一种用于发光的剪切正方形。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(lit clipped square材质)

在Lit include文件中,添加主纹理及其采样器状态的声明。这类似于阴影贴图,但使用TEXTURE2D和SAMPLER宏。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

我们需要UV纹理坐标进行采样,这是网格数据的一部分。因此,将它们添加到顶点输入和输出结构。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

要应用纹理的平铺和偏移,请在UnityPerMaterial缓冲区中添加所需的_MainTex_ST着色器变量。然后,当在LitPassVertex中传输UV坐标时,可以使用TRANSFORM_TEX宏。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

现在,我们可以使用SAMPLE_TEXTURE2D宏在LitPassFragment中对主贴图进行采样,以获取反照率和Alpha数据,将其与颜色数据相乘。从现在开始,我们还将返回alpha值。现在不需要,但是以后会使用。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

1.3 丢弃片段

当片段的alpha值低于某个临界值时,通过丢弃片段来进行alpha裁剪。截止值位于0到1之间,并且是可配置的,因此请为其添加一个着色器属性,默认值为?。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

将相应的变量添加到UnityPerMaterial缓冲区。然后使用片段的alpha值减去阈值调用clip函数。这将导致所有最终低于阈值的片段被丢弃,这意味着它们不会被渲染。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(alpha裁剪之后的球体和立方体)

现在,将使用具有Alpa材质的对象渲染带有孔的对象。孔的大小取决于裁剪值。但是,这仅适用于对象表面本身。它们投射的阴影仍然是实体的,因为我们尚未对其进行调整。

1.4 裁剪阴影

剪切阴影的工作方式与lit通道中的剪切完全相同,因此请相应地调整ShadowCaster包含文件。因为最终的alpha值取决于主贴图和材质颜色,所以我们现在还必须在ShadowCasterPassFragment中对实例化的颜色进行采样,因此也需要传递实例ID。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(剪切阴影)

1.5 双面渲染

因为仅渲染了几何图形的正面,所以我们的Alpha剪切对象缺少背面。当围绕它们旋转视图时,这会显而易见。而且,它们的阴影与我们看到的并不匹配,因为只有相对于光源的前侧会投射阴影。解决方案是渲染几何图形的两面,使我们能够看到对象表面的内部并使内部表面投射阴影。

渲染哪一侧由着色器的剔除模式控制。要么不进行剔除,要么对所有正面三角形进行剔除,要么对所有背面三角形进行剔除。我们可以添加一个浮点着色器属性,该属性表示一个枚举值,默认值为2,对应于通常的背面剔除。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

通过将枚举属性添加到该属性,我们可以通过枚举弹出窗口公开此属性。可以将所需的枚举类型作为参数提供,在本例中为UnityEngine.Rendering命名空间中的CullMode。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(禁用剔除)

虽然我们已将其定义为着色器属性,但着色器程序并未直接使用剔除模式。GPU使用它来决定将哪些三角形传递给片段程序以及将哪些三角形丢弃。我们通过着色器遍历中的Cull语句来控制它。如果我们使用固定的剔除模式,则可以满足诸如“Cull Off ”之类的要求,但是我们也可以通过编写Cull [_Cull]使其依赖于着色器属性。两次都执行此操作。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(双面渲染)

1.6 给背面翻转法线

现在,我们看到了几何图形的两面,但内部照明不正确。通过把我们的材质剔除正面比较容易看出来,让我们只看到内部。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(只渲染背面,不正确的光照)

事实证明,灯光是翻转的。亮的地方本应该是暗的,反之亦然。这是因为法向向量是用于外部而不是内部的。因此,渲染背面时,我们必须取反法线向量。

GPU可以告诉片段程序是否着色正面或背面的片段。我们可以通过向LitPassFragment添加一个附加参数来访问此信息。此参数的类型和语义取决于API,但是我们可以使用Core库中的FRONT_FACE_TYPE和FRONT_FACE_SEMANTIC宏。同样,我们可以使用IS_FRONT_VFACE宏根据要处理的是正面还是背面来选择两个选项。必要时可使用它来抵消法向矢量。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(背面VS全部 正确的光照)

现在,内部表面已正确着色,单由于自阴影的问题其最终仍比外部更暗。

为什么阴影有时会忽略裁剪(Clip)模式?

渲染阴影时始会终应用剪辑模式。但是,因为对象使用不同的材质,Unity也会主动批处理阴影投射器,并且在这么做时会忽略剪辑模式。这意味着当你混合使用除裁剪模式之外相同的阴影投射器时,整个裁剪将使用哪种裁剪模式是任意的。由于为每个着色器都设置了裁剪模式,所以不能逐示例修改。

你可以通过在渲染阴影时禁用实例化和批处理来避免此问题。但这不是一定成功的,因为具有不同裁剪模式的材质可能具有其他相关特性,这些特性也有所不同,从而阻止了批处理。稍微不同的cutoff值可以防止错误的批处理。

1.7 可选的裁剪

使用alpha裁剪时,GPU无法再假定整个三角形都已渲染,这使得某些优化无法实现。因此,最好仅在必要时启用alpha裁剪。因此,我们将创建两个着色器变体:一个带着色器裁剪,一个不带。可以使用shader关键字来做到这一点,就像管道控制是否使用阴影一样,但这次我们将通过material属性来控制它。

添加一个toggle属性,以控制对着色器的剪辑。它必须是浮点数,默认值为零。给它一个Toggle属性,它将使它显示为一个复选框。除此之外,可以为属性提供一个关键字,该关键字在更改属性时会启用或禁用。我们将使用_CLIPPING关键字。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(裁剪启用)

现在,我们可以添加另一个多编译语句,但是可以预期,此切换不会在运行过程中更改,而只会在编辑素材资源时更改。因此,我们不必总是为这两个选项生成着色器变体。可以通过使用#pragma shader_feature指令来实现。如果只使用一个切换关键字,我们只需列出该关键字即可,仅此而已。两次都执行此操作。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

使用shader feature有什么优势?

无论在编辑器中编译着色器还是将其放入构建中时,都必须始终包含所有多编译着色器变体。在Unity编辑器可以确定的范围内,shader feature替代项仅包括实际需要的变体。这可以大大减少着色器的编译时间和构建的大小。

使用多重编译方法的唯一原因是在运行过程中启用了哪些关键字的更改。例如shadow关键字,但是如果你在运行过程中配置材质,也可以使用true。

现在,我们可以确保仅在定义_CLIPPING关键字的情况下才剪切Lit。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

ShadowCaster也是如此。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

请注意,我们也可以通过消除UV坐标来进一步优化,但是优化的重要性不大,因此我将不予赘述。同样,你可以使用着色器特性(shader feature)仅在关闭裁剪时检查三角形的朝向,这是我没有介绍的另一种优化。

1.8 Alpha-Test渲染队列

除了可能丢弃的片段以外,alpha剪切渲染的工作方式与不透明渲染相同,并且可以将两者混合在一起而不会出现问题。但是由于alpha裁剪会阻止某些GPU优化,因此通常在渲染所有alpha裁剪的对象之前先渲染所有纯不透明的对象。这样可以实现最大程度的GPU优化,随着更多的最终隐藏在不透明的几何图形后面的透明片段,潜在地减少了alpha剪切片段的数量,并且还可以减少批处理数量。所有这些都可以通过简单地设置alpha裁剪的材质以使用更高的渲染队列来完成。默认的材质检查器会显示队列,因此我们可以手动更改它。Alpha裁剪材质的默认队列为2450,与下拉菜单中的AlphaTest选项相对应。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(使用alpha-test 队列)

2 半透明

如果片段没有被剪切,则它是完全不透明的。因此,可以使用alpha剪切在对象上切割孔,但它不能表示半透明的表面。在着色器支持半透明之前,我们还有更多工作要做。

2.1 混合模式

当某些东西是半透明的时,它至少会让部分的光通过。为了让着色器实现这一点,必须更改片段自身的颜色与之前渲染的颜色混合的方式。可以通过更改着色器的混合模式来实现。

混合模式的控制类似于剔除模式,但具有两个用于混合新旧颜色的权重选项。第一个称为源,即我们现在正在渲染的东西,第二个称为目标,即以前渲染的东西。例如,默认的混合模式是“Blend One Zero ”,这意味着新颜色完全替代旧颜色。

alpha通道也没有单独的选项吗?

有,但是很少使用。如果未明确指定Alpha的混合模式,则将所有四个通道以相同方式混合。

就像剔除一样,为源和目标混合添加两个着色器属性,但BlendMode枚举类型除外。将其默认值设置为1和0。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

仅向lit pass 添加混合声明。ShadowCaster 通道 仅关心深度,因此混合模式不会对其产生影响。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

半透明的最简单形式是根据其alpha值使片段淡化。这是通过使用源的Alpha作为源的权重,再减去源的Alpha作为目标的权重来完成的。我们可以从下拉菜单中选择这些选项。对新的淡化材质执行此操作,并关闭它们的剔除。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(混合模式设置为淡入淡出 不正确的结果)

也有很多其他混合模式。大多数很少使用,但有些用于不同类型的透明度。例如,pre-multiplied 混合使用1作为源而不是源的Alpha。这样就可以保持镜面反射(以表示玻璃之类的表面),但也需要对着色器进行一些更改,在此不做介绍。

2.2 透明渲染队列

仅当正在渲染的内容背后有东西时,淡入淡出(fading)才有效。我们的管道已经解决了这个问题,首先呈现不透明队列,然后呈现天空盒,最后呈现透明队列。我们的淡入淡出材质只需要使用正确的队列即可。默认的“Transparent ”选项很好。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(移动透明队列,仍然不正确)

2.3 不写入深度

现在,半透明有时可以正常工作,但也会产生奇怪的结果。这里要主要的是,因为我们仍在投射阴影,好像表面是不透明的一样。发生这种情况是因为我们没有剔除,所以表面的两面都被渲染。首先渲染哪个部分取决于网格的三角形顺序。当正面的三角形先渲染时,还没有可与之融合的背面。背面不会被渲染,因为它位于已经渲染的东西后面。

当两个单独的透明对象彼此靠近时,也会发生相同的问题。Unity对透明对象进行从前到后排序,这是正确的,但它只能考虑对象位置,而不能考虑形状。首先绘制的对象的一部分仍可以终止于后来绘制的对象的前面。例如,在场景中放置两个两个大部分重叠的四边形,一个在另一个四边形上,然后调整视图,直到最上面的一个首先被渲染。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(上面的四边形先被绘制)

我们无法避免这种情况,除非仔细控制半透明对象的位置或使用具有不同渲染队列的材质。如果对象与具有任意三角形顺序的双面材质相交,它将始终会出错。但是我们可以做的是禁用对透明材质的深度缓冲区的写入。这样,首先渲染的内容将永远不会阻塞随后渲染的内容。

添加另一个浮动着色器属性以控制Z编写,默认情况下该属性处于启用状态。我们可以再次使用切换,但这将始终产生一个关键字,而这种情况下我们不需要。因此,我们改为通过编写[Enum(Off,0,On,1)]使其具有关闭和打开状态的自定义枚举。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

仅将ZWrite控件添加到lit pass中,这再次与阴影无关。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(不写入深度缓冲)

现在,即使四边形的绘制顺序不正确,它们也会完全渲染。但是,底部四边形仍在顶部四边形之后绘制,因此它仍然不正确。方形的实心阴影让情况更加恶化。当绘制顺序翻转时,这也非常明显。这是设计场景时必须牢记的透明渲染的局限性。

2.4 双面材质的半透明

禁用Z写入功能后,关闭剔除时始终会渲染对象的内部。但是,绘制顺序仍然由网格的三角形顺序确定。使用默认的球体和立方体时,一定会产生不正确的结果。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(双面,不写入深度缓冲)

对于任意网格,确保首先绘制背面的唯一方法是复制对象并使用两种材质,一种材质是剔除正面,另一种材质是剔除背面。然后调整渲染队列,以便首先绘制内部。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(内部和外部分开的对象和材质)

这适用于单个对象,但是当多个这样的对象在视觉上重叠时无效。在这种情况下,所有外部都会被绘制到所有内部之上。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(立方体的外部在球体里面之上)

2.5 制作一个双面mesh

渲染双面半透明表面的最佳方法是使用为此目的专门创建的网格。网格的内部和外部必须包含单独的三角形,并按顺序排列,以便首先绘制内部。即使这样,这也只能可靠地用于不会在视觉上重叠的凹入对象。

你可以使用单独的3D建模器创建双面网格,但是我们也可以在Unity中制作一个简单的工具来快速生成任何源网格的双面变体。为此,创建一个静态DoubleSidedMeshMenuItem类,并将其资产文件放在Editor文件夹中。我们将使用它来将Assets Create Double-Sided Mesh项目添加到Unity的菜单中。这是通过将MenuItem属性添加到静态方法,并以所需的项目路径作为参数来完成的。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

这个想法是,用户首先选择一个网格,然后**菜单项,然后我们将创建其双面等效项。因此,第一步是获取对选定网格的引用,这是通过Selection.activeObject完成的。如果没有选定的网格,请指示用户选择一个网格并中止。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

as是干嘛的?

如果可能,它将强制转换为指定的类型。否则,结果为null。请注意,这仅适用于引用类型。

我们首先创建网格的内部。通过实例化克隆源网格,检索其三角形,通过System.Array.Reverse反转其顺序,然后将结果分配回去。翻转所有三角形的面。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

接下来,检索法线,取反法线,然后将其分配回去。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

然后创建一个新的网格并在其上调用CombineMeshes。它的第一个参数是CombineInstance结构的数组,只需要引用相关的网格即可。首先是内部网格,然后是源网格。这样可以保证先绘制内部三角形。之后是三个布尔参数。第一个必须为true,表示必须将网格合并为单个网格,而不是定义多个子网格。另外两个是我们不需要的矩阵和光照贴图数据。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

完成后,我们不再需要内部网格,因此请立即销毁它。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

最后,通过调用AssetDatabase.CreateAsset创建一个网格资产。它的第一个参数是组合网格,第二个参数是资产路径。我们将其简单地放在资产根文件夹中,并为其赋予与附加了双面的源网格相同的名称。路径和文件名可以通过System.IO.Path.Combine方法进行组合,因此无论你的操作系统使用哪个路径分隔符,它都可以工作。而且我们需要使用 asset 作为文件扩展名。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

现在我们可以选择任何网格并为其创建双面变体。你可以通过选择使用该网格物体的游戏对象并在网格物体渲染器组件中双击其引用来选择默认球体或立方体。生成的资产看起来像导入的网格物体,因为它们是自定义资产,但是可以正常工作。我们可以将这些网格物体用于透明对象,并将淡入淡出材质切换为背面剔除。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(使用双面网格)

2.6 Alpha-Clipped 阴影

到现在为止,我们都忽略了阴影,因此我们的半透明对象仍会投射阴影就好像自己是不透明一样。他们也会收到阴影,但这还好。

透明物体可以接收阴影吗?

是的。接收阴影所需要做的就是确定片段与光源之间是否存在阴影投射器,阴影贴图会告诉我们。片段是用于不透明表面还是透明表面都无关紧要。话虽如此,Unity不支持与级联阴影贴图结合使用的透明表面阴影接收。这是因为Unity在单独的全屏通道中采样了级联的阴影贴图,该通道依赖于深度缓冲区,因此无法与透明度结合使用。当采样每个片段的所有阴影时,我们没有该限制。

阴影贴图不能表示部分阴影。我们能做的最好的就是使用alpha裁剪的阴影。当前,可以对透明材质启用Alpha裁剪,但这也会影响表面本身。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(淡入淡出加上裁剪)

只能对阴影执行alpha裁剪。我们可以通过使用三个选项替换裁剪开关来支持这一点:关闭,打开和阴影。首先,关闭当前使用它的所有材料的裁剪,以便清除_CLIPPING关键字。然后,使用三个选项作为参数,用KeywordEnum替换切换。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

现在你可以重新打开裁剪了。我们这样做是因为KeywordEnum使用不同的关键字。现在使用的关键字是通过使用着色器属性名称,下划线,然后每个选项分别大写而形成的。因此,在光照阶段,我们必须更改着色器功能以改为依靠_CLIPPING_ON。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

还要调整关键字检查。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

ShadowCaster通道现在启用或设置为阴影时,现在必须使用剪辑。换句话说,它关闭时不应该剪切。我们将为着色器功能使用后一个条件,因此我们仅依靠_CLIPPING_OFF。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

因此,我们现在必须检查是否未定义_CLIPPING_OFF。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

这使得透明材质可以投射alpha剪切的阴影。这不是一个完美的匹配,但它易于支持并且在某些情况下可能已经足够。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(带有阴影裁减的淡入淡出,cutoff为0.75)

如果不需要,可以关闭每个对象的阴影投射。我们还将应用在以后的每种材质中。

有没有办法创建半透明阴影?

Unity的旧版管道具有渲染半透明阴影的选项,如渲染12,半透明阴影中所述。它通过抖动阴影,基于alpha和屏幕空间抖动模式裁剪阴影,依靠过滤来模糊结果来伪造半透明。在某些有限的情况下,它可以产生令人信服的阴影,但总的来说,结果比较糟糕糟糕,以至于无法实际使用。

2.7 接受阴影

透明的表面可以很好地接收阴影,但这可能是不希望的。因此,通过添加链接到_RECEIVE_SHADOWS关键字的着色器属性开关(默认情况下处于启用状态),使它成为可选的。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

将其着色器功能添加到lit pass。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

如果未定义_RECEIVE_SHADOWS关键字,只需在ShadowAttenuation和CascadedShadowAttenuation中返回1。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

进行这些更改后,即使所有材质都启用了阴影,所有阴影也会消失。发生这种情况是因为向着色器添加新属性不会自动启用相关关键字。选择所有材质并切换选项将同步属性及其关键字。相反,当创建新材质时,将立即正确设置其链接到属性的所有关键字。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(投射,但是不接受阴影)

3 Shader GUI

虽然现在可以使用我们的着色器创建不透明和透明材质,但我们必须手动选择正确的混合模式,以及相似的情况处理。Unity的着色器检查器隐藏了这些详细信息,而是显示受支持的表面类型的下拉菜单。通过为材质创建自定义着色器GUI,我们可以做类似的事情。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

ShaderGUI定义了一个OnGUI方法,该方法将被调用以创建材质的检查器。它具有MaterialEditor参数,该参数是跟踪正在编辑的材质的基础对象。它还具有一个MaterialProperty数组参数,该参数包含对所选材质的所有着色器属性的引用。必须重写此方法才能创建自己的GUI,但是不会替换默认的GUI,只需添加它即可。因此,我们的方法将调用ShaderGUI的 base OnGUI实现。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

为什么会有单独的材料编辑器?

最初,为材质创建自定义检查器的方法是扩展MaterialEditor。ShaderGUI方法后来出现,并且更加直接和专注于材质。但是,原始的编辑器功能仍用于生成检查器。它只是调用着色器GUI的适当方法。通过参数提供编辑器,因为直接操纵选定的材质需要它。

要使用我们的自定义GUI,我们必须在我们的Lit着色器中添加一个CustomEditor语句,然后是一个包含类名的字符串。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

为了完成我们的自定义工作,我们需要使用编辑器,属性和选定的材质,因此让我们跟踪那些带有字段的材质。因为我们将支持多材质编辑,所以我们必须处理一系列选定的材质。可以通过编辑器的targets属性检索它们。但是,由于编辑器是通用的,所以我们得到一个Object数组,而不是Material数组。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

3.1 投射阴影

让我们开始禁用每种材质的阴影投射。这是通过对所有选定的材质禁用ShadowCaster传递来完成的。可以通过遍历材质数组并在每个数组上调用SetShaderPassEnabled来实现此目的,附带通道名称以及是否应启用它的参数。将该代码放入带有通道和启用状态作为参数的SetPassEnabled方法中。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

foreach如何工作?

这是for循环的方便替代方法。在这种情况下,它与以下代码相同:

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

通常,foreach依靠迭代器对象来完成其工作,因此分配临时内存。但是,在显式遍历数组时不会执行此操作。在那种情况下,根据上面的示例,代码有效地转换为简单的for循环。

除此之外,我们需要确定是否启用阴影投射。我们可以通过在材质上调用GetShaderPassEnabled进行检查。为此,创建另一个方法,检查选择的第一个材质。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

但是,如果选择了多种材质,我们可能会得出不同的结果。我们不能用一个布尔值来表示。因此,让我们返回一个可为空的布尔值。然后,我们可以遍历所有其他材质,如果发现不一致,则将返回null。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

什么是可空(nullable )类型?

可以通过在类型名称后面写一个问号来创建结构类型的可为空的变体。该结构将结构包装在另一个结构中,该结构具有一个额外的布尔值以指示它是否被认为具有值。因此,它实际上不能设置为null,但是有一种语法糖可以让我们为它分配null,这将其标记为没有值。

在我们的例子中,我们最终得到一个具有两个布尔值的结构,类似于以下代码,以及自定义赋值运算符:

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

现在,我们可以创建一个方法,该方法负责显示用于投射阴影的切换选项。首先,检查是否启用了ShadowCaster传递。如果没有得到值,则将EditorGUI.showMixedValue设置为true,以指示输入控件应绘制其自身的混合值表示形式。我们还必须将启用状态设置为某种状态,可以是任何一种,因此只需使用false。在该方法的最后,我们应该禁用混合值表示。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

可以通过调用EditorGUILayout.Toggle显示带有Cast Shadows标签和启用值的复选框。将其结果分配回启用状态。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

那只会改变我们的变量。要调整材质,我们必须调用SetPassEnabled。但是我们只能在用户更改状态时执行此操作。我们可以通过在切换之前调用EditorGUI.BeginChangeCheck并在切换之后调用EditorGUI.EndChangeCheck来确保这一点。后面的调用返回是否在两者之间进行了更改。如果是这样,请调用SetPassEnabled。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

因为我们直接更改材质资产,所以不会自动生成撤消步骤。在进行更改之前,需要自己通过在编辑器上使用label参数调用RegisterPropertyChangeUndo来做到这一点。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

最后,调用该方法以在OnGUI的末尾显示切换。这会使切换显示在材质检查器的底部。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

(没有投射阴影 逐材质控制)

3.2 设置着色器属性

从不透明的表面更改为修剪或褪色的表面需要更改多个着色器属性。我们将通过为需要的属性添加setter属性来使此操作变得方便。cull,blend和z-write属性非常简单。对属性使用适当的类型,以其名称和属性数组作为参数调用FindProperty,然后将其分配给其floatValue属性。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

剪辑模式和阴影接收属性需要更多工作,因为它们需要与着色器关键字同步。可以通过在材质上调用EnableKeyword为材质启用关键字。禁用它需要调用DisableKeyword。我们必须对所有选定的材质执行此操作。为此创建一个方便的方法,以关键字名称和启用状态作为参数。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

现在,我们可以添加用于裁剪和阴影接收的setter属性,并为所有相关关键字调用SetKeywordEnabled。还为我们的自定义剪辑模式创建一个枚举。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

我们需要调整的最后一件事是渲染队列。可以通过设置材质的renderQueue属性来完成。为此,使用RenderQueue类型添加一个setter属性,设置所有材质的队列。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

3.3 预设

Unity的着色器仅允许你选择一些预定义的表面类型,隐藏混合模式,剔除,队列等的所有详细信息。我们不会这样做。相反,我们将添加一些按钮,允许用户应用预设。

第一个预设是不透明的,对应于默认的材质设置。为此添加一个方法,该方法首先以Opaque作为其标签参数调用GUILayout.Button。如果单击该按钮,则该方法返回true。如果没有,则没有单击它,我们可以中止该方法。否则,我们将继续注册并撤消步骤。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

不透明的设置是:不裁剪;背面剔除,零混合,写入深度,接收阴影,投射阴影并使用几何队列。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

为片段预设添加另一种方法。唯一的区别是剪辑打开并且使用alpha测试队列。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

但是裁剪可以与双面渲染结合使用,因此可以添加另一个预设,并剔除掉。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

为传统的淡入淡出材质设置另一个预设。与不透明的预设相比,它使用source-alpha和1减source-alpha混合,不写入深度,既不接收也不投射阴影,并使用透明队列。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

但是我们的淡入淡出材质可以投射和接收阴影,因此添加另一个启用投射和接收并使用阴影剪辑模式的预设。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

在OnGUI的末尾调用所有预设方法。首先调用EditorGUILayout.Space,在它们和其余的GUI之间添加一些空间。另外,应该很少使用预设按钮,因此请使用标有“Preset”的折叠将其隐藏。可以通过调用布尔值的EditorEditorGUILayout.Foldout来创建该折页,该布尔值指示它是否已打开,其后是其标签,以及是否单击该标签(而不只是其图标)会切换其状态。跟踪字段中的折叠状态,以便可以更改它。如果打开,则显示预设。

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

下一个章节是反射。

往期推荐:

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(五)方向阴影(级联贴图 Cascaded Maps)


Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(四)聚光灯阴影(阴影贴图)


Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(三)光照(单通道 正向渲染)


Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)

Unity可编程渲染管线系列(二)自定义着色器(HLSL和核心库)

欢迎扫描二维码,查看更多精彩内容。点击 阅读原文 可以跳转原教程。

本文翻译自 Jasper Flick的系列教程

原文地址:

https://catlikecoding.com/unity/tutorials

Unity可编程渲染管线系列(六)透明度(裁剪与淡化 Clipping and Fading)