user3054986
user3054986

Reputation: 437

How to draw TRIANGLE_FAN with geometry shader created coordinates? (GLSL 3.3)

I want to draw multiple fans with a GS. Each fan should billboard to the camera at each time, which makes it necessary that each vertex is multiplied with MVP matrix. Since each fan is movable by the user, I came up with the idea to feed the GS with the position.

The following geometry shader works as expected with points as in and output:

uniform mat4 VP;
uniform mat4 sharedModelMatrix;

const int STATE_VERTEX_NUMBER = 38;

layout (shared) uniform stateShapeData {
    vec2 data[STATE_VERTEX_NUMBER];
};

layout (triangles) in;
layout (triangle_strip, max_vertices = 80) out;

void main(void)
{
    int i;
    mat4 modelMatrix = sharedModelMatrix;
    modelMatrix[3]   = gl_in[0].gl_Position;
    mat4 MVP = VP * modelMatrix;

    gl_Position = MVP * vec4( 0, 0 , 0, 1 );
    EmitVertex(); // epicenter

    for (i = 37; i >= 0; i--) {
        gl_Position = MVP * vec4( data[i], 0, 1 );
        EmitVertex();
     }

     gl_Position = MVP * vec4( data[0], 0, 1 );
     EmitVertex();
}

I tried to run this with glDrawElements, glDrawArrays and glMultiDrawArrays. None of these commands draws the full fan. Each draws the first triangle filled and the remaining vertices as points.

So, the bottom question is: Is it possible to draw a fan with GS created vertices and how?

Upvotes: 2

Views: 3128

Answers (1)

Andon M. Coleman
Andon M. Coleman

Reputation: 43319

Outputting fans in a Geometry Shader is very unnatural as you have discovered.

You are currently outputting the vertices in fan-order, which is a construct that is completely foreign to GPUs after primitive assembly. Fans are useful as assembler input, but as far as output is concerned the rasterizer only understands the concept of strips.

To write this shader properly, you need to decompose this fan into a series of individual triangles. That means the loop you wrote is actually going to output the epicenter on each iteration.

void main(void)
{
    int i;
    mat4 modelMatrix = sharedModelMatrix;
    modelMatrix[3]   = gl_in[0].gl_Position;
    mat4 MVP = VP * modelMatrix;

    for (i = 37; i >= 0; i--) {
        gl_Position = MVP * vec4( 0, 0 , 0, 1 );
        EmitVertex(); // epicenter

        gl_Position = MVP * vec4( data[i], 0, 1 );
        EmitVertex();

        gl_Position = MVP * vec4( data[i-1], 0, 1 );
        EmitVertex();

        // Fan and strip DNA just won't splice
        EndPrimitive ();
     }
}

You cannot exploit strip-ordering when drawing this way; you wind up having to end the output primitive (strip) multiple times. About the only possible benefit you get to drawing in fan-order is cache locality within the loop. If you understand that geometry shaders are expected to output triangle strips, why not order your input vertices that way to begin with?

Upvotes: 3

Related Questions