Search code examples
openglglsltessellation

GLSL tessellation control shader indexing gl_TessLevelOuter with gl_InvocationID


Why does the following tessellation control shader makes most triangles disappear?

#version 410

layout(vertices = 3) out;

void main(void) {
    gl_TessLevelInner[0]=1;
    gl_TessLevelOuter[gl_InvocationID]=gl_InvocationID+1;
    gl_TessLevelOuter[gl_InvocationID]=gl_InvocationID+1;
    gl_TessLevelOuter[gl_InvocationID]=gl_InvocationID+1;
}

I input triangles. When I index gl_TessLevelOuter with 0,1 and 2, everything works fine. It seems to me that this construct saves me the if statement, which I believe helps in parallel execution of the shader. Of course, I omitted the vertex calculations in the snippet.


Solution

  • Triangles disappear because you incurred in "undefined" behaviour, that's it. The Inner tesellation level should be at least 2.

    gl_TessLevelInner[0] = 2;
    

    The weird results you obtain are because of how baricentric coordinates are computed when inner level is 1. Given you provide at least 2 as value, you can use any value you want for Outerlevels.

    The following is the edge association for a triangle

    out0 => edge 1-2
    out1 => edge 2-3
    out2 => edge 3-1
    

    Tessellated triangle "fan"

    Out[0] = 4
    Out[1] = 1
    Out[2] = 2
    

    As you see, the Inner of 2, cutted the triangle twice along triangle's bisectors, while the 3 different Outer levels cutted the triangle along the edges (no cuts for 1, twice for 2 and 4 pieces when value is 4)

    Tips:

    Also there are some small advices, data in tessellation control is shared, that's means it is called multiple times, you need to set tessellation control only once:

    if(gl_InvocationID==0){
        gl_TessLevelInner[0] = 2; //take a triangle
    
        gl_TessLevelOuter[0] = 1; //and subdivde it in 3 triangles
        gl_TessLevelOuter[1] = 1;
        gl_TessLevelOuter[2] = 1;
    }
    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
    

    So why it is still possible to set different values for each invocation? that's to add subdivisions based on screen error (at least is a reasonable use).

    if(gl_InvocationID==0){
        gl_TessLevelInner[0] = 2; //take a triangle
        gl_TessLevelOuter[0] = lenghtOnScreen<5? 3: 4;
    }
    
    if(gl_InvocationID==1)
        gl_TessLevelOuter[1] = lenghtOnScreen<5? 3: 4;
    
    if(gl_InvocationID==2)
        gl_TessLevelOuter[2] = lenghtOnScreen<5? 3: 4;
    
    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
    

    In the End:

    Another example with Inner=3 and Outer=2.

    Tessellated triangle

    Note the 3 cuts along the bisector and 2 cuts along the edges, all other cuts are just for "seamless transition"