Unity 渲染教程(十一):透明度

本文介绍了Unity中如何实现透明材质,包括剪切渲染、透明度贴图、Alpha Cutoff截止阈值以及渲染模式的切换。通过控制片段的透明度值,使用clip函数进行剪切,实现物体表面的开孔效果。同时,讨论了不同渲染模式下的混合模式、渲染队列、渲染类型标签和深度缓冲区的处理,确保半透明和淡入渲染的正确性。文章还探讨了透明度与反射率的关系,以及如何调整透明度以符合能量守恒原则。
摘要由CSDN通过智能技术生成

剪切渲染

要创建一个透明的材质,我们必须知道每个片段的透明度。透明度信息最常存储在颜色的透明度通道中。在我们的例子中,我们使用的是主反射率纹理的透明度通道和颜色色调的透明度通道。

这里有一个透明度贴图的例子。 它是一个白色纹理,在透明度通道带有平滑衰退噪声。它是白色的,所以我们可以完全专注于透明度,而不会被反射率的模式所分心。

黑色背景下的透明度贴图。

将这个纹理分配给我们的材质只是使材质变成白色。透明度通道被忽略,除非你选择使用透明度通道作为平滑源。但是当你用这种材质选择一个四边形的时候,你会看到一个大致是圆形的选择轮廓。

在实四边形上的选择轮廓。

我如何能使用选择轮廓?

Unity 5.5引入了一种新的选择高亮方法。以前,你总是看到所选网格的线框。 现在,你还可以通过场景视图的Gizmos菜单选择使用轮廓效果。

Unity使用替换着色器来创建轮廓,稍后我们将提到这个着色器。这个着色器采样主纹理的透明度通道。轮廓会在那些透明度值变为零的地方进行绘制。

确定透明度值

要获取透明度的值,我们可以通过添加一个GetAlpha函数到My Lighting导入文件。像反射率一样,我们通过乘以色调和主纹理的透明度值来找到这个值。

floatGetAlpha (Interpolators i) {

return_Tint.a * tex2D(_MainTex, i.uv.xy).a;

}

然而,当我们不使用纹理的透明度通道来确定平滑度的时候,我们应该只使用纹理。如果我们没有检查这一点的话,那么我们可能会曲解数据。

floatGetAlpha (Interpolators i) {

floatalpha = _Tint.a;

#if !defined(_SMOOTHNESS_ALBEDO)

alpha *= tex2D(_MainTex, i.uv.xy).a;

#endif

returnalpha;

}

剪孔

在不透明的材质情况下,每个通过深度测试的片段都会被渲染。所有片段是完全不透明的,并将结果写入深度缓冲区中。透明度的加入使这一点变得复杂。

实现透明度的最简单的方法是保持二进制。一个片段要么是完全不透明的,要么是完全透明的。如果它是透明的话,那么它根本不会被渲染。这使得可以在表面中剪出一个孔来。

要中止渲染片段,我们可以使用clip函数。 如果这个函数的参数为负,则片段将被丢弃。图形处理器不会混合这个片段的颜色,它的结果不会写入深度缓冲区中去。如果发生这种情况,我们不需要担心材质的所有其他属性。所以最有效的是尽早进行裁剪。在我们的例子中,这个事情放在MyFragmentProgram函数开始的地方。

我们将使用透明度值来确定我们是否应该裁剪。由于透明度值位于零和一之间,我们必须将其减去一些,使其为负。通过减去1/2,我们将使透明度值的范围的下半部分变为负。这意味着至少有1/2透明度值对应的片段将被渲染,而所有其他的片段将被裁剪。

float4 MyFragmentProgram (Interpolators i) : SV_TARGET {

floatalpha = GetAlpha(i);

clip(alpha - 0.5);

}

剪切一切透明度值低于0.5的片段。

可变的截止阈值

从透明度值中减去1/2是我们随便做的一个选择。我们可以减去另一个数字。如果我们从透明度值中减去一个更高的值,那么更大的范围将被剪切。因此,这个值用作截止阈值。让我们先把这个值变成一个变量。首先,在我们的着色器中添加Alpha Cutoff属性。

Properties {

_AlphaCutoff ("Alpha Cutoff", Range(0, 1)) = 0.5

}

然后将相应的变量添加到My Lighting之中,并在剪裁之前从透明度值中减去这个变量的值,而不是减去½。

float_AlphaCutoff;

float4 MyFragmentProgram (Interpolators i) : SV_TARGET {

floatalpha = GetAlpha(i);

clip(alpha - _AlphaCutoff);

}

最后,我们还必须向我们的自定义着色器的UI里面添加截止阈值。标准着色器显示了反射率线下面的截止阈值,所以我们也这样做。 我们将显示一个缩进的滑块,就像我们对平滑度的滑块所做的事情一样。

voidDoMain () {

GUILayout.Label("Main Maps", EditorStyles.boldLabel);

MaterialProperty mainTex = FindProperty("_MainTex");

editor.TexturePropertySingleLine(

MakeLabel(mainTex,"Albedo (RGB)"), mainTex, FindProperty("_Tint")

);

DoAlphaCutoff();

}

voidDoAlphaCutoff () {

MaterialProperty slider = FindProperty("_AlphaCutoff");

EditorGUI.indentLevel += 2;

editor.ShaderProperty(slider, MakeLabel(slider));

EditorGUI.indentLevel -= 2;

}

Alphacutoff属性的滑块。

现在,你可以根据需要来调整截止阈值。 你还可以对其进行动画处理,例如创建添加材质或去除材质的效果。

Alpha cutoff属性变化的效果请见:https://zippy.gfycat.com/SelfishCloudyAyeaye.mp4

着色器编译器将剪辑转换为丢弃指令。这里是相关的OpenGL核心代码片段。

u_xlat10_0 = texture(_MainTex, vs_TEXCOORD0.xy);

u_xlat1.xyz = u_xlat10_0.xyz * _Tint.xyz;

u_xlat30 = _Tint.w * u_xlat10_0.w + (-_AlphaCutoff);

u_xlatb30 = u_xlat30<0.0;

if((int(u_xlatb30) *int(0xffffffffu))!=0){discard;}

这里是相关的Direct3D11核心代码片段版本。

0: sample r0.xyzw, v1.xyxx, t0.xyzw, s1

1: mul r1.xyz, r0.xyzx, cb0[4].xyzx

2: mad r0.w, cb0[4].w, r0.w, -cb0[9].x

3: lt r0.w, r0.w, l(0.000000)

4: discard_nz r0.w

阴影怎么办?

我们将在下一个教程中处理剪切和半透明材质的阴影。在此之前,你可以使用对使用这些材质的对象关闭阴影。

渲染模式

剪裁不是免费的。它在桌面电脑的图形处理器上不那么糟糕,但使用分片渲染的移动图形处理器不喜欢丢弃片段。所以如果我们真的渲染一个剪切材质的话,我们应该只包括clip语句。而完全不透明的材料不需要这一点。为此,让我们依赖一个新的关键字_RENDERING_CUTOUT。

floatalpha = GetAlpha(i);

#if defined(_RENDERING_CUTOUT)

clip(alpha - _AlphaCutoff);

#endif

为这个关键字添加一个着色器特性,包括基本渲染通道和附加渲染通道。

#pragma shader_feature _RENDERING_CUTOUT

#pragma shader_feature _METALLIC_MAP

在我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农老K

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值