Blend+ZWrite Off在某些情况下也有问题:半透模型自身遮挡关系渲染错乱

左侧是错误的,右侧是我们想要的

 

分析原因

因为关闭了深度写入,所有顶点都会被渲染,在重叠的地方就发生混合(颜色较深的区域)。

 

深度写入打开,会是什么效果?

和上面差不多,只是深色区域稍微变小了点。变小的就是没通过深度测试被丢弃的像素,有些区域视觉上明显是前面挡住后面了还是存在混合是因为内部顶点顺序存在跳跃造成的(就是离相机远的顶点先绘制了)

 

解决方法

深度写入得开启,但深度写入不是渲染一个顶点写入一下,而是先把物体当作一个整体把他所有顶点的深度先写入,然后再渲染颜色。

在shader中的体现就是多一个Pass只写深度,不写出颜色。

Shader "My/Blend/Diffuse_Blend_ZWriteOn" {
    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 {
                ZWrite On
                ColorMask 0
            }

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

                ZWrite On
                Blend SrcAlpha OneMinusSrcAlpha

                CGPROGRAM

                #pragma vertex vert
                #pragma fragment frag

                #include "Lighting.cginc"

                fixed4 _Color;
                sampler2D _MainTex;
                float4 _MainTex_ST;
                fixed _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"
}

 

一些备忘

深度测试就是到相机的距离与z-buffer比较,如果小与则保留,否则丢弃。

 

不透明的物体先绘制,深度测试+深度写入。

半透明物体绘制,只有深度测试,所有半透明物体只要在不透明物体前面,全都会被绘制,半透明之间相互叠加。

 

1.RenderQueue 2500以下(不透明物体)

a.RenderQueue 越小越优先
b.RenderQueue 相等,由近到远排序优先,直接绘制近的,远的被遮挡的直接丢弃

2.RenderQueue 2500以上(半透明物体)
a.RenderQueue 越小越优先
b.RenderQueue 相等,由远到近排序优先,一层一层绘制,层与层之间会混合
 
 

参考

《shader入门精要》 8.5节
posted @ 2023-03-04 23:27  yanghui01  阅读(168)  评论(0编辑  收藏  举报