Geometry Shader generiert für einige Scheitelpunkte keine Geometrie

7

Ich spiele mit einem Geometrie-Shader und frage mich, warum er nicht für jeden Scheitelpunkt in allen Netzen Geometrie generiert.

  • Ich denke nicht, dass dies ein Cull-Problem ist, da dieselben Scheitelpunkte Geometrie erzeugen, wenn Sie sich drehen und die Einstellung Cull Offnichts ändert.
  • Ein anderes Problem könnte eine Multiplikation mit Null sein, aber ich habe versucht, so viele Variablen wie möglich zu eliminieren und habe immer noch das Problem.
  • Ein drittes unwahrscheinliches Problem könnte darin bestehen, die Ausgabemenge irgendwo zu begrenzen. Ich habe nichts explizit festgelegt, um dies zu verhindern.

Ich bemerke das Problem bei einem Cube (5/8) und Quad (2/4), aber es scheint, als würde die gesamte Geometrie in einer Ebene, einem Zylinder usw. fein erzeugt. Im Folgenden sind einige Ergebnisse aus der Verwendung des Bare-Bones-Geometrie-Shaders aufgeführt.

Cube (am nächsten: Unity-Standard, weiter hinten: Cinema 4D exportierter .fbxCube) :

Quad:

Es scheint in einer Ebene gut zu funktionieren (Update: Es fehlt tatsächlich eine ganze Zeile auf jeder Achse)

Unity Shader Code GeometryShaderTest1.shader:

Hinweis: Wenn Sie den Shader testen und nichts angezeigt wird; Drehen Sie Ihre Kamera so, dass die Normalen in die richtige Richtung zeigen.

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"
}

Geometrie-Shader-Ressourcen:

MLM
quelle

Antworten:

2

Aus dem OpenGL-Wiki :

Der Typ input_primitive muss mit dem primitiven Typ übereinstimmen, der mit dem Zeichnungsbefehl verwendet wird, der mit diesem Shader-Programm gerendert wird .

Natürlich verwendet Unity DirectX unter Windows, aber das Gleiche sollte gelten. Da Ihr Shader nach einem Punkt fragt, nimmt er nur den ersten Punkt von jedem Grundelement und ignoriert den Rest.

Angenommen, Unity zeichnet mit Dreiecken, müssen Sie den ersten Parameter ändern, um diesen zu erhalten, maxvertexcountauf 12 setzen und drei Quads anstelle von einem generieren. Kurzes Beispiel:

[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();
    }
}
Eisiger Trotz
quelle
5

Das Eingabeprimitiv, auch bekannt als. Das in den Shader eingespeiste Netz hat eine MeshTopologyEinstellung, die im Code angepasst werden kann. Die akzeptierte Antwort funktioniert, da das Standardeingabeprimitiv für ein Netz (einschließlich importierter Netze) triangle/ istMeshTopology.Triangles .

Wenn Sie jedoch verwenden MeshTopology.Triangles, erstellen Sie mehr Geometrie, als Sie für den Shader benötigen. Da ein einzelner Scheitelpunkt Teil mehrerer Dreiecke sein kann, haben Sie Werbetafeln über anderen Werbetafeln. Sie werden es visuell nicht bemerken, da sie sich perfekt überlappen, aber es kostet Sie Leistung. Der Fragment-Shader arbeitet ohne Grund an doppelter Geometrie.

Stattdessen sollten Sie point/ MeshTopology.Pointsals Eingabeprimitiv verwenden, wie es Ihr ursprünglicher Shader bereits tut, und dann Unity anweisen, mit Punkten anstatt mit Dreiecken zu zeichnen.

Sie können Mesh.SetIndicesdie Topologie des Netzes anpassen, und dann funktioniert Ihr ursprünglicher Shader einwandfrei.

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();
    }
}
Croolsby
quelle
1
Die Frage betrifft Geometrie-Shader, die keine Netze erstellen.
MLM
Meine Antwort besteht darin, den Zeichenbefehl zu ändern, der den Geometrie-Shader beeinflusst, und keine Netze zu erstellen.
Croolsby
Es tut mir leid, ich habe Ihre Antwort völlig falsch verstanden. Dies ist ein großartiges Detail darüber, wie das Eingabeprimitiv geändert wird und wie diese Beziehung funktioniert. Ich habe die Antwort bearbeitet, die die Sprache für mich klarer macht, und ich hoffe, dass die Zukunft zuschaut.
MLM
Keine Sorge. Ich wusste nicht, dass es ursprünglich nicht klar genug war. Danke für die Bearbeitung.
Croolsby