7
\$\begingroup\$

I am playing with a geometry shader and I am wondering why it doesn't generate geometry for each vertex in all meshes?

  • I don't think this is a Cull problem because the same vertices generate geometry as you rotate around and setting Cull Off changes nothing.
  • Another problem could be a multiply by zero somewhere but I tried to eliminate as many variables as possible and still have the issue.
  • A third unlikely issue could be limiting the output amount somewhere. I didn't set anything explicitly to hinder that.

I notice the problem on a Cube(5/8) and Quad(2/4) but seems to generate all the geometry fine on a plane, cylinder, etc. The following are some results from using the bare-bones geometry shader.

Cube (closest: Unity default, further back: Cinema 4D exported .fbx cube):

Quad:

It seems to work fine on a plane (update: It is actually missing a whole row on each axis)

Unity Shader Code GeometryShaderTest1.shader:

Note: If you are testing out the shader and nothing is displaying; Rotate your camera around so the normals are facing the right direction to show up.

Shader "Custom/GeometryShaderTest1" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _Amount ("Height Adjustment", Float) = 0.0
    }
    SubShader {
        Pass {
            Tags { "RenderType"="Opaque" }
            LOD 200

            CGPROGRAM
            #pragma target 5.0
            //#pragma addshadow
            #pragma vertex vert
            #pragma geometry GS_Main
            #pragma fragment frag
            #include "UnityCG.cginc"

            // Vert to geo
            struct v2g
            {
                float4 pos: POSITION;
            };

            // geo to frag
            struct g2f
            {
                float4 pos: POSITION;
            };


            // Vars
            fixed4 _Color;
            float _Amount;


            // Vertex modifier function
            v2g vert(appdata_base v) {

                v2g output = (v2g)0;
                output.pos = mul(_Object2World, v.vertex);
                // Just testing whether all vertices affected
                output.pos.y -= _Amount;

                return output;
            }

            // GS_Main(point v2g p[1], inout TriangleStream<g2f> triStream)
            // GS_Main(line v2g p[2], inout TriangleStream<g2f> triStream)
            // GS_Main(triangle v2g p[3], inout TriangleStream<g2f> triStream)
            [maxvertexcount(4)]
            void GS_Main(point v2g p[1], inout TriangleStream<g2f> triStream)
            {
                float4 v[4];

                float3 asdfUp = float3(0, 1, 0);
                float3 asdfRight = float3(0, 0, 1);
                v[0] = float4(p[0].pos + 0.25 * asdfRight - 0.25 * asdfUp, 1.0f);
                v[1] = float4(p[0].pos + 0.25 * asdfRight + 0.25 * asdfUp, 1.0f);
                v[2] = float4(p[0].pos - 0.25 * asdfRight - 0.25 * asdfUp, 1.0f);
                v[3] = float4(p[0].pos - 0.25 * asdfRight + 0.25 * asdfUp, 1.0f);

                float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object);
                g2f pIn;
                pIn.pos = mul(vp, v[0]);
                triStream.Append(pIn);

                pIn.pos = mul(vp, v[1]);
                triStream.Append(pIn);

                pIn.pos = mul(vp, v[2]);
                triStream.Append(pIn);

                pIn.pos = mul(vp, v[3]);
                triStream.Append(pIn);
            }

            fixed4 frag(g2f input) : COLOR {
                return float4(1.0, 0.0, 0.0, 1.0); 
            }

            ENDCG
        }


    }

    FallBack "Diffuse"
}

Geometry shader resources:

\$\endgroup\$
2
\$\begingroup\$

From the OpenGL wiki:

The input_primitive​ type must match the primitive type used with the drawing command that renders with this shader program.

Given, Unity is probably using DirectX on Windows, but the same thing should hold true. Because your shader is asking for a point, it is only taking the first point from each primitive and ignoring the rest.

Assuming Unity draws with triangles, you need to change the first parameter to receive that, set maxvertexcount to 12, and generate three quads instead of one. Quick example:

[maxvertexcount(12)]
void GS_Main(triangle v2g p[3], inout TriangleStream<g2f> triStream)
{
    for(int i = 0; i < 3; i++)
    {
        // generate a quad with p[i]

        triStream.RestartStrip();
    }
}
\$\endgroup\$
5
\$\begingroup\$

The input primitive, aka. the mesh fed into the shader, has a MeshTopology setting which can be adjusted in code. The accepted answer works because the default input primitive for a mesh(including imported meshes) is triangle/MeshTopology.Triangles.

But if you use MeshTopology.Triangles, you will be creating more geometry than you need for the shader. Because a single vertex may be part of multiple triangles, you will have billboards on top of other billboards. You won't notice it visually, because they overlap perfectly, but it will cost you performance. The fragment shader will be working on duplicate geometry for no reason.

Instead, you should use point/MeshTopology.Points as your input primitive, as your original shader already does, and then tell Unity to draw with points rather than triangles.

You can use Mesh.SetIndices to adjust the mesh's topology and then your original shader will work just fine.

using UnityEngine;

public class UsePointTopologyMeshMode : MonoBehaviour {

    [ContextMenu("ConvertToPointTopology")]
    void ConvertToPointTopology() {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        int[] indices = new int[mesh.vertices.Length];
        for (int i = 0; i < indices.Length; i++) {
            indices[i] = i;
        }
        mesh.SetIndices(indices, MeshTopology.Points, 0);
    }

    // Use this for initialization
    void Start () {
        ConvertToPointTopology();
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ The question is about Geometry shaders, not constructing meshes. \$\endgroup\$ – MLM Sep 6 '15 at 23:00
  • \$\begingroup\$ My answer is about changing the drawing command which influences the geometry shader, not constructing meshes. \$\endgroup\$ – Croolsby Sep 6 '15 at 23:45
  • \$\begingroup\$ I am sorry, I misunderstood your answer completely. This is an awesome detail on how to change the input primitive and how this relationship works. I made an edit to the answer that clears up the language for me and I hope future onlookers. \$\endgroup\$ – MLM Sep 7 '15 at 2:37
  • \$\begingroup\$ No worries. I didn't realize it wasn't clear enough originally. Thanks for the edit. \$\endgroup\$ – Croolsby Sep 7 '15 at 2:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.