Недавно я начал изучать тесселяцию, и сегодня я пытался нарисовать треугольник после тесселяции, чтобы я мог видеть все меньшие тесселяции, используя glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
, Но по какой-то причине на выходе получается просто цветной фон без треугольника.
Для тесселяции я делаю control shader
а также evaluation shader
а затем связать их с program
(Код ниже)
// 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"};
Я тогда называю glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
в моем render
Функция прямо перед рисованием треугольника, используя glDrawArrays(GL_TRIANGLE, 0, 3)
,
Я изначально думал, что glPolygonMode
по умолчанию GL_FILL
но я не думаю, что это проблема, так как я просто следую книге (OpenGL Superbible 7th Edition).
Как я могу это исправить?
Редактировать:
Я добавил код всей моей программы ниже:
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);
Если вы используете тесселяционный шейдер, вы должны рисовать патчи. Вы должны установить размер патчей с помощью glPatchParameteri( GL_PATCH_VERTICES, ...)
и примитивный тип должен быть GL_PATCHES
,
Если количество вершин в патче равно 3, то вы должны сделать это следующим образом:
glPatchParameteri(GL_PATCH_VERTICES, 3);
glDrawArrays(GL_PATCHES, 0, 3)
Увидеть Спецификация основного профиля API OpenGL 4.6; 10.1.15 Отдельные патчи; страница 342:
Отдельные патчи указываются в режиме
PATCHES
, Патч — это упорядоченная коллекция вершин, используемая для примитивной тесселяции (раздел 11.2). Вершины, содержащие патч, не имеют подразумеваемого геометрического порядка. Вершины патча используются шейдерами тесселяции и тесселлятором с фиксированной функцией для генерации новых точечных, линейных или треугольных примитивов.void PatchParameteri( enum pname, int value );
с pname установленным в
PATCH_VERTICES
Ваша шейдерная программа даже не связывается, потому что фрагментный шейдер пытается прочитать из блока входного интерфейса,
который не объявлен как выход из предыдущего этапа шейдера.
Вы должны передать атрибуты вершин через шейдер управления и оценки тесселяции фрагментному шейдеру:
Шейдер управления тесселяцией:
#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;
}
Шейдер оценки тесселяции:
#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;
}
Фрагмент шейдера:
#version 450 core
in TESE_OUT
{
vec4 color;
} fs_in;
out vec4 color;
void main(void)
{
color = fs_in.color;
}
Далее, я рекомендую проверить, был ли успешно скомпилирован шейдерный объект:
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;
}
и программный объект шейдера был успешно связан:
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;
}
Кстати, читайте о Необработанные строковые литералы, которые упрощают объявление строк исходного кода шейдера:
например
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;
}
)";
Далее обратите внимание, что offset
вероятно, перемещает треугольник из области просмотра. Либо измените значение offset
в инициализации атрибута:
GLfloat attrib[] = { 0.0, 0.0, 0.0, 0.0 };
или избавиться от offest
в вершинном шейдере по причинам отладки
gl_Position = vertices[gl_VertexID];
Вы должны убедиться, что атрибут color также установлен:
GLfloat attrib1[] = { 1.0, 1.0, 0.0, 1.0 };
glVertexAttrib4fv(1, attrib1);
Результат может выглядеть так:
Поэтому, проверив много источников и репозиторий кода в Superbible, я понял, что у меня много ненужного кода (например, interface blocks
в шейдерах) и даже немало ошибок (например у меня было два program
varibles).
Но после исправления всего этого код, который выдает требуемый результат (тесселяционный треугольник):
/**
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)
Надеюсь, это поможет кому-то еще с той же проблемой.