UnityShader之(半)透明效果

片元的信息

对于渲染片元,其所携带的信息包括颜色值(用于与颜色缓冲区中的颜色进行合并----覆盖和混合),深度值(用于与深度缓冲区中的深度值进行比较,通过测试后,在开启深度写入的情况下,更新深度缓冲区中的值),透明度(用于透明度测试或者用作混合因子进行颜色混合(透明度混合))!

不透明物体间无需考虑渲染顺序!----深度缓冲

当场景中包含很多模型时,对于不透明 (opaque) 物体,由于深度缓冲depth buffer (z-buffer) 的存在,不必考虑它们的渲染顺序也能得到正确的渲染效果!即距离摄像机近的不透明物体总会把远的不透明物体遮挡
例如A挡住B, 即便我们先渲B再渲染A也不用担心B会遮盖掉 A, 因为在进行深度测试时会发现A的深度值大于B,判断出B距离摄像机更远,B的颜色值也就不会写入到颜色缓冲区中 。

在实时渲染中 ,深度缓冲解决片元的可见性问题的基本思想是:
渲染某个不透明物体的片元时,在开启了深度测试的前提下,需要把它的深度值和已经存储在深度缓冲中的深度值进行比较,如果渲染片元的深度值大于深度缓冲中已存储的深度值,则将渲染片元的颜色值覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲区中(如果开启了深度写入)!反之如果渲染片元的深度值小于深度缓冲中已存储的深度值,就意味着这个片元没有通过测试,是不可见的,不应该被渲染到屏幕上(即有东西挡住了它,可能是遮挡的物体,也有可能是其他)。
在这里插入图片描述

(半)透明效果的实现

但如果想要实现透明效果,事情就不那么简单了。
在Unity 中,我们通常使用两种方法来实现透明效果:

  • 使用透明度测试(Alpha Test):这种方法其实无法得到真正的半透明效果,要么完全透明,要么完全不透明
    即在片元进行深度测试,深度写入之前就进行透明度测试!它采用一种“霸道极端”的机制,只要某个片元的透明度不满足条件(通常是小于某个阙值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响。
  • 使用透明度混合 (Alpha Blending):这种方法可以得到真正的半透明效果;
    用透明度混合时,需要关闭了深度写入 (ZWrite),但并没有关闭深度测试!此时就需要格外注意渲染顺序----距摄像机由远到近依次将半透明物体进行渲染
    渲染一个半透明物体的片元时,先进行深度测试,比较渲染片元的深度值与当前深度缓冲中的深度值,未通过测试片元则直接被丢弃,不做任何处理;通过测试的片元认为是可见的,才有资格进行透明度混合,更新颜色缓冲区中的颜色,但不会进行深度写入。这就决定了当一个不透明物体出现在一个透明物体的前面,而我们先渲染了不透明物体,它仍然可以正常地遮挡住透明物体。也就是说,对于透明度混合来说,深度缓冲是只读的。
    什么是透明度混合?使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。

对于透明度混合实现半透明效果为什么需要关闭深度写入功能?

如果不关闭深度写入,即在深度测试后进行深度写入和透明度混合, 一个半透明物体表面的后表面本来是可以透过它的前表面被我们看到见,在先渲染前表面时,由于深度测试时判断该半透明物体的后表面距离摄像机更远, 导致后面的表面将会被剔除, 我们也就无法透过半透明表面看到后面的物体了。 即使在满足半透明物体间正确的渲染顺序的情况下,先渲染半透明物体的后表面,再渲染前表面,得到的效果也并非正确的效果!

渲染引擎整体的渲染顺序:

渲染引擎一般都会先对物体进行排序,再渲染。常用的方法是。
(1) 先渲染所有不透明物体而无需考虑渲染顺序。渲染不透明物体时无需也不能关闭深度写入!
(2)理想情况下,对于所有的半透明物体按照距摄像机由远及近的顺序依次渲染。

Unity通过渲染队列来解决渲染顺序

通过SubShader的Queue标签来决定模型将归于哪个渲染队列。Unity在内部使用一系列整数索引来表示每个渲染队列,且索引号越小表示越早被渲染!

将模型划分给相应的渲染队列,Unity根据已规定好的渲染队列顺序去对各个渲染队列进行顺序渲染,而渲染队列中的模型则根据渲染队列本身的规定顺序进行渲染,这里主要强调Transparent渲染队列本身的顺序----将半透明的物体按照距摄像机由远及近的顺序依次渲染。
在这里插入图片描述
例如:
在这里插入图片描述

对于复杂的半透明物体

即半透明物体表面的正面和背面相互覆盖(遮挡)的情况,如何渲染?

代码示例

通过透明度测试实现透明效果

三个基本的标签:
Queue标签属性值设置为 AlphaTest ,表明将该Shader(即使用该shader的模型归为AlphaTest渲染队列中)
RenderType 标签可以让 Unity 把这个 Shader 归入到提前定义的组(这里就是 TransparentCutout 组)中,以指明该 Shader是一个使用了透明度测试的 Shader
RenderType 标签通常被用于着色器替换功能。
IgnoreProjector 设置为 True, 这意味着这个 Shader 不会受到投影器 (Projectors) 的影响。通常,
使用了透明度测试的 Shader 应该在 SubShader 中设置这三个标签。
最后 LightMode 标签是 Pass标签中的一种,它用于定义该 Pass通道在Unity 的光照流水线中的角色。只有定义了正确的 LightMode,我们才能正确得到一些 Unity 的内置光照变量,例如_LightColor0。

Shader "Unlit/016"
{
    Properties
    {
        _MainTex("MainTex",2D) = "White"{}
         _Diffuse("Diffuse",Color) = (1,1,1,1)
        _Cutoff("Alpha Cutoff",Range(0,1))=0.5
    }
        SubShader
         {
             Tags { "RenderType" = "TransparentCutout" "Queue"="AlphaTest" "IngnoreProjector"="True"}
             LOD 100

             Pass
             {
                 CGPROGRAM
                 #pragma vertex vert
                 #pragma fragment frag
                 #include "UnityCG.cginc"
                 #include "Lighting.cginc"
                 fixed4 _Diffuse;
                 sampler2D _MainTex;
                 float4 _MainTex_ST;
                 float _Cutoff;
                 //用作顶点着色输出的输出结构体至少包含绑定了SV_POSITION语义的变量
                 struct v2f {
                     float4 vertex:SV_POSITION;
                     fixed3 worldnormal : TEXCOORD0;
                     float3 worldvertex:TEXCOORD1;
                     float2 uv:TEXCOORD2;
                 };

                 //通过片元着色器实现的漫反射+Blinn高光反射光照模型+添加纹理贴图

                 v2f vert(appdata_base v)
                 {
                     v2f o;
                     o.vertex = UnityObjectToClipPos(v.vertex);
                     o.worldnormal = UnityObjectToWorldNormal(v.normal);
                     o.worldvertex = mul(unity_ObjectToWorld, v.vertex);
                     o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                     return o;
                 }
                 fixed4 frag(v2f i) : SV_TARGET
                 {
                     fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                     //纹理采样
                     fixed4 colortest = tex2D(_MainTex,i.uv);
                     if ((colortest.a - _Cutoff) < 0) {
                         discard;
                     }
                     fixed3 worldlightdirection = normalize(_WorldSpaceLightPos0.xyz);
                     fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * colortest.rgb * (dot(i.worldnormal, worldlightdirection) * 0.5 + 0.5);
                     return fixed4(ambient + diffuse,1);
                 }
                     ENDCG
                 }
         }
}

<think>好的,我现在需要回答用户关于在Unity Shader中实现半透明效果的教程。首先,我需要回忆Unity中处理半透明的两种主要方法:透明度测试和透明度混合。用户已经提到了这两种方法,但重点在于半透明效果,所以应该聚焦于透明度混合。 接下来,我应该详细说明透明度混合的原理。需要提到关闭深度写入的重要性,以及渲染顺序的问题,因为不透明物体和半透明物体的渲染顺序会影响最终效果。这里可能需要引用用户提供的参考资料,特别是引用[1]中提到的渲染顺序步骤。 然后,我需要提供一个具体的Shader代码示例,展示如何编写一个实现半透明效果的Unity Shader。代码部分要使用正确的语法,结构清晰,包括Properties、SubShader、Tags、Pass块等。要确保代码中的关键部分有注释,解释每部分的作用,比如混合模式的使用和深度写入的设置。 此外,还需要提到一些注意事项,比如渲染队列的设置(“Transparent”)、混合方程的选择(这里用了Blend SrcAlpha OneMinusSrcAlpha,即传统透明度混合),以及可能的性能影响。可能还要建议用户如何处理复杂情况下的物体排序问题,或者使用其他混合模式。 最后,生成相关问题,帮助用户进一步学习,比如优化方法、与透明度测试的区别,以及混合模式的其他应用。这些问题需要与主题紧密相关,并且引导用户深入探索。 需要确保回答符合用户给出的格式要求,使用中文,正确应用LaTeX格式的数学表达式(如果有的话),但在这个问题中可能涉及较少数学公式,但如果有提到颜色混合的公式,比如使用$C_{result} = C_{src} \times \alpha + C_{dst} \times (1 - \alpha)$,需要用行内格式。同时,引用标记要正确添加在相关段落的末尾。 现在整理这些思路,按照结构化的步骤来撰写回答,确保每个部分都覆盖必要的信息,并且引用正确。检查是否有遗漏的关键点,比如代码示例的正确性,混合模式的具体参数,以及渲染队列的设置是否正确。确认所有技术术语使用准确,没有错误。</think>### Unity Shader半透明效果实现教程 在Unity中实现半透明效果主要依赖**透明度混合(Alpha Blending)**技术,通过控制片元的透明通道值($0 \leq \alpha \leq 1$)实现颜色混合。以下是具体实现步骤: --- #### 一、核心原理 1. **颜色混合公式** 透明度混合通过以下公式计算最终颜色: $$C_{result} = C_{src} \times \alpha + C_{dst} \times (1 - \alpha)$$ 其中$C_{src}$为当前片元颜色,$C_{dst}$为已存在颜色缓冲区的颜色[^1]。 2. **渲染顺序与深度写入** - 需关闭半透明物体的深度写入(`ZWrite Off`),避免遮挡后续物体。 - 渲染顺序需按**从后到前**排序,确保颜色混合正确[^1]。 --- #### 二、Shader代码实现 ```glsl Shader "Custom/AlphaBlend" { Properties { _MainTex ("Texture", 2D) = "white" {} _Alpha ("Alpha", Range(0,1)) = 0.5 } SubShader { Tags { "Queue"="Transparent" // 设置渲染队列为透明物体 "RenderType"="Transparent" } Pass { ZWrite Off // 关闭深度写入 Blend SrcAlpha OneMinusSrcAlpha // 设置混合模式 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; }; sampler2D _MainTex; float _Alpha; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col.a *= _Alpha; // 控制整体透明度 return col; } ENDCG } } } ``` --- #### 三、关键配置说明 1. **混合模式(Blend)** - `Blend SrcAlpha OneMinusSrcAlpha`:最常用的传统透明度混合模式,对应公式$C_{result} = C_{src} \times \alpha + C_{dst} \times (1 - \alpha)$。 - 其他模式如`Blend One One`可用于加色混合(如发光效果)。 2. **渲染队列(Render Queue)** - 设置`"Queue"="Transparent"`确保半透明物体在不透明物体之后渲染。 3. **性能注意事项** - 半透明物体过多时,排序可能影响性能,需合理控制物体数量[^1]。 --- #### 四、测试与应用场景 1. **测试方法** 将Shader赋给材质后,调整`_Alpha`参数观察透明度变化,或使用带透明通道的纹理。 2. **典型应用** - 玻璃、水面等半透明物体 - 粒子特效(烟雾、火焰) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值