Search code examples
unity-game-enginecggeometry-shader

CG/Unity: Toy Shader For Duplicating Object


I'm new to writing shaders and I'm working on a practice geometry shader. The goal of the shader is to make the "normal" pass produce transparent pixels such that the object is invisible, while the "geometry" pass will take each triangle, redraw in the same place as the original but colored black. Thus, I expect the output to be the original object, but black. However, my geometry pass seems to not produce any output I can see:

enter image description here

Here is the code I currently have for the shader.

Shader "Outlined/Silhouette2" {
    Properties
    {
        _Color("Color", Color) = (0,0,0,1)
        _MainColor("Main Color", Color) = (1,1,1,1)
        _Thickness("Thickness", float) = 4
        _MainTex("Main Texture", 2D) = "white" {}
    }
    SubShader
    {

        Tags{ "Queue" = "Geometry" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        Cull Back
        ZTest always
        Pass
        {
            Stencil{
                Ref 1
                Comp always
                Pass replace
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct v2g
            {
                float4  pos : SV_POSITION;
                float2  uv : TEXCOORD0;
                float3 viewT : TANGENT;
                float3 normals : NORMAL;
            };

            struct g2f
            {
                float4  pos : SV_POSITION;
                float2  uv : TEXCOORD0;
                float3  viewT : TANGENT;
                float3  normals : NORMAL;
            };

            float4 _LightColor0;
            sampler2D _MainTex;
            float4 _MainColor;

            v2g vert(appdata_base v)
            {
                v2g OUT;
                OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                OUT.uv = v.texcoord;
                OUT.normals = v.normal;
                OUT.viewT = ObjSpaceViewDir(v.vertex);

                return OUT;
            }

            half4 frag(g2f IN) : COLOR
            {
                //this renders nothing, if you want the base mesh and color
                //fill this in with a standard fragment shader calculation

                float4 texColor = tex2D(_MainTex, IN.uv);
                float3 normal = mul(float4(IN.normals, 0.0), _Object2World).xyz;
                float3 normalDirection = normalize(normal);
                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz * -1);
                float3 diffuse = _LightColor0.rgb * _MainColor.rgb * max(0.0, dot(normalDirection, lightDirection));

                texColor = float4(diffuse,1) * texColor;

                //
                //return texColor;
                return float4(0, 0, 0, 0);
            }
            ENDCG
        }

        Pass
        {
            Stencil{
                Ref 0
                Comp equal
            }

            CGPROGRAM
            #include "UnityCG.cginc"
            #pragma target 4.0
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag


            half4 _Color;
            float _Thickness;

            struct v2g
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 local_pos: TEXCOORD1;
                float3 viewT : TANGENT;
                float3 normals : NORMAL;
            };

            struct g2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 viewT : TANGENT;
                float3 normals : NORMAL;
            };

            v2g vert(appdata_base v)
            {
                v2g OUT;
                OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                OUT.local_pos = v.vertex;

                OUT.uv = v.texcoord;
                OUT.normals = v.normal;
                OUT.viewT = ObjSpaceViewDir(v.vertex);

                return OUT;
            }

            [maxvertexcount(12)]
            void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
            {
                g2f OUT;
                OUT.pos = IN[0].pos;
                OUT.uv = IN[0].uv;
                OUT.viewT = IN[0].viewT;
                OUT.normals = IN[0].normals;
                triStream.Append(OUT);

                OUT.pos = IN[1].pos;
                OUT.uv = IN[1].uv;
                OUT.viewT = IN[1].viewT;
                OUT.normals = IN[1].normals;
                triStream.Append(OUT);

                OUT.pos = IN[2].pos;
                OUT.uv = IN[2].uv;
                OUT.viewT = IN[2].viewT;
                OUT.normals = IN[2].normals;
                triStream.Append(OUT);
            }

            half4 frag(g2f IN) : COLOR
            {
                _Color.a = 1;
                return _Color;
            }
            ENDCG

        }
    }
    FallBack "Diffuse"
}

Since all I'm doing is taking the same triangles I've been given and appending them to the triangle stream I'm not sure what I could be doing wrong to cause nothing to appear. Anyone know why this is happening?


Solution

  • I notice that you don't call triStrem.RestartStrip(); after feeding in 3 vertices of a triangle in your geometry shader.

    This informs the stream that a particular triangle strip has ended, and a new triangle strip will begin. If you don't do this, each (single) vertex passed to the stream will add on to the existing triangle strip, using the triangle strip pattern: https://en.wikipedia.org/wiki/Triangle_strip

    I'm fairly new to geo-shaders myself, so I'm not sure if this is your issue or not, I don't THINK the RestartStrip function is called automatically at the end of each geomerty-shader, but have not tested this. Rather I think it gets called automatically only when you you reach the maxvertexcount. For a single triangle, I would set the maxvertexcount to 3, not the 12 you have now. (I also know it can be tough to get ANY shader answers so figgured I'd try to help.)