最简单的曲面细分着色器(Tesselation Shader)【OpenGL】【GLSL】
发表于2018-02-07
效果如图:
按 ‘m’ 键后
代码及详细注释如下:
GLuint program; GLuint vao; void startup() { static const char * vs_source[] = { "#version 410 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" // 根据当前处理的顶点 ID 为顶点位置赋值 "} \n" }; // http://www.cnblogs.com/zenny-chen/p/4280100.html static const char * tcs_source[] = { "#version 410 core \n" " \n" "layout (vertices = 3) out; \n" // out-patch 的顶点个数,细分控制着色器将会被执行3次 " \n" "void main(void) \n" "{ \n" // 仅在第一次执行(第一个顶点)时赋值(控制细分程度) " if (gl_InvocationID == 0) \n" " { \n" " gl_TessLevelInner[0] = 5.0; \n" // 内部划分5个区域(新增4排顶点,见下图) " gl_TessLevelOuter[0] = 5.0; \n" // 左边划分5段 " gl_TessLevelOuter[1] = 5.0; \n" // 右边划分5段 " gl_TessLevelOuter[2] = 5.0; \n" // 下边划分5段 " } \n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; \n" // 通常直接将 in-patch 顶点赋给 out-patch 顶点(也可以新建或移除) "} \n" }; static const char * tes_source[] = { "#version 410 core \n" " \n" "layout (triangles, equal_spacing, cw) in; \n" // 指定图元生成域、细分坐标空间、图元的面朝向 //"layout (triangles, equal_spacing, cw, point_mode) in; \n" // 输出点模式 " \n" "void main(void) \n" "{ \n" " gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) + \n" // gl_TessCoord:细分后的新增坐标(插值比例) " (gl_TessCoord.y * gl_in[1].gl_Position) + \n" // 根据 input-patch 生成输出顶点的位置 " (gl_TessCoord.z * gl_in[2].gl_Position); \n" // 每个细分坐标都会让 TES 的执行一次 "} \n" }; static const char * fs_source[] = { "#version 410 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" }; program = glCreateProgram(); GLuint vs = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vs, 1, vs_source, NULL); glCompileShader(vs); // Tesselation Control Shader GLuint tcs = glCreateShader(GL_TESS_CONTROL_SHADER); glShaderSource(tcs, 1, tcs_source, NULL); glCompileShader(tcs); // Tesselation Evaluation Shader GLuint tes = glCreateShader(GL_TESS_EVALUATION_SHADER); glShaderSource(tes, 1, tes_source, NULL); glCompileShader(tes); GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fs, 1, fs_source, NULL); glCompileShader(fs); glAttachShader(program, vs); glAttachShader(program, tcs); glAttachShader(program, tes); glAttachShader(program, fs); glLinkProgram(program); glGenVertexArrays(1, &vao); glBindVertexArray(vao); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } void render() { static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f }; glClearBufferfv(GL_COLOR, 0, green); glUseProgram(program); glDrawArrays(GL_PATCHES, 0, 3); glutSwapBuffers(); } void shutdown() { glDeleteVertexArrays(1, &vao); glDeleteProgram(program); } void reshape(int width, int height) { glViewport(0, 0, width, height); GLfloat aspect = GLfloat(width) / height; //glutPostRedisplay(); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 'q': case 'Q': case 033 /* Escape key */: exit(EXIT_SUCCESS); break; case 'm': // 切换填充模式或镂空模式 { static GLenum mode = GL_FILL; mode = (mode == GL_FILL ? GL_LINE : GL_FILL); glPolygonMode(GL_FRONT_AND_BACK, mode); break; } } glutPostRedisplay(); } int _tmain(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA /*| GLUT_STENCIL | GLUT_DEPTH*/); glutInitWindowPosition(100, 100); glutInitWindowSize(512, 512); glutCreateWindow("TesselateTri"); // get an openGL context GLenum err = glewInit(); // init glew if (err != GLEW_OK) { fprintf(stderr, "Error: %s\n", glewGetErrorString(err)); exit(-2); } startup(); glutDisplayFunc(render); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }
内部新增的4排顶点示意图