Shader ZTest表现不如预期

时间:2018-06-07 20:43:09

标签: unity3d shader

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 Standard着色器,则可以正常工作 问题:我正在使用着色器代替标准着色器以启用GPU实例化。从实例化着色器交换时,只有在对象完全通过其封堵器后才会绘制效果 ZTest failing

ZTest working
除非对象完全超过遮挡对象,否则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"
}  

这是在着色器切换时直接影响材质/着色器的代码段:

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射线着色器的效果不同?

1 个答案:

答案 0 :(得分:1)

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

  

如果多次传递着色器的传球次数超过两次,则只能对第一次传球进行实例化。这是因为Unity强制后续传递为每个对象一起渲染,强制更改材质。

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

所以从本质上讲,使用Passes的当前实现,无论如何都无法使用GPU实例

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