Shader8-透明效果

Z缓冲区(Z-Buffer)算法

当要渲染一个片元时候,需要把它的深度值和已经存在于z缓冲器中的深度值进行比较(如果开启了深度测试),

如果该片元值较小,该片元的z值应该覆盖掉对应单元颜色缓冲区中的颜色值。并将该片元深度值写入z缓冲器中。

如果该片元深度值和z缓冲器中对应单元已有的值比较,值较大。表明有物体遮住了它,该片元不被渲染。

 

参考:https://www.cnblogs.com/cnblog-wuran/p/9830994.html 


透明度操作-测试&混合

  • 透明度测试    一个片元透明度不满足某个条件(通常是小于某个设置的阈值),对应的片元会被舍弃。不会对颜色缓冲区产生任何影响。否则就按照普通不透明片元来处理。

深度测试不需要关闭深度写入。

  • 透明度混合    使用当前透明度作为混合因子,与已经存在颜色缓冲区中的颜色值进行混合,得到新的颜色。

深度混合需要关闭深度写入。不需要关闭深度测试。如果开启深度写入,靠近摄像机有个半透明物体,此物体深度写入深度缓冲区,再更远距离后边有个模型由于深度大于半透物体,被认为是被半透物体遮罩。不渲染,透过半透物体,看不到远距离物体。。。不合理。。。

因此,关闭深度写入才可能得到正确渲染结果。 但是关闭深度写入,需要设置正确的渲染顺序,才能得到正确的渲染结果。

UnityShader 的渲染顺序

为了解决渲染顺序,Unity为我们提供了渲染队列(render queue),我们可以用SubShader的Queue标签决定模型归于哪个渲染队列。索引号越小越早渲染。

名称索引号描述
Background1000最先渲染,通常该队列用来渲染需要绘制在背景上的物体
Geometry2000默认渲染队列,大多数物体都是用这个队列,不透明物体使用这个队列
AlphaTest2450需要透明度测试的物体使用,Unity5之后把它从Geometry队列中单独分出来,这是因为在所有不透明物体渲染之后再渲染他们会更高效
Transparent3000从后往前渲染,任何使用了透明度混合(关闭了深度写入的Shader)的物体,都应该使用该队列。
Overlay4000实现叠加效果,需要在最后渲染的物体使用该队列

透明度测试(AlphaTest)

Shader "Chan/Chapter8_AlphaTest" {
    Properties
    {
        _Color("Main Tint",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}
        _CutOff("Alpha Cutoff",Range(0,1)) = 0.5
    }
    SubShader
    {
        //Queue = 定义该SubShader的渲染队列  IgnoreProject = 不受投影影响   RenderType = 提前定义组???
        Tags{"Queue" = "AlphaTest" "IgnoreProject" = "True" "RenderType" = "TransparentCutout"}
        Pass
        {
            Tags{"LightMode" = "Forwardbase"}

            CGPROGRAM

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

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

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

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

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

            fixed4 frag(v2f i):SV_Target
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                fixed4 texColor = tex2D(_MainTex,i.uv);

                /*
                if(texColor.a - _Cutoff) 为负
                {
                    discard;//丢弃片元
                }
                */
                //同上 纹理A通道值小于_Cutoff,该片元被丢弃
                clip(texColor.a - _CutOff);

                fixed3 albedo = texColor.rgb * _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 "Transparent/Cutout/VertexLit"
}

Clip()为负,该片元被丢弃。纹理A通道值小于_Cutoff,该片元被丢弃。否则按普通纹理,采样显示。

透明度混合(AlphaBlend)

透明度混合需要关闭深度写入。为了混合,需要Unity提供的混合模式的命令。设置了混合因子,就默认开启了混合模式

ShaderLab Blend命令
语义描述
Blend Off关闭混合
Blend On开启混合
Blend 混合因子A 混合因子B 开启混合,并设置混合因子,具体混合操作看混合因子定义
BlendOp BlendOperation开启混合,使用BlendOperation已定义好的其他混合操作

关闭深度吸入的透明度混合效果 :

Shader "Chan/Chapter8_AlphaBlend" {
    Properties
    {
        _Color("Color Tint",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}
        _AlphaScale("Alpha Scale",Range(0,1.0)) = 1.0
    }
    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;
            float4 _MainTex_ST;
            float _AlphaScale;

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

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

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

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

                fixed4 texColor = tex2D(_MainTex,i.uv);
                fixed3 albedo = texColor.rgb * _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 "Transparent/VertexLit"
}

关闭深度写入后,模型自身有遮挡的情况就会出现右图情况。。。解决问题如下:

开启深度吸入的透明度混合效果 :

Shader "Chan/Chapter8_AlphaBlendZWrite" {
    Properties
    {
        _Color("Color Tint",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}
        _AlphaScale("Alpha Scale",Range(0,1.0)) = 1.0
    }
    SubShader
    {
        //透明度操作必有的三个标签
        Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}

        //多了一个Pass
        Pass
        {
            ZWrite On //开启深度写入
            ColorMask 0; //不写入颜色通道,只写入深度缓冲器中写入缓存
        }

        Pass
        {
            Tags{"LightMode" = "Forwardbase"}
            Zwrite Off //关闭深度写入
            Blend SrcAlpha OneMinusSrcAlpha //设置混合因子

            CGPROGRAM

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

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

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

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

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

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

                fixed4 texColor = tex2D(_MainTex,i.uv);
                fixed3 albedo = texColor.rgb * _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 "Transparent/VertexLit"
}

对了一个Pass通道,1.开启深度写入,2.帧缓冲器中不写入颜色,只在深度缓冲器中写入深度值。

ColorMask 设置那些颜色通道可以写入帧缓存中。可以是RGBA四个通道中的任意组合,或者是0。例如:

ColorMask 0  不写入任何颜色

ColorMask RGA 写入颜色通道中的RGA通道。

开启深度写入效果如下:


透明度混合中的混合因子

混合操作时候:

  • 有两个操作对象,源颜色S(片元着色器产生颜色)和目标颜色D(颜色缓冲区中读取的颜色),混合后得到输出颜色O(写入颜色缓冲区)。
  • 混合时候,RGB通道和A通道时分开混合的,因此需要设置两个混合因子,如果只设置一个,A通道默认复用RGB通道混合因子
  • 混合因子,默认是相加操作。使用其他操作,设置BlendOp BlendOperation

参数描述
One因子为1
Zero因子为0
SrcColor因子为源颜色值,当用作RGB通道混合时,使用SrcColor(源颜色值)的RGB分量作为混合因子。当用于混合A通道时候,使用SrcColor(源颜色值)的A分量作为混合因子。
SrcAlpha因子为源颜色的透明度值(A通道)
DstColor因子为源颜色值,当用作RGB通道混合时,使用DstColor(目标颜色值)的RGB分量作为混合因子。当用于混合A通道时候,使用DstColor(目标颜色值)的A分量作为混合因子。
DstAlpha因子为目标颜色的透明度值(A通道)
OneMinusSrcColor因子为(1-源颜色)。当用于混合RGB通道时候,使用结果的RGB分量作为混合因子。当用于混合A通道时候,使用结果的A分量作为混合因子。
OneMinusSrcAlpha因子为(1-源颜色的透明度值)
OneMinusDstColor因子为(1-目标颜色)。当用于混合RGB通道时候,使用结果的RGB分量作为混合因子。当用于混合A通道时候,使用结果的A分量作为混合因子。
OneMinusDstAlpha因子为(1-目标颜色的透明度值)

透明度混合中的混合操作

BlendOp BlendOperation混合操作命令
操作描述
Add

将混合后的源颜色和目的颜色相加,默认的操作。

O_{rgb} = SrcFactor x S_{rgb} + DstFactor x D_{rgb}

O_{a} = SrcFactorA x S_{a} + DstFactorA x D_{a}

Sub

用混合后的源颜色减去混合后的目的颜色。

O_{rgb} = SrcFactor x S_{rgb} - DstFactor x D_{rgb}

O_{a} = SrcFactorA x S_{a} - DstFactorA x D_{a}

RevSub

混合后的目的颜色减去混合后的源颜色

O_{rgb} = DstFactor x D_{rgb} - SrcFactor x S_{rgb}

O_{a} = DstFactorA x D_{a} - SrcFactorA x S_{a} 

Min

使用源颜色和目的颜色中比较小的值,是逐分量比较的。

O_{rgba} = (min(S_{r},D_{r}),min(S_{g},D_{g}),min(S_{b},D_{b}),min(S_{a},D_{a}))

MaxO_{rgba} = (max(S_{r},D_{r}), max(S_{g},D_{g}), max(S_{b},D_{b}), max(S_{a},D_{a}))
其他逻辑操作仅在DirectX 11.1中支持

常用混合类型

混合设置混合效果
Blend SrcAlpha OneMinusSrcAlpha透明度混合
Blend OneMinusDstColor One柔和相加
Blend DstColor Zero正片叠底,即相乘
Blend DstColor SrcColor两倍相乘
BlendOp Min/Blend One One变暗
BlendOp Max/Blend One One变亮

Blend OneMinusDstColor One

Blend One OneMinusDstColor

滤色
Blend One One线性减淡

双面渲染的透明效果

透明物体,能看到物体内部及背面形状。因为渲染引擎默认剔除背对摄像机的图元。可通过Cull设置正反面剔除状态。

Cull Back/Front Off/On =  剔除 背面/正面 开启/关闭

双面透明度测试效果:

在AlphaTest Shader中添加  Cull Off不剔除背面即可。

双面透明度混合效果:

透明度混合关闭了深度写入,因此要控制物体正面背面渲染顺序。因此第一个Pass渲染背面,第二个Pass渲染正面。

相比AlphaBlend Shader中,添加一个跟原来一样的Pass。

然后第一个Pass 剔除正面-Cull Front 渲染背面,第二个Pass剔除背面-Cull Back渲染正面。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值