《Unity 入门精要》第8章 透明效果

这篇具有很好参考价值的文章主要介绍了《Unity 入门精要》第8章 透明效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第8章 透明效果

在 Unity 中,我们通常使用两种方法来实现透明效果:透明度测试(Alpha Test)透明度混合(Alpha Blending)
当我们渲染不透明物体时,我们不需要特别考虑渲染顺序的问题,因为有深度缓冲(depth buffer,也称 z-buffer)的存在,它的基本思想是:根据深度缓存中的来判断该片元距离摄像机的距离,当渲染一个片元是,需要把它的深度值和已经存在于深度缓存中的值来做对比,如果它的值距离摄像机更远,那么说明这个片元不应该被渲染到屏幕上;否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲中。
但如果想要实现透明效果,事情就不那么简单了,这是因为,当使用透明度混合时,我们关闭了深度写入(Write)
简单来说,透明度测试和透明度混合的基本原理如下:

  • 透明度测试:它采用一种“霸道极端”的机制,只要一个片元的透明度不满足条件(通常是小于某个阈值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。也就是说,透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是它会根据透明度来舍弃一些片元。虽然简单,但是它产生的效果也很极端,要么完全透明,要么完全不透明。
  • 透明度混合:这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。需要注意的是,透明度混合只关闭了深度写入,但没有关闭深度测试。这意味着,当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果它的深度值距离摄像机更远,那么就不会再进行混合操作。这一点决定了,当一个不透明物体出现在一个透明物体的前面,而我们先渲染了不透明物体,它仍然可以正常地遮挡住透明物体。也就是说,对于透明度混合来说,深度缓冲是只读的。

8.1 为什么渲染顺序很重要

在使用透明度混合时,需要关闭深度写入,此时渲染顺序将变得非常重要。我们可以思考一下以下几种渲染情况。

半透明物体 A + 不透明物体 B,A 在 B 前

《Unity 入门精要》第8章 透明效果
此种情况有两种渲染顺序:

  • 先渲染 B 再渲染 A, B 会正常写入颜色缓冲,然后 A 会和颜色缓冲中的 B 颜色进行混合,得到正确的半透明效果
  • 先渲染 A 再渲染 B,A 会正常写入颜色缓冲,但是因为半透明物体关闭了深度写入,因此 A 不会修改深度缓冲。接着渲染 B,B会进行深度测试,它发现,“咦,深度缓存中还没有人来过,那我就放心地写入颜色缓冲了!”,结果就是 B 会直接覆盖 A 的颜色,从视觉上来看,B就出现在了A的前面,而这是错误的。

由此可知,我们应该在渲染完不透明物体后再渲染半透明物体

A B 均为半透明,A 在 B 前

《Unity 入门精要》第8章 透明效果
此种情况有两种渲染顺序:

  • 先渲染 B 再渲染 A, B 会正常写入颜色缓冲,然后 A 会和颜色缓冲中的 B 颜色进行混合,得到正确的半透明效果
  • 先渲染 A 再渲染 B,A 会正常写入颜色缓冲,然后 B 会和颜色缓冲中的 A 颜色进行混合,这样混合结果会完全反过来,看起来就好像 B 在 A 前面一样,得到错误的半透明结构。

可以看出,半透明物体之间应该先渲染离摄像机远的物体再渲染近的,才能得到正确的效果

循环重叠的半透明物体

基于前面两点,渲染引擎通常会对物体的渲染顺序做一下两点优化:

  1. 先渲染所有不透明物体,并开启它们的深度测试和深度写入。
  2. 把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。

但就算做了这两点优化,有些时候还是会出现错误的透明效果,比如存在循环重叠的物体:
《Unity 入门精要》第8章 透明效果
这是因为深度缓冲中的值其实是像素级别的,即每个像素有一个深度值,但是现在我们对单个物体级别进行排序,这意味着排序结果是,要么物体 A 全部在 B 前面渲染,要么 A 全部在 B 后面渲染。但如果存在循环重叠的情况,我们永远不可能得到一个正确的排序顺序。这种时候,我们可以选择把物体拆分成两个部分,然后再进行正确的排序。但即便我们通过分割的方法解决了循环覆盖的问题,还是会有其他的错误情况。

无法判定物体排序规则的情况

《Unity 入门精要》第8章 透明效果
这里的问题是:如何排序?我们知道,一个物体的网格结构往往占据了空间中的某一块区域,也就是说,这个网格上每一个点的深度值可能都是不一样的,我们选择哪个深度值来作为整个物体的深度值和其他物体进行排序呢?是网格中点吗?还是最远的点?还是最近的点?不幸的是,对于上图中的情况,选择哪个深度值都会得到错误的结果,我们的排序结果总是 A 在 B 的前面,但实际上 A 有一部分被 B 遮挡了。这也意味着,一旦选定了一种判断方式后,在某些情况下半透明物体之间一定会出现错误的遮挡问题。这种问题的解决方法通常也是分割网格。
尽管总是会有一些情况打乱我们的阵脚,但由于上述方法足够有效并且容易实现,因此大多数游戏引擎都使用了这样的方法。为了减少错误排序的情况,我们可以尽可能让模型是凸面体,并且考虑将复杂的模型拆分成可以独立排序的多个子模型等

8.2 Unity Shader 中的渲染顺序

Unity 为了解决渲染顺序的问题设计了渲染队列(render queue),我们可以使用 SubShader 的 Queue 标签来决定我们的模型将归于哪个渲染队列。Unity 在内部使用一系列整数索引来表示每个渲染队列,且索引号越小表示越早被渲染,并且Unity提前定义了5个渲染队列:
《Unity 入门精要》第8章 透明效果
要设置 Shader 的渲染队列,可以通过在 SubShader 中指定相应标签:

SubShader {
	// 指定渲染队列为 AlphaTest
    Tags { "Queue"="AlphaTest" }
    Pass {
        ...
    }
}

也可以直接在编辑器中直接指定 Shader 的渲染队列:
《Unity 入门精要》第8章 透明效果

8.3 透明度测试

透明度测试原理:只要一个片元的透明度不满足条件(通常是小于某个阈值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它。
通常我们在片元着色器中使用 clip 函数来进行透明度测试,它是 Cg 中的一个函数。
下面我们实现一个包含透明度测试的 Shader :

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Chaptor 8/SHA_AlphaTest"
{
    Properties
    {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        // 用于控制透明度测试的边界值
        _CutOff ("Alpha CutOff", Range(0, 1.0)) = 0.5
    }
    SubShader
    {
        // 通过标签 Queue 指定渲染队列为 AlphaTest
        // RenderType 标签可以让Unity把这个Shader归入到提前定义的组中,以指明该 Shader 是一个使用了透明度测试的Shader。
        // RenderType标签通常被用于着色器替换功能
        // IgnoreProjector 设置为 true 意味着这个 Shader 不会受投影器影响
        // 通常,一个透明度测试 Shader 都需要设置这三个标签
        Tags {"Queue"="AlphaTest" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _CutOff;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldNormal = normalize(i.worldNormal);
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);
                // 使用 clip 函数进行透明度测试,等价于代码
                // if((texColor.a - _CutOff) < 0.0){
                //     discard
                // }
                clip(texColor.a - _CutOff);

                fixed3 albedo = texColor * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                return fixed4(ambient + diffuse, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

效果如下,左侧为正常方块,右侧为开启了透明度测试的方块:
《Unity 入门精要》第8章 透明效果

可以在编辑器中通过参数 CutOff 参数来设置透明度效果。
可以看出,透明度测试得到的透明效果很“极端”——要么完全透明,要么完全不透明,它的效果往往像在一个不透明物体上挖了一个空洞。而且,得到的透明效果在边缘处往往参差不齐,有锯齿,这是因为在边界处纹理的透明度的变化精度问题。为了得到更加柔滑的透明效果,就可以使用透明度混合。

8.4 透明度混合

透明度混合:使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。
为了进行混合,我们需要使用 Unity 提供的混合命令Blend。Blend 是 Unity 提供的设置混合模式的命令。想要实现半透明的效果就需要把当前自身的颜色和已经存在于颜色缓冲中的颜色值进行混合,混合时使用的函数就是由该指令决定的。
《Unity 入门精要》第8章 透明效果
只有开启了混合,设置片元的透明通道才有意义。
下面是一个透明度混合的例子,其中采用Blend SrcFactor DstFactor语义,并设置 SrcFactorSrcAlphaDstFactorOneMinusSrcAlpha,这样讲采用如下的混合算法:
《Unity 入门精要》第8章 透明效果
更多混合语义的说明可查看官方文档。下面是 Shader 代码:

Shader "Chaptor 8/SHA_AlphaBlend"
{
    Properties
    {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        // 用于控制整体的透明度
        _AlphaScale ("Alpha Scale", Range(0, 1.0)) = 1
    }
    SubShader
    {
        // 通过标签 Queue 指定渲染队列为 Transparent
        Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            // 深度写入设置为关闭状态
            ZWrite off
            // 开启混合,并设置相应混合因子
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaScale;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldNormal = normalize(i.worldNormal);
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 albedo = texColor * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

效果如下,左侧为透明度测试效果,右侧为透明度混合效果:
《Unity 入门精要》第8章 透明效果

8.5 开启深度写入的半透明效果

由于在进行透明度混合时关闭了深度写入,所以当模型中有互相交叉的结构时,往往会得到错误的半透明效果:
《Unity 入门精要》第8章 透明效果
为了解决这个问题,我们可以使用两个 Pass 来渲染,第一个 Pass 开启深度写入,但不输出颜色,它的目的仅仅是把模型的深度值写入深度缓冲中;第二个 Pass 进行正常的透明度混合,由于上一个 Pass 已经得到逐像素的正确的深度信息,该 Pass 就可以按照像素级别的深度排序结果进行透明渲染。但这种的方法的缺点在于多使用了一个 Pass,会对性能造成一定影响
实现的 Shader 如下:

Shader "Chaptor 8/SHA_AlphaBlendZWrite"
{
    Properties
    {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        // 用于控制整体的透明度
        _AlphaScale ("Alpha Scale", Range(0, 1.0)) = 1
    }
    SubShader
    {
        // 通过标签 Queue 指定渲染队列为 Transparent
        Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}

        Pass {
            // 第一个 Pass 开启深度写入
            ZWrite on
            // ColorMask 为渲染命令,用于设置颜色通道的写掩码
            // ColorMask 0 表示该 Pass 不写入任何颜色通道,即不会输出任何颜色
            ColorMask 0
        }

        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            // 深度写入设置为关闭状态
            ZWrite off
            // 开启混合,并设置相应混合因子
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaScale;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldNormal = normalize(i.worldNormal);
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 albedo = texColor * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

效果如下:
《Unity 入门精要》第8章 透明效果

ShaderLab 的混合命令

混合就是通过混合等式(blend equation)源颜色(source color)目标颜色(destination color)进行相应计算产生输出颜色(output color)

  • 源颜色,我们用 S 表示,指的是由片元着色器产生的颜色值
  • 目标颜色,我们用 D 表示,指的是从颜色缓冲中读取的颜色值
  • 输出颜色,我们用 O 表示,指的是混合完成我们得到的结果颜色值
  • S、D、O 都是包含了 RGBA 四个通道的颜色值。
  • 混合等式就是用于操作计算 S 和 D 的函数。当进行混合时,通常我们需要使用两个混合等式,一个用于混合 RGB 通道,一个用于混合 A 通道。当设置混合状态时,我们实际设置的就是混合等式中的操作和因子。

混合等式和混合因子

Unity ShaderLab 使用渲染命令 Blend 来设置混合等式的混合因子:
《Unity 入门精要》第8章 透明效果
ShaderLab 支持的常用混合因子如下:
《Unity 入门精要》第8章 透明效果
更详细信息见Unity 官方手册

混合操作

我们可以使用渲染命令BelndOp 来设置混合操作类型,默认的操作类型为加法,支持的常见混合操作如下:
《Unity 入门精要》第8章 透明效果
更详细信息见Unity 官方手册

常见的混合类型

下面列出了常见的几种混合类型:

// 正常(Normal),即透明度混合
Blend SrcAlpha OneMinusSrcAlpha

// 柔和相加(Soft Additive)
Blend OneMinusDstColor One

// 正片叠底(Multiply),即相乘
Blend DstColor Zero

// 两倍相乘(2x Multiply)
Blend DstColor SrcColor

// 变暗(Darken)
BlendOp Min
Blend One One

// 变亮(Lighten)
BlendOp Max
Blend One One

// 滤色(Screen)
Blend OneMinusDstColor One
// 等同于
Blend One OneMinusSrcColor

// 线性减淡(Linear Dodge)
Blend One One

对应的混合效果如下图:
《Unity 入门精要》第8章 透明效果

双面渲染的透明效果

在现实生活中,如果一个物体是透明的,意味着我们不仅可以透过它看到其他物体的样子,也可以看到它内部的结构。但在前面实现的透明效果中,无论是透明度测试还是透明度混合,我们都无法观察到正方体内部及其背面的形状,导致物体看起来就好像只有半个一样。这是因为,**默认情况下渲染引擎剔除了物体背面(相对于摄像机的方向)的渲染图元,而只渲染了物体的正面。**如果我们想要得到双面渲染的效果,可以使用 Cull 指令来控制需要剔除哪个面的渲染图元。

Cull Back | Front | Off
  • Back:剔除背面,背对着摄像机的图元不会被渲染,Shader 默认使用此种剔除模式
  • Front:剔除正面,炒香摄像机的图元不会被渲染
  • Off:关闭剔除,所有图元都会被渲染,但图元数会成倍增加,谨慎使用

透明度测试的双面渲染

在 Pass 中关闭剔除即可:

Shader "Chaptor 8/SHA_AlphaTest"
{
    Properties
    {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _CutOff ("Alpha CutOff", Range(0, 1.0)) = 0.5
    }
    SubShader
    {
        Tags {"Queue"="AlphaTest" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            // 关闭剔除
            Cull off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _CutOff;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldNormal = normalize(i.worldNormal);
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);
                clip(texColor.a - _CutOff);

                fixed3 albedo = texColor * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                return fixed4(ambient + diffuse, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

效果如下:
《Unity 入门精要》第8章 透明效果

透明度混合的双面渲染

和透明度测试相比,想要让透明度混合实现双面渲染会更复杂一些,这是因为透明度混合需要关闭深度写入,所以我们需要保证渲染顺序的正确性。我们想要保证图元是从后往前渲染的。为此,我们选择把双面渲染的工作分成两个Pass,第一个Pass只渲染背面,第二个Pass只渲染正面,由于Unity会顺序执行SubShader中的各个Pass,因此我们可以保证背面总是在正面被渲染之前渲染,从而可以保证正确半透明效果:

Shader "Chaptor 8/SHA_AlphaBlend"
{
    Properties
    {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _AlphaScale ("Alpha Scale", Range(0, 1.0)) = 1
    }
    SubShader
    {
        Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}

        Pass {
 Tags {"LightMode" = "ForwardBase"}
            // 先剔除正面渲染背面
            Cull Front
            ZWrite off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaScale;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldNormal = normalize(i.worldNormal);
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 albedo = texColor * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
            }
            ENDCG
        }

        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            // 再剔除背面渲染正面
            Cull Back
            ZWrite off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaScale;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldNormal = normalize(i.worldNormal);
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 albedo = texColor * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

效果如下:
《Unity 入门精要》第8章 透明效果文章来源地址https://www.toymoban.com/news/detail-496857.html

到了这里,关于《Unity 入门精要》第8章 透明效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《Unity Shader 入门精要》笔记07

    Unity中通常使用两种方法来实现透明效果:第一种是试用 透明度测试(Alpha Test) ,这种方法其实无法得到真正的半透明效果;另一种是 透明度混合(Alpha Blending) 。 由于深度缓冲的存在,可以让不透明物体不考虑他们渲染顺序也能得到正确的排序效果。但是实现透明效果需

    2024年02月07日
    浏览(43)
  • Unity Shader学习3:透明效果

    Unity中的透明效果由透明通道控制(RGBA中的A),其值为0是完全透明,为1时完全不透明。有两种方法可以实现透明效果: 透明度测试(Alpha Test) 和 透明度混合(Alpha Blend) 。 透明度测试是指通过特定的条件(通常是Alpha通道的值是否超过某个阈值)来判断片元是否透明,只

    2024年01月19日
    浏览(62)
  • Unity 设置贴图黑色区域为透明效果

    TexstureType为默认类型 勾选 Alpha from GrayScale 勾选 Alpha Is Transparent 材质球使用默认材质 Shader选为Standard 它的Rendering Mode选择Transparent

    2024年02月15日
    浏览(47)
  • [Unity Shader入门精要]初级篇 代码拆解

    简介: 介绍了Unity Shader入门精要中初级篇包含的所有代码, 通过详细拆解代码 ,一步一步揭晓Shader的原理。 5.2.1 顶点/片元着色器的基本结构 说人话: •第一行是着色器的名字,用大括号{} 包裹后续所有的Shader代码, Shader。 •紧接着是Shader的属性,用大括号{} 包裹

    2024年02月03日
    浏览(44)
  • Unity入门精要04(0)-更复杂的光照

    本书知识点是真的多( Unity中的渲染路径有如下选项  对某一Pass指定了渲染路径后,Unity会在过程中自动填充系统的变量,便于使用。    前向渲染的伪代码如下 一句话概括前向渲染就是:对于每个光源在其范围能影响物体的都计算一次所有的Pass(除了Base Pass以外),计算

    2023年04月12日
    浏览(32)
  • 学习100个Unity Shader (14) ---透明效果

    由”Queue“ 标签决定,索引号越小越早被渲染: 名称 队列索引号 Background 1000 Geometry 2000 AlphaTest 2450 Transparent 3000 Overlay 4000 某一片元的透明度小于某个阈值,即被舍弃,反之,按非透明物体处理,进行正常的深度测试和深度写入【不需要关闭深度写入】。 UnityObjectToClipPos 将顶

    2024年04月29日
    浏览(45)
  • Unity Shader入门精要 第六章——Unity中的基础光照

    目录 一、标准光照模型(Phong光照模型) 1、环境光  2、自发光 3、漫反射 4、高光反射 (1)Phong模型 (2)Blinn模型 5、光照模型实现方法——逐顶点和逐像素 二、Unity Shader 漫反射光照模型的实现 1、实践:逐顶点 2、实践:逐像素 3、半兰伯特模型 4、漫反射光照模型效果展

    2024年02月04日
    浏览(51)
  • Unity Shader学习记录(11) ——透明效果的实现方式

    1 透明效果的两种方法 透明是游戏中经常要使用的一种效果。在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道(Alpha Channel)。当开启透明混合后,当一个物体被渲染到屏幕上时,每个片元除了颜色值和深度值之外,它还有另一个属性一一透明度。 当透明度

    2024年02月07日
    浏览(45)
  • Unity中的渲染优化技术 -- Shader入门精要学习(15)

    本章中,我们将阐述一些 Unity 中常见的优化技术。这些优化技术都是和渲染相关的,例如,使用批处理、LOD 技术等。 游戏优化不仅是程序员的工作,更需要美工人员在游戏的美术上进行一定的权衡。例如,避免使用全屏的屏幕特效,避免使用计算复杂的 Shader,减少透明混合

    2024年01月18日
    浏览(43)
  • 【UnityShader入门精要学习笔记】第六章(1)Unity中的基础光照

    本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 + 个人批注 项目源码 一堆新手会犯的错误 潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 一个物体为什么看起来是红色的?从物理上解释是因为这个物体

    2024年03月22日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包