Search code examples
c++openglglslflickertessellation

Tesselation result flickering - OpenGL/GLSL


I'm trying to implement a simple tessellation program based on this tutorial.

These are my shaders:

Vertex:

#version 410 core

uniform mat4 mvMatrix;
uniform mat3 normalMatrix;

in vec4 vPosition;
in vec3 vNormal;

// Input for the tesselation control shader
smooth out vec3 vEyePos_TCS;
smooth out vec3 vNorm_TCS;

void main() 
{
    vNorm_TCS = normalize(normalMatrix * vNormal);
    vEyePos_TCS = vec3(mvMatrix * vPosition);       
}  

Tessellation Control:

#version 410 core

// Define the number of CPs in the output patch
layout (vertices = 3) out;

uniform vec3 eyePos;

in vec3 vEyePos_TCS[];
in vec3 vNorm_TCS[];

// Output for the tesselation evaluation shader
out vec3 vEyePos_TES[];
out vec3 vNorm_TES[];

float GetTessLevel(float dist0, float dist1)
{
    float avgDist = (dist0 + dist1) / 2.0;

    if (avgDist <= 10.0)
        return 5.0;
    else if (avgDist <= 15.0)
        return 3.0;
    else 
        return 1.0;
}

void main()
{
    // Set the control points of the output patch
    vEyePos_TES[gl_InvocationID] = vEyePos_TCS[gl_InvocationID];
    vNorm_TES[gl_InvocationID] = vNorm_TCS[gl_InvocationID];

    // Calculate the distance from the camera to the three control points
    float dist0 = distance(eyePos, vEyePos_TES[0]);
    float dist1 = distance(eyePos, vEyePos_TES[1]);
    float dist2 = distance(eyePos, vEyePos_TES[2]);

    // Calculate the tesselation levels
    gl_TessLevelOuter[0] = GetTessLevel(dist1, dist2);
    gl_TessLevelOuter[1] = GetTessLevel(dist2, dist0);
    gl_TessLevelOuter[2] = GetTessLevel(dist0, dist1);

    gl_TessLevelInner[0] = gl_TessLevelOuter[2];
}

Tessellation Evaluator:

#version 410 core

layout(triangles, equal_spacing, ccw) in;

uniform mat4 pMatrix;

in vec3 vEyePos_TES[];
in vec3 vNorm_TES[];

// Input for the geometry shader
out vec3 vEyePos_GS;
out vec3 vNorm_GS;

vec3 interpolate3D(vec3 v0, vec3 v1, vec3 v2)
{
    return vec3(gl_TessCoord.x) * v0 + vec3(gl_TessCoord.y) * v1 + vec3(gl_TessCoord.z) * v2;
} 

void main()
{
    // Interpolate the attributes of the output vertex using the barycentric coordinates
    vNorm_GS = interpolate3D(vNorm_TES[0], vNorm_TES[1], vNorm_TES[2]);
    vEyePos_GS = interpolate3D(vEyePos_TES[0], vEyePos_TES[1], vEyePos_TES[2]);

    gl_Position = pMatrix * vec4(vEyePos_GS, 1.0);
}

Geometry:

#version 410

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

in vec3 vEyePos_GS[];
in vec3 vNorm_GS[];

out vec3 vEyePos_FS;
out vec3 vNorm_FS;
out vec3 gTriDistance;

void main()
{
    vNorm_FS = vNorm_GS[0];
    vEyePos_FS = vEyePos_GS[0];
    gTriDistance = vec3(1, 0, 0);
    gl_Position = gl_in[0].gl_Position;
    EmitVertex();

    vNorm_FS = vNorm_GS[1];
    vEyePos_FS = vEyePos_GS[1];
    gTriDistance = vec3(0, 1, 0);
    gl_Position = gl_in[1].gl_Position;
    EmitVertex();

    vNorm_FS = vNorm_GS[2];
    vEyePos_FS = vEyePos_GS[2];
    gTriDistance = vec3(0, 0, 1);
    gl_Position = gl_in[2].gl_Position; 
    EmitVertex();

    EndPrimitive();
}  

Fragment:

#version 410 core

uniform vec3 vLightPosition;
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;

in vec3 vEyePos_FS;
in vec3 vNorm_FS;
in vec3 gTriDistance;

out vec4 fragColor;

float amplify(float d, float scale, float offset)
{
    d = scale * d + offset;
    d = clamp(d, 0, 1);
    d = 1 - exp2(-2 * d * d);
    return d;
}

void main()
{ 
    vec3 L = normalize(vLightPosition - vEyePos_FS);   
    vec3 E = normalize(-vEyePos_FS);
    vec3 R = normalize(-reflect(L, vNorm_FS));  

   //calculate Ambient Term:  
   vec4 Iamb = ambientColor;    

   //calculate Diffuse Term:  
   vec4 Idiff = diffuseColor * max(dot(vNorm_FS, L), 0.0);
   Idiff = clamp(Idiff, 0.0, 1.0);     

   // calculate Specular Term:
   vec4 Ispec = specularColor * pow(max(dot(R, E), 0.0), 0.3 * 128.0);
   Ispec = clamp(Ispec, 0.0, 1.0); 

   fragColor = Iamb + Idiff + Ispec; 

   float d1 = min( min( gTriDistance.x, gTriDistance.y ), gTriDistance.z );
   fragColor = fragColor * amplify( d1, 60, -0.5 ) ;
};

The problem that I'm facing seems to be similar to this one, in which when the tess level gets higher, the surface starts to flicker. However, I couldn't solve it so far.

To draw, I simple do:

glBindVertexArray(_vao);
LoadUniformVariables();
glPatchParameteri(GL_PATCH_VERTICES, 3);
glDrawArrays(GL_PATCHES, 0, _modelCoordinates.size());
glBindVertexArray(0);

Those are my results:

enter image description here enter image description here enter image description here


Solution

  • Well, the problem seemed to be with my drawing call and how my data was sctructured. The following works properly:

        glBindVertexArray(_vao);
        LoadUniformVariables();
        glPatchParameteri(GL_PATCH_VERTICES, 3);
        glDrawElements(GL_PATCHES, _modelTriangulation.size(), GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);