2

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?

JWWalker
  • 22,385
  • 6
  • 55
  • 76
user3054986
  • 417
  • 7
  • 18

1 Answers1

3

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?

Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • Since I provide only 1 useful vertex as input to the GS and the option TRIANGLE_FAN is interpreted during primitive assembly between VS and GS and not as I assumed after GS, the option is effect less in my case. That would lead to the conclusion that primitive assembly would just construct a fan with a triangle strip and the only benefit would be the data transferred from CPU to GPU. The GS output would be equal, if constructed with TRIANGLE_FAN or TRIANGLE_STRIP, correct? – user3054986 Feb 25 '15 at 21:24
  • @user3054986: Correct. `GL_TRIANGLE_FAN` would be for input primitive type. I would go ahead and order the vertices you _output_ in your geometry shader as a strip and ignore trying to do this as a fan altogether. However, from your description, your input type should actually be `points` (1 vertex emits many triangles) and not `triangles`. – Andon M. Coleman Feb 25 '15 at 21:25
  • I also use transform feedback, so points is not an option. (I put colour and zoom factor in the remaing 2 vertexes.). I also want to point out, besides cache locality, we gain NOT to construct the MVP very every vertex, only once per ellipse. – user3054986 Feb 25 '15 at 21:35