Unity Shader学习3:透明效果

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

透明效果原理

Unity中的透明效果由透明通道控制(RGBA中的A),其值为0是完全透明,为1时完全不透明。有两种方法可以实现透明效果:透明度测试(Alpha Test)透明度混合(Alpha Blend)

透明度测试(Alpha Test)

透明度测试是指通过特定的条件(通常是Alpha通道的值是否超过某个阈值)来判断片元是否透明,只有完全透明和完全不透明两种状态。完全透明时,该片元直接被舍弃,不会对颜色缓冲产生任何影响,完全不透明时,就会被当做普通的不透明物体进行处理(进行深度测试和深度写入等)。

透明度混合(Alpha Blend)

透明度混合可以得到真正的半透明效果。它会使用当前片元的透明度做为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。

透明度混合需要关闭深度写入。如果不关闭深度写入,在渲染一个半透明物体的片元时,如果片元后面有不透明物体,那么写入深度缓存后,不透明物体的片元就会被剔除,此时透过半透明物体无法看到不透明物体,这样效果就出错了。

关闭深度写入后,渲染顺序会变得尤为重要。
Unity Shader学习3:透明效果,unity,学习,游戏引擎
如图8.1中,若先渲染A,由于关闭了半透明物体的深度写入,此时深度缓冲中不会写入A的深度值,再渲染B的时候,由于B是不透明物体,且深度缓冲中没有值,那么B的片元则会通过深度测试,写入深度值,同时覆盖颜色缓冲中A之前渲染得到的颜色值,效果就变成了A被B遮挡。

如图8.2中,若先渲染A, 那么A的颜色会先写入颜色缓冲,再渲染B,B片元的颜色值和颜色缓冲的值混合时,混合后的效果会反过来,看起来像B覆盖在A上。

可以看出,渲染含有半透明物体的场景分为两个步骤:
a. 先渲染所有不透明物体,开启深度测试和深度写入。
b. 将半透明物体按照他们距离摄像机的远近进行排序,并按照从后往前的顺序渲染这些半透明物体,开启深度测试,但关闭深度写入。

这里有个隐患:按照离摄像机的远近排序物品不能完全保证渲染片元的顺序,比如如下图所示的互相交叠的情况。
Unity Shader学习3:透明效果,unity,学习,游戏引擎
为了减少错误的排序,我们可以尽可能让模型是凸面体,或者将复杂的模型拆分成可以独立排序的多个子模型等。如果不想分隔网格,可以试着让透明通道更加柔和(?这啥意思),使穿插看起来不是那么明显。

Unity Shader 的渲染顺序

Unity 为了解决渲染顺序的问题提供了渲染队列(render queue) 这一解决方案。SubShade 的 Queue 标签来决定模型将归于哪个渲染队列。队列索引号越小越先渲染。
Unity Shader学习3:透明效果,unity,学习,游戏引擎

Unity Shader 实现透明度测试

透明度测试中重点的代码如下,其含义是若参数值小于零,则该片元被舍弃,不对颜色缓冲产生任何影响。

clip(nAlpha - nCutOff)

完整代码如下:

// 透明度测试:判断片元的透明度是否满足条件(比如小于某个阈值),若不满足则舍弃,该片元不会对颜色缓冲产生任何影响,若满足,则按照不透明物体的处理方式来处理(进行深度测试,深度写入等),所以透明度测试不需要关闭深度写入
// 透明度混合:它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合得到新的颜色。透明度混合关闭了深度写入,但没有关闭深度测试,深度缓冲的值是只读的,保证能够遮挡出于不透明物体后面的透明物体。
Shader "Custom/Chapter8-AlphaTest"
{
    Properties{
        _Color("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex("Main Tex", 2D) = "white" {}
        _Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5  // 决定片元是否显示的透明度阈值
    }

    SubShader{
        
        // AlphaTest是Unity中透明度测试使用的渲染队列(在所有不透明物体(渲染队列为Geometry)渲染之后)
        // IgnoreProjector设为true可以让该shader不受投影器(Projector)的影响
        // RenderType标签可以让Unity把这个shader归入到提前定义的组(这里就是TransparentCutout组中),以指明改shader是一个实用了透明度测试的shader
        // 以上太长不看:通常使用了透明度测试的shader都应该在SubShader中设置这三个标签
        Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}

        Pass{
            Tags {"LightMode" = "ForwardBase"}

            CGPROGRAM
            
            #include "Lighting.cginc"

            #pragma vertex vert 
            #pragma fragment frag 

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Cutoff;

            struct a2v {
                float4 pos : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };

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

            v2f vert(a2v i)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(i.pos);
                o.worldPos = mul(unity_ObjectToWorld, i.pos).xyz;
                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                o.uv = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                float3 worldLight = normalize(UnityWorldSpaceLightDir(i.pos));
                fixed4 texColor = tex2D(_MainTex, i.uv);

                // if (texColor.a < _Cutoff)
                // {
                //     discard;
                // }
                // or 
                clip(texColor.a - _Cutoff);     // 参数值小于0,则该片元被舍弃

                fixed3 albedo = texColor.rgb * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldLight, i.worldNormal));

                return fixed4((ambient + diffuse), 1);
            }

            ENDCG
        }
    }

    // 这里如果设置成diffuse,透明处会有阴影,为何?
    Fallback "Transparent/Cutout/VertexLit"
}

效果如下:
Unity Shader学习3:透明效果,unity,学习,游戏引擎
透明度测试得到的效果很极端——要么完全透明,要么完全不透明。如果想要更柔滑的透明效果,可以使用透明度混合。

Unity Shader 实现透明度混合

为了进行透明度混合,我们需要使用 Unity 提供的混合命令:Blend
Unity Shader学习3:透明效果,unity,学习,游戏引擎
混合命令进行的操作是,将片元的颜色值和颜色缓冲中的颜色值分别乘以不同的混合因子后再相加,得到新的颜色值写入颜色缓冲。
D s t C o l o r n e w = S r c F a c t o r ⋅ S r c C o l o r + D s t F a c t o r ⋅ D s t C o l o r o l d DstColor_{new} = SrcFactor · SrcColor + DstFactor · DstColor_{old} DstColornew=SrcFactorSrcColor+DstFactorDstColorold

具体代码如下:

Shader "Custom/Chapter8-AlphaBlend"
{
    Properties
    {
        _Color("Color Tint", Color) = (1, 1, 1, 1)   
        _MainTex("Main Tex", 2D) = "white" {}
        _AlphaScale("Alpha Scale", Range(0, 1)) = 1
    }

    SubShader{
        Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}

        Pass{
            Tags{"LightMode" = "ForwardBase"}

            ZWrite Off // 关闭深度写入后无法进行像素级别的深度排序,在模型网格之间有相互交叉的结构是不能得到正确的半透明效果
            Blend SrcAlpha OneMinusSrcAlpha     // 开启混合并设置混合因子

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            fixed4 _MainTex_ST;
            float _AlphaScale;

            struct a2v {
                float4 pos : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };

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

            v2f vert(a2v i)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(i.pos);
                o.worldPos = mul(unity_ObjectToWorld, i.pos).xyz;
                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                o.uv = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target 
            {
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.pos));
                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 albedo = texColor.rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldLight, i.worldNormal));

                return fixed4((ambient + diffuse), texColor.a * _AlphaScale);   // 只有打开了混合,这里设置alpha通道才有意义,否则透明度值不会对片元的透明效果有任何影响
            }


            ENDCG

        }
    }

    Fallback "Transparent/VertexLit"
}

最后效果如下:
Unity Shader学习3:透明效果,unity,学习,游戏引擎

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

上述的 AlphaBlend 有一个问题,由于关闭了深度写入,缺少像素级的深度缓冲值,物体自身发生交叠时,前后的片元颜色会混合在一起。Unity Shader学习3:透明效果,unity,学习,游戏引擎
一种解决方式是使用两个 Pass来渲染模型。第一个Pass开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中。第二个Pass进行正常的透明度混合。由于上一个Pass已经得到了逐像素的深度信息,第二个Pass就可以按照像素级别的深度排序结果进行透明渲染。这种做法的缺点是额外的Pass会对性能造成一定的影响,但可以让最终效果的模型内部之间不会有半透明效果。

代码的修改与之前的 Alpha Blend 几乎一样,只需要在原来的 Pass 之前增加一个 Pass:

        Pass{
            ZWrite On   // 写入深度数据
            ColorMask 0 // 用于设置颜色通道的写掩码,设为0即不输出任何颜色
        }

效果如下:
Unity Shader学习3:透明效果,unity,学习,游戏引擎

双面渲染的透明效果

在现实生活中,如果一个物体是透明的,意味着我们可以透过物体表面看到它的内部结构,但上述的效果没能做到这一点,这是因为默认情况下渲染引擎提出了物体背面的渲染图元。要得到双面渲染的透明效果,我们需要使用 Cull 指令来控制剔除哪个面的渲染图元

Cull Back | Front | Off

设置为Back,那么背对摄像机的图元不会被渲染,设置为Front,正对摄像机的图元不会被渲染,设置为Off,所有的图元都会被渲染。

透明度测试的双面渲染

Shader的修改在AlphaTest的基础上只在Pass中增加了一行:

        Pass{
            Tags{"LightMode" = "ForwardBase"}

            Cull off    // 比起AlphaTest只添加了这一行代码实现双面渲染

效果如下:
Unity Shader学习3:透明效果,unity,学习,游戏引擎

透明度混合的双面渲染

由于透明度混合需要关闭深度写入,所以要保证背向摄像机的图元永远在面向摄像机的图元之前被渲染。实现这种效果的方式是设置两个Pass,第一个Pass剔除正面,第二个Pass剔除背面。

具体代码如下:

Shader "Custom/Chapter8-AlphaBlendBothSided"
{
    Properties{
        _Color("Color Tint", Color) = (1, 1, 1, 1)
        _AlphaScale("Alpha Scale", Range(0, 1)) = 0.5
        _MainTex("Main Tex", 2D) = "white" {}
    }

    SubShader{
        Tags{"RenderType" = "TransParent" "Queue" = "Transparent" "IgnoreProjector" = "True"}

        Pass{
            Tags{"LightMode" = "ForwardBase"}

            ZWrite Off 
            Blend SrcAlpha OneMinusSrcAlpha

            Cull Front 

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            fixed4 _MainTex_ST;
            float _AlphaScale;

            struct a2v {
                float4 pos : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };

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

            v2f vert(a2v i)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(i.pos);
                o.worldPos = mul(unity_ObjectToWorld, i.pos).xyz;
                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                o.uv = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target 
            {
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.pos));
                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 albedo = texColor.rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldLight, i.worldNormal));

                return fixed4((ambient + diffuse), texColor.a * _AlphaScale);
            }
            ENDCG
        }

        Pass{
            Tags{"LightMode" = "ForwardBase"}

            ZWrite Off 
            Blend SrcAlpha OneMinusSrcAlpha

            Cull Back 

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            fixed4 _MainTex_ST;
            float _AlphaScale;

            struct a2v {
                float4 pos : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };

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

            v2f vert(a2v i)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(i.pos);
                o.worldPos = mul(unity_ObjectToWorld, i.pos).xyz;
                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                o.uv = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target 
            {
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.pos));
                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 albedo = texColor.rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldLight, i.worldNormal));

                return fixed4((ambient + diffuse), texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
    Fallback "Transparent/VertexLit"
}

效果如下:
Unity Shader学习3:透明效果,unity,学习,游戏引擎文章来源地址https://www.toymoban.com/news/detail-806180.html

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

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

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

相关文章

  • Unity Shader入门精要 第八章——透明效果

    目录 前言 一、渲染顺序的重要性 二、Unity Shader的渲染顺序 三、透明度测试 1、什么是透明度测试 2、实践 四、透明度混合 1、什么是透明度混合 2、实践  五、开启深度写入的半透明效果 1、实现方法  2、实践 六、ShaderLab的混合命令 1、混合等式和参数 2、混合操作  3、常见

    2024年02月04日
    浏览(42)
  • 【Unity Shader Graph URP渲染管线下的自定义半透明效果_半透明案例分享】

    URP的渲染管线下 在项目设置里找到“Graphic” 找到URP Asset文件 索引到Renderer List文件——“ForwardRenderer” 在这个“ForwardRenderer”文件里找到“Add Renderer Feature” 添加一个渲染对象,类似下图:Render Object (Experimental) 如图设置,将“Event”设置成 AfterRenderingSkybox ,然后“Layer M

    2024年02月09日
    浏览(58)
  • unity Shader实现半透明阴影

    在shader中,要对移动端的兼容,还不想实现两套分开兼容的话, 这两句话一定要改掉,第一行代码直接剔除了gles的渲染,而恰恰大部分移动端都是用的gles(安卓平台)所以,第一行要去掉。第二行是针对于ShaderMod,也就是一些shader新特性,可以调低,内置的lit里面是实现了

    2024年02月01日
    浏览(36)
  • Unity入门精要03---透明效果

    本节知识架构      如果采用了透明度混合即要是实现半透明效果,那么就要关闭深度写入,那么此时渲染顺序就会变得非常非常重要,不然会出现不正确的遮挡效果。具体的分析可见书中解释 一句话概括就是因为没有写入深度,会导致之后读取的时候没有读取到深度,就可

    2024年02月04日
    浏览(61)
  • 【Unity Shader】Unity中利用GrabPass实现玻璃效果

    《入门精要》中模拟玻璃是用了Unity里的一个特殊的Pass来实现的,这个Pass就是 GrabPass ,比起上一篇博客实现镜子的方法,这个方法我认为相对复杂,因此在实现之前需要对GrabPass及实现原理做一个更加详细的介绍。 场景物体拜访和贴图完全参考《入门精要》: 以及当前场景

    2024年02月09日
    浏览(46)
  • Unity 设置贴图黑色区域为透明效果

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

    2024年02月15日
    浏览(43)
  • 《Unity 入门精要》第8章 透明效果

    在 Unity 中,我们通常使用两种方法来实现透明效果: 透明度测试(Alpha Test) 和 透明度混合(Alpha Blending) 。 当我们渲染不透明物体时,我们不需要特别考虑渲染顺序的问题,因为有 深度缓冲(depth buffer,也称 z-buffer) 的存在,它的基本思想是:根据深度缓存中的来判断

    2024年02月10日
    浏览(45)
  • Unity用Shader实现边缘光效果

    《自学记录》 1、先创建一个Cube,再创建两个材质球Cube、Unilt 2、再创建一个shader代码UniltShader【Project右键Create-Shader-NewSurfaceShader】把里面原来的代码删除,写入下面的代码 3、把shader UniltShader拖给材质球Unilt 4、把Cube的Mesh Renderer中Materials的Size改为2,然后把材质球Cube、Unilt分

    2024年02月08日
    浏览(44)
  • 【unity shader案例】如何实现一个玻璃效果

      原理:玻璃最大的特点当然就是半透明了,如何在游戏中实现这样的效果呢?我这里总体的思路就是先截取整个场景作为一张纹理,然后把玻璃区域的纹理贴到这个玻璃模型上。 下面看具体实现代码 实现效果如下    更进一步,还可以实现毛玻璃的效果,思路就是用一张

    2024年02月16日
    浏览(48)
  • 【unity shader】水体渲染基础-水下透视效果

    接下来是水体渲染基础的最后一篇,通过水面看到水下的物体,并呈现深度效果。 我们直接搭一个小场景。 增加水面,赋予uv变形的水面材质,并增加透明度的设置。 水体会吸收光线,所以真实的水体并不是完全透明的。此外,水体对不同频率的光吸收率不同,蓝光被吸收

    2024年03月14日
    浏览(52)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包