1

I'm trying to learn how to use tesselation to render simple polygons, and for a starter I'm trying to write a simple program that essentially would just draw whatever vertices it's given, without any extra logic. My three shaders compile without error, but when I try to apply the program OpenGL returns error 1282. Based on the documentation for glUseProgram my best guess is that OpenGL is in a state where the program cannot be used, but I'm not able to tell why. If I don't include the TES in the program my code runs just fine, so I'm sure I'm not trying to apply the wrong program ID.

Since I know with fairly good confidence that my vertex and fragment shader are working correctly, the issue must be with the TES, which in my eyes will only put out the control points as vertices.

Is it not possible to simply pass control points through the shader as vertices?

My Tesselation Evaluation Shader:

#version 440 core
layout(triangles, equal_spacing, ccw) in;
in vec4 positionIn[];
void main()
{
    gl_Position = positionIn[0];
}

Vertex Shader:

#version 440 core
layout(location = 0) in vec4 position;
uniform mat4 u_MVP;
void main()
{
    gl_Position = u_MVP * position;
}

Fragment Shader:

#version 440 core
layout(location = 0) out vec4 colour;
uniform vec4 u_Colour;
void main()
{
    colour = u_Colour;
}

Rest of the code that handles the compilation and setup:

int compileShader(unsigned int type, const std::string& source)
{
    unsigned int id = glCreateShader(type);
    const char* src = source.c_str();
    glShaderSource(id, 1, &src, nullptr);
    glCompileShader(id);

    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);

    if (result == GL_FALSE)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        glGetShaderInfoLog(id, length, &length, message);

        std::cout << message << std::endl;
        glDeleteShader(id);
        return 0;
    }
    return id;
}

void setUpProgram()
{
    unsigned int program = glCreateProgram();
    unsigned int vs = compileShader(GL_VERTEX_SHADER, vertexShader);
    unsigned int fs = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
    unsigned int es = compileShader(GL_TESS_EVALUATION_SHADER, tesselationEvaluationShader);

    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glAttachShader(program, es);
    glLinkProgram(program);
    glValidateProgram(program);

    GLenum error = glGetError();
    while (error != GL_NO_ERROR)
    {
        std::cout << "Error: " << error << std::endl;
        GLenum error = glGetError();
    }

    glUseProgram(program);
    error = glGetError();
    std::cout << "Error: " << error << std::endl;
}
  • 2
    Why aren't you checking `GL_LINK_STATUS`? – genpfault May 14 '20 at 20:19
  • @genpfault I checked ´´´glGetError´´´ when debugging which returned no errors, and I assumed that the linking was successful. Checking ´´´GL_LINK_STATUS´´´ revealed that linking wasn't actually succseful, so thanks for the tip! – Andreas Vibeto May 14 '20 at 21:01

1 Answers1

2

There are two problems.

The first is that the vertex shader output is not being consumed by the tessellation evaluation shader. The VS outputs gl_Position, but the TES doesn't read gl_Position. It instead reads a user-defined input positionIn, which has no linkage to the value of gl_Position. Indeed, it has no value at all, and your shader linking process would have told you that if you checked whether your program successfully linked.

The corresponding TES input for the gl_Position output is, cleverly named, gl_Position. However, since this is the same name as the output you intend to write to, it is defined within an input interface block gl_PerVertex. So if you want to access it, you need to declare this interface block appropriately:

in gl_PerVertex
{
  vec4 gl_Position;
} gl_in[gl_MaxPatchVertices];

You can then use gl_in[ix].gl_Position to access the position for that vertex. Speaking of array indexing...

The second problem is that [0] is the wrong index. The job of the TES is to compute the attributes for a vertex within the tessellated abstract patch. To do that job, the TES is given all of the vertices for the patch. So each TES invocation for a patch gets the same set of inputs, in the same order.

So doing gl_in[0].gl_Position would yield the same value for each vertex in the patch. That's a triangle with zero area, and thus isn't helpful in displaying anything.

The only thing the TES invocations get which are different is the input value vertexTexCoord, which is the coordinate of this TES invocation within the abstract patch. This is a vec3, and for triangle tessellation, represents the barycentric coordinate of the vertex within the abstract patch.

So you need to convert this vec3 into an index. If you aren't doing any tessellation at all, then the barycentric coordinate will have one of the values at 1.0, with the other values being zero. So you can use that to tell you which index you should use.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982