【问题标题】:Shader ZTest not behaving as expected着色器 ZTest 未按预期运行
【发布时间】:2018-06-11 20:29:09
【问题描述】:
Shader "Custom/Selected Object" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Emission ("EmissionColor", Color) = (0.5, 0.5, 0.5, 1)
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        _Mode ("Blend Mode", Range(0,3)) = 0
        _ZWrite ("ZWrite", Int) = 0
        _SrcBlend ("SrcBlend", Int) = 0
        _DstBlend ("DstBlend", Int) = 0

        _Cutoff ("Alpha Cutoff", Float) = 0.05
    }
    SubShader{
        //Behind other geometry
        Pass
        {
            ZTest GEqual
            ZWrite [_ZWrite]
            Blend [_SrcBlend] [_DstBlend]

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 viewDir : TEXCOORD1;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.normal = UnityObjectToWorldNormal(v.normal);
                o.viewDir = normalize(UnityWorldSpaceViewDir(o.pos));

                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f i) : SV_Target
            {
                //Note you have to normalize these again since they are being interpolated between vertices
                float rim = 1 - dot(normalize(i.normal), normalize(i.viewDir));
                fixed4 rimLight = lerp(half4(.95, .95, .95, 1), half4(0.65, 0.65, .95, 1), rim);
                fixed4 t = tex2D(_MainTex, i.uv);
                clip(t.a < 0.2);
                return t * rimLight;
            }

            ENDCG
        }

        ZTest Less
        ZWrite On
        Blend [_SrcBlend][_DstBlend]

        //Front geometry
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        // And generate the shadow pass with instancing support
        #pragma surface surf Standard fullforwardshadows addshadow alphatest:_Cutoff //alpha:blend //keepalpha
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        #pragma multi_compile __ EMISSIVE_ON

        sampler2D _MainTex;
        fixed4 _Emission;
        fixed4 _Color;
        //float _Mode;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;

        void surf(Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            #if EMISSIVE_ON //Glowing
                o.Emission = _Emission;
            #endif
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }

        ENDCG
    }
    FallBack "Instanced/InstancedSurfaceShader"
}

此着色器应用于用户选择的对象。它添加了一个通道来绘制可能被遮挡的对象部分,例如 X 射线效果。着色器在运行时被交换,如果起始着色器是 Unity 标准着色器,它应该可以正常工作。
问题:我正在制作一个着色器来代替标准着色器为了启用 GPU 实例化。从实例化着色器交换时,只有在对象完全通过其遮挡物时才会绘制效果。


ZTest GEqual 似乎失败了,除非对象完全超过了遮挡对象。在上面的图像中,在第二个上看到的效果也应该在对象被遮挡的第一个上看到。

它与标准着色器配合良好的事实让我相信问题是我在实例化着色器中遗漏的问题,这里:

Shader "Custom/Instanced/InstancedSurfaceShader - Glow" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Emission ("EmissionColor", Color) = (1, 1, 1, 1)
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        _Mode ("Blend Mode", Range(0,3)) = 0
        _Cutoff("Alpha Cutoff", Float) = 0.05
    }
    SubShader {
        ZTest Less
        ZWrite On

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        // And generate the shadow pass with instancing support
        #pragma surface surf Standard fullforwardshadows addshadow alphatest:_Cutoff

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        // Enable instancing for this shader
        #pragma multi_compile_instancing

        // Config maxcount. See manual page.
        // #pragma instancing_options

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        fixed4 _Emission;
        half _Glossiness;
        half _Metallic;

        // Declare instanced properties inside a cbuffer.
        // Each instanced property is an array of by default 500(D3D)/128(GL) elements. Since D3D and GL imposes a certain limitation
        // of 64KB and 16KB respectively on the size of a cubffer, the default array size thus allows two matrix arrays in one cbuffer.
        // Use maxcount option on #pragma instancing_options directive to specify array size other than default (divided by 4 when used
        // for GL).
        // https://docs.unity3d.com/Manual/GPUInstancing.html
        UNITY_INSTANCING_BUFFER_START(Props)
            UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color) // Make _Color an instanced property (i.e. an array)
#define _Color_arr Props
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(_Color_arr, _Color);
            o.Albedo = c.rgb;
            o.Emission = _Emission;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Instanced/InstancedSurfaceShader"
}  

这里是在shader切换时直接影响材质/shader的代码sn-p:

material.SetOverrideTag("RenderType", "");
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_ZWrite", 1);
material.DisableKeyword("_ALPHATEST_ON");
material.DisableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = -1;  

实例着色器中的什么可能导致 X 射线着色器的效果不同?

【问题讨论】:

    标签: unity3d shader


    【解决方案1】:

    在这里回答实际问题之前,我需要指出这一点:

    如果多通道着色器有两个以上通道,则只能实例化第一个通道。这是因为 Unity 会强制为每个对象一起渲染后面的通道,从而强制更改材质。

    https://docs.unity3d.com/Manual/GPUInstancing.html

    因此,从本质上讲,在您当前使用 Passes 实现的情况下,无论如何您都无法使用 GPU 实例化

    现在,着色器的问题是所有通道都是不透明的,因此无法保证网格的绘制顺序。如果在被遮挡物之后渲染遮挡物,则 Zbuffer 中将没有任何信息可供测试。解决方案只是增加材质的渲染顺序,因为它会在所有其他不透明 (ZWrite On) 像素之后立即绘制。

    【讨论】:

    • 首先:将 X 射线着色器的队列设置为“几何+1”确实解决了 z 距离问题。感谢您的建议和解释!其次,澄清一下:实例化着色器只有一个通道,因此 GPU 实例化在那里可以正常工作。我的描述可能不清楚。替代的 X 射线效果着色器是 2-pass 着色器,一次可打开/关闭单个对象,无需实例化。
    • 如果您确实没有很多选定的对象,那就太好了 :) 干杯
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-19
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-28
    • 2021-04-08
    相关资源
    最近更新 更多