Why my shader doesn't render the subshader wit

2019-08-23 14:38发布

问题:

I found a shader and it's a Wireframe shader with no diagonals (see ref. here). I'd like to fill is with color so I added a subshader that will render the color (taking from here).

Both shaders work great independently, however when I try to combine them the color from the second shader is not rendering. What can the problem be?

This is the code I have:

Shader "Custom/TransparentSingleColorShader" {
    Properties{
      _Color("Color", Color) = (1.0, 1.0, 1.0, 1.0)
      [PowerSlider(3.0)]
        _WireframeVal("Wireframe width", Range(0., 0.5)) = 0.05
        _FrontColor("Front color", color) = (1., 1., 1., 1.)
        _BackColor("Back color", color) = (1., 1., 1., 1.)
        [Toggle] _RemoveDiag("Remove diagonals?", Float) = 0.
    }
        SubShader
        {
            Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }

            Pass
            {
                Cull Front
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma geometry geom

            // Change "shader_feature" with "pragma_compile" if you want set this keyword from c# code
            #pragma shader_feature __ _REMOVEDIAG_ON

            #include "UnityCG.cginc"

            struct v2g {
                float4 worldPos : SV_POSITION;
            };

            struct g2f {
                float4 pos : SV_POSITION;
                float3 bary : TEXCOORD0;
            };

            v2g vert(appdata_base v) {
                v2g o;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

            [maxvertexcount(3)]
            void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
                float3 param = float3(0., 0., 0.);

                #if _REMOVEDIAG_ON
                float EdgeA = length(IN[0].worldPos - IN[1].worldPos);
                float EdgeB = length(IN[1].worldPos - IN[2].worldPos);
                float EdgeC = length(IN[2].worldPos - IN[0].worldPos);

                if (EdgeA > EdgeB && EdgeA > EdgeC)
                    param.y = 1.;
                else if (EdgeB > EdgeC && EdgeB > EdgeA)
                    param.x = 1.;
                else
                    param.z = 1.;
                #endif

                g2f o;
                o.pos = mul(UNITY_MATRIX_VP, IN[0].worldPos);
                o.bary = float3(1., 0., 0.) + param;
                triStream.Append(o);
                o.pos = mul(UNITY_MATRIX_VP, IN[1].worldPos);
                o.bary = float3(0., 0., 1.) + param;
                triStream.Append(o);
                o.pos = mul(UNITY_MATRIX_VP, IN[2].worldPos);
                o.bary = float3(0., 1., 0.) + param;
                triStream.Append(o);
            }

            float _WireframeVal;
            fixed4 _BackColor;

            fixed4 frag(g2f i) : SV_Target {
            if (!any(bool3(i.bary.x < _WireframeVal, i.bary.y < _WireframeVal, i.bary.z < _WireframeVal)))
                 discard;

                return _BackColor;
            }

            ENDCG
        }

        Pass
        {
            Cull Back
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma geometry geom

                // Change "shader_feature" with "pragma_compile" if you want set this keyword from c# code
                #pragma shader_feature __ _REMOVEDIAG_ON

                #include "UnityCG.cginc"

                struct v2g {
                    float4 worldPos : SV_POSITION;
                };

                struct g2f {
                    float4 pos : SV_POSITION;
                    float3 bary : TEXCOORD0;
                };

                v2g vert(appdata_base v) {
                    v2g o;
                    o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                    return o;
                }

                [maxvertexcount(3)]
                void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
                    float3 param = float3(0., 0., 0.);

                    #if _REMOVEDIAG_ON
                    float EdgeA = length(IN[0].worldPos - IN[1].worldPos);
                    float EdgeB = length(IN[1].worldPos - IN[2].worldPos);
                    float EdgeC = length(IN[2].worldPos - IN[0].worldPos);

                    if (EdgeA > EdgeB && EdgeA > EdgeC)
                        param.y = 1.;
                    else if (EdgeB > EdgeC && EdgeB > EdgeA)
                        param.x = 1.;
                    else
                        param.z = 1.;
                    #endif

                    g2f o;
                    o.pos = mul(UNITY_MATRIX_VP, IN[0].worldPos);
                    o.bary = float3(1., 0., 0.) + param;
                    triStream.Append(o);
                    o.pos = mul(UNITY_MATRIX_VP, IN[1].worldPos);
                    o.bary = float3(0., 0., 1.) + param;
                    triStream.Append(o);
                    o.pos = mul(UNITY_MATRIX_VP, IN[2].worldPos);
                    o.bary = float3(0., 1., 0.) + param;
                    triStream.Append(o);
                }

                float _WireframeVal;
                fixed4 _FrontColor;

                fixed4 frag(g2f i) : SV_Target {
                if (!any(bool3(i.bary.x <= _WireframeVal, i.bary.y <= _WireframeVal, i.bary.z <= _WireframeVal)))
                     discard;

                    return _FrontColor;
                }

                ENDCG
            }
        }
        SubShader{
          Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
          Blend SrcAlpha OneMinusSrcAlpha
          Cull Front
          LOD 200

          CGPROGRAM
          #pragma surface surf Lambert

          fixed4 _Color;

    // Note: pointless texture coordinate. I couldn't get Unity (or Cg)
    //       to accept an empty Input structure or omit the inputs.
    struct Input {
      float2 uv_MainTex;
    };

    void surf(Input IN, inout SurfaceOutput o) {
      o.Albedo = _Color.rgb;
      o.Emission = _Color.rgb; // * _Color.a;
      o.Alpha = _Color.a;
    }
    ENDCG
    }
        FallBack "Diffuse"
}

This is the photo: one the right (how I want it to look but without the diagonal) on the left (how I want it but color is missing)

回答1:

Subshaders don't do what you think they do. Subshaders are used to provide different compability levels to the same shader. Unity will always select the first compatiable subshader - so for instance if the first one uses tesselation, and your hardware doesn't support that, Unity will look further down the list until it finds one which works with your machine.

What you want is a second render pass. Surface shaders generate their own passes, so you just paste the code to the end of the first shader, after the last pass.