Reputation: 199
I recently started learning tessellation and today I was trying to draw a triangle after tessellation so that I can see all the tessellated smaller triangles using glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
. But for some reason the output is just a coloured background without any triangle in it.
For the tessellation, I make a control shader
and evaluation shader
and then link them to the program
.(Code below)
// Source code for Tesselation Control Shader
static const GLchar * tesselation_control_shader[] =
{
"#version 450 core \n"
" \n"
"layout(vertices = 3) out; \n"
" \n"
"void main(void) \n"
"{ \n"
" //Only if I am invocation 0 \n"
" if (gl_InvocationID == 0) \n"
" { \n"
" gl_TessLevelInner[0] = 5.0; \n"
" gl_TessLevelOuter[0] = 5.0; \n"
" gl_TessLevelOuter[1] = 5.0; \n"
" gl_TessLevelOuter[2] = 5.0; \n"
" } \n"
" \n"
" // Everybody copies their input to their input \n"
" gl_out[gl_InvocationID].gl_Position = \n"
" gl_in[gl_InvocationID].gl_Position; \n"
"} \n"
};
// Source code for tesselation evaluation shader
static const GLchar * tesselation_evaluation_shader[] =
{
"#version 450 core \n"
" \n"
"layout(triangles, equal_spacing, cw) in; \n"
" \n"
"void main(void) \n"
"{ \n"
" gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position + \n"
" gl_TessCoord.y * gl_in[1].gl_Position + \n"
" gl_TessCoord.z * gl_in[2].gl_Position); \n"
"} \n"
};
I then call the glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
in my render
function right before draw the triangle using glDrawArrays(GL_TRIANGLE, 0, 3)
.
I initially thought that the glPolygonMode
was defaulting to GL_FILL
but I don't think that's the issue since I am just following a book (OpenGL Superbible 7th Edition).
How can I fix this?
Edit:
I have added the code of my entire program below:
GLuint compile_shaders(void)
{
GLuint vertex_shader;
GLuint fragment_shader;
GLuint control_shader;
GLuint evaluation_shader;
GLuint program;
// Source code for Vertex Shader
static const GLchar * vertex_shader_source[] =
{
"#version 450 core \n"
" \n"
"// offset and color are input vertex attribute \n"
"layout (location = 0) in vec4 offset; \n"
"layout (location = 1) in vec4 color; \n"
" \n"
"//Declare VS_OUT as an output interface block \n"
"out VS_OUT \n"
"{ \n"
" vec4 color; //Send color to next stage \n"
"}vs_out; \n"
" \n"
"void main(void) \n"
"{ \n"
" //Decalre a hardcoded array of positions \n"
" const vec4 vertices[3] = vec4[3](vec4(0.25, -0.25, 0.5, 1.0), \n"
" vec4(-0.25, -0.25, 0.5, 1.0), \n"
" vec4(0.25, 0.25, 0.5, 1.0)); \n"
" \n"
" //Index into our array using gl_VertexID \n"
" gl_Position = vertices[gl_VertexID] + offset; \n"
" \n"
"//color = vec4(1.0, 0.0, 0.0, 1.0); \n"
"//Output fixed value for vs_color \n"
"vs_out.color = color; \n"
"} \n"
};
// Source code for Fragment Shader
static const GLchar * fragment_shader_source[] =
{
"#version 450 core \n"
" \n"
"//Declare VS_OUT as an input interface block \n"
"in VS_OUT \n"
"{ \n"
" vec4 color; //Send color to next stage \n"
"}fs_in; \n"
" \n"
"//Ouput to the framebuffer \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
"// Simply assign the color we were given by the vertex shader to our output \n"
" color = fs_in.color; \n"
"} \n"
};
// Source code for Tesselation Control Shader
static const GLchar * tesselation_control_shader[] =
{
"#version 450 core \n"
" \n"
"layout(vertices = 3) out; \n"
" \n"
"void main(void) \n"
"{ \n"
" //Only if I am invocation 0 \n"
" if (gl_InvocationID == 0) \n"
" { \n"
" gl_TessLevelInner[0] = 5.0; \n"
" gl_TessLevelOuter[0] = 5.0; \n"
" gl_TessLevelOuter[1] = 5.0; \n"
" gl_TessLevelOuter[2] = 5.0; \n"
" } \n"
" \n"
" // Everybody copies their input to their input \n"
" gl_out[gl_InvocationID].gl_Position = \n"
" gl_in[gl_InvocationID].gl_Position; \n"
"} \n"
};
// Source code for tesselation evaluation shader
static const GLchar * tesselation_evaluation_shader[] =
{
"#version 450 core \n"
" \n"
"layout(triangles, equal_spacing, cw) in; \n"
" \n"
"void main(void) \n"
"{ \n"
" gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position + \n"
" gl_TessCoord.y * gl_in[1].gl_Position + \n"
" gl_TessCoord.z * gl_in[2].gl_Position); \n"
"} \n"
};
// Create and compiler Vertex Shader
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, vertex_shader_source, NULL);
glCompileShader(vertex_shader);
// Create and compiler Fragment Shader
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, fragment_shader_source, NULL);
glCompileShader(fragment_shader);
// Create and compile tesselation control shader
control_shader = glCreateShader(GL_TESS_CONTROL_SHADER);
glShaderSource(control_shader, 1, tesselation_control_shader, NULL);
glCompileShader(control_shader);
// Create and compile tesselation evaluation shader
evaluation_shader = glCreateShader(GL_TESS_CONTROL_SHADER);
glShaderSource(evaluation_shader, 1, tesselation_control_shader, NULL);
glCompileShader(evaluation_shader);
// Create program, attach shaders to it, and link it
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glAttachShader(program, control_shader);
glAttachShader(program, evaluation_shader);
glLinkProgram(program);
// Delete shaders as program has them now
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
glDeleteShader(control_shader);
glDeleteShader(evaluation_shader);
return program;
};
class TesselationCSOne : public sb7::application
{
public:
void startup()
{
rendering_program = compile_shaders();
glCreateVertexArrays(1, &vertex_array_object);
glBindVertexArray(vertex_array_object);
}
void shutdown()
{
glDeleteVertexArrays(1, &vertex_array_object);
glDeleteProgram(rendering_program);
glDeleteVertexArrays(1, &vertex_array_object);
}
// Our rendering function
void render(double currentTime)
{
// Sets colour
static const GLfloat color[] = { (float)sin(currentTime) * 0.5f + 0.5f, (float)sin(currentTime) * 0.5f + 0.5f, 0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, color);
//Tell OpenGL to draw only the outlines of the resulting triangle
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Use program object we created for rendering
glUseProgram(rendering_program);
GLfloat attrib[] = { 1.0, 0.0, 0.0, 0.0 };/*{ (float)sin(currentTime) * 0.5f, (float)sin(currentTime) * 0.6f, 0.0f, 0.0f };*/
// Update value of input attribute 0
glVertexAttrib4fv(0, attrib);
// Draw pathes for tesselation shaders
glPatchParameteri(GL_PATCH_VERTICES, 3);
// Draw one triangle
glDrawArrays(GL_PATCHES, 0, 3);
}
private:
GLuint rendering_program;
GLuint vertex_array_object;
};
// Only instance of DECLARE_MAIN to state entry point
DECLARE_MAIN(TesselationCSOne);
Upvotes: 1
Views: 1199
Reputation: 199
So after checking a lot of sources and the Superbible's code repository I realized I have a lot of unnecessary code (For example, the interface blocks
in the shaders) and even quite a few mistakes (For example I had two program
varibles).
But, after fixing all that the code that produces the required output (A tessellated triangle) is:
/**
Program to draw a triangle with tesselation.
**/
#include <sb7.h>
class TesselatedTriangle : public sb7::application
{
void init()
{
static const char title[] = "Tessellated Triangle";
sb7::application::init();
memcpy(info.title, title, sizeof(title));
}
virtual void startup()
{
static const char * vertex_shader_source[] =
{
"#version 450 core \n"
" \n"
"void main(void) \n"
"{ \n"
" const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0), \n"
" vec4(-0.25, -0.25, 0.5, 1.0), \n"
" vec4( 0.25, 0.25, 0.5, 1.0)); \n"
" \n"
" gl_Position = vertices[gl_VertexID]; \n"
"} \n"
};
static const char * tesselation_control_shader_source[] =
{
"#version 450 core \n"
" \n"
"layout (vertices = 3) out; \n"
" \n"
"void main(void) \n"
"{ \n"
" if (gl_InvocationID == 0) \n"
" { \n"
" gl_TessLevelInner[0] = 5.0; \n"
" gl_TessLevelOuter[0] = 5.0; \n"
" gl_TessLevelOuter[1] = 5.0; \n"
" gl_TessLevelOuter[2] = 5.0; \n"
" } \n"
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; \n"
"} \n"
};
static const char * tesselation_evaluation_shader_source[] =
{
"#version 450 core \n"
" \n"
"layout (triangles, equal_spacing, cw) in; \n"
" \n"
"void main(void) \n"
"{ \n"
" gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) + \n"
" (gl_TessCoord.y * gl_in[1].gl_Position) + \n"
" (gl_TessCoord.z * gl_in[2].gl_Position); \n"
"} \n"
};
static const char * fragment_shader_source[] =
{
"#version 450 core \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = vec4(0.0, 0.8, 1.0, 1.0); \n"
"} \n"
};
rendering_program = glCreateProgram();
// Compile shaders
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, vertex_shader_source, NULL);
glCompileShader(vs);
GLuint tcs = glCreateShader(GL_TESS_CONTROL_SHADER);
glShaderSource(tcs, 1, tesselation_control_shader_source, NULL);
glCompileShader(tcs);
GLuint tes = glCreateShader(GL_TESS_EVALUATION_SHADER);
glShaderSource(tes, 1, tesselation_evaluation_shader_source, NULL);
glCompileShader(tes);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, fragment_shader_source, NULL);
glCompileShader(fs);
// Attach shaders to the program
glAttachShader(rendering_program, vs);
glAttachShader(rendering_program, tcs);
glAttachShader(rendering_program, tes);
glAttachShader(rendering_program, fs);
// Link the program
glLinkProgram(rendering_program);
// Generate vertex arrays
glGenVertexArrays(1, &vertex_array_object);
glBindVertexArray(vertex_array_object);
// Declare the drawing mode for the polygons
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
virtual void render(double currentTime)
{
static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, green);
glUseProgram(rendering_program);
glDrawArrays(GL_PATCHES, 0, 3);
}
virtual void shutdown()
{
glDeleteVertexArrays(1, &vertex_array_object);
glDeleteProgram(rendering_program);
}
private:
GLuint rendering_program;
GLuint vertex_array_object;
};
// One and only instance of DECLARE_MAIN
DECLARE_MAIN(TesselatedTriangle)
Hopefully this helps somebody else with the same problem.
Upvotes: 1
Reputation: 210998
If you use a tessellation shader then you have to draw Patches. You have to set the size of the patches by glPatchParameteri( GL_PATCH_VERTICES, ...)
and the primitive type has to be GL_PATCHES
.
If the the number of vertices in a patch is 3, then you have to do it like this:
glPatchParameteri(GL_PATCH_VERTICES, 3);
glDrawArrays(GL_PATCHES, 0, 3)
See OpenGL 4.6 API Core Profile Specification; 10.1.15 Separate Patches; page 342:
Separate patches are specified with mode
PATCHES
. A patch is an ordered collection of vertices used for primitive tessellation (section 11.2). The vertices comprising a patch have no implied geometric ordering. The vertices of a patch are used by tessellation shaders and the fixed-function tessellator to generate new point, line, or triangle primitives.void PatchParameteri( enum pname, int value );
with pname set to
PATCH_VERTICES
Your shader program doesn't even link, because the fragment shader tries to read from an input interface block,
which is not declared as an output from the previous shader stage.
You have to pass the vertex attributes, through the tessellation control and evaluation shader to the fragment shader:
Tessellation control shader:
#version 450 core
layout(vertices = 3) out;
in VS_OUT
{
vec4 color;
} tesc_in[];
out TESC_OUT
{
vec4 color;
} tesc_out[];
void main(void)
{
if (gl_InvocationID == 0)
{
gl_TessLevelInner[0] = 5.0;
gl_TessLevelOuter[0] = 5.0;
gl_TessLevelOuter[1] = 5.0;
gl_TessLevelOuter[2] = 5.0;
}
tesc_out[gl_InvocationID].color = tesc_in[gl_InvocationID].color;
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
}
Tessellation evaluation shader:
#version 450 core
layout(triangles, equal_spacing, cw) in;
in TESC_OUT
{
vec4 color;
} tese_in[];
out TESE_OUT
{
vec4 color;
} tese_out;
void main(void)
{
tese_out.color = ( gl_TessCoord.x * tese_in[0].color +
gl_TessCoord.y * tese_in[1].color +
gl_TessCoord.z * tese_in[2].color ) / 3.0;
gl_Position = ( gl_TessCoord.x * gl_in[0].gl_Position +
gl_TessCoord.y * gl_in[1].gl_Position +
gl_TessCoord.z * gl_in[2].gl_Position ) / 3.0;
}
Fragment shader:
#version 450 core
in TESE_OUT
{
vec4 color;
} fs_in;
out vec4 color;
void main(void)
{
color = fs_in.color;
}
Further, I recommend to check if a shader object was successfully compiled:
GLuint shaderObj = .... ;
glCompileShader( shaderObj );
GLint status = GL_TRUE;
glGetShaderiv( shaderObj, GL_COMPILE_STATUS, &status );
if ( status == GL_FALSE )
{
GLint logLen;
glGetShaderiv( shaderObj, GL_INFO_LOG_LENGTH, &logLen );
std::vector< char >log( logLen );
GLsizei written;
glGetShaderInfoLog( shaderObj, logLen, &written, log.data() );
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
and a shader program object was successfully linked:
GLuint progObj = ....;
glLinkProgram( progObj );
GLint status = GL_TRUE;
glGetProgramiv( progObj, GL_LINK_STATUS, &status );
if ( status == GL_FALSE )
{
GLint logLen;
glGetProgramiv( progObj, GL_INFO_LOG_LENGTH, &logLen );
std::vector< char >log( logLen );
GLsizei written;
glGetProgramInfoLog( progObj, logLen, &written, log.data() );
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
By the way, read about Raw string literals, which simplify the declaration of the shader source code strings:
e.g.
std::string fragment_shader_source = R"(
#version 450 core
in TESE_OUT
{
vec4 color;
} fs_in;
out vec4 color;
void main(void)
{
color = fs_in.color;
}
)";
Further note, the the offset
probably moves the the triangle out of the viewport. Either change the value of the offset
in the attribute initialization:
GLfloat attrib[] = { 0.0, 0.0, 0.0, 0.0 };
or get rid of the offest
in the vertex shader for debug reasons
gl_Position = vertices[gl_VertexID];
You have to ensure that the color attribute is set too:
GLfloat attrib1[] = { 1.0, 1.0, 0.0, 1.0 };
glVertexAttrib4fv(1, attrib1);
The result may look like this:
Upvotes: 3