OpenGL几何着色器实现贝塞尔曲线
几何着色器 (GS) 是用 GLSL 编写的着色器程序,用于控制基元的处理。几何着色器位于顶点着色器(或Tessellation 阶段)和Vertex Post-Processing阶段之间。
本文实现的是三次方贝塞尔曲线。
P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。
曲线的参数形式为:
需要注意的是,显卡会影响曲线的绘制效果,如果使用的是集中显卡,建议切换为独立显卡。
lines_adjacency:
line_strip:
geometry_shader_bezier.vs
#version 330 core layout (location = 0) in vec3 aPos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = vec4(aPos, 1.0); }
geometry_shader_bezier.gs
#version 330 core layout (lines_adjacency) in; //基元类型模式lines_adjacency,输入图元邻接线,输入数组gl_in[]大小为4,刚好绘制三次bezier曲线需要四个控制点 layout (line_strip, max_vertices = 200) out; //将一个点变为最多32个可连成线条的点 交给FragShader uniform mat4 model; uniform mat4 view; uniform mat4 projection; void creatBezier(){ int segments = 1000; float delta = 1.0 / float(segments); vec4 v; for ( int i=0; i<=segments; ++i ) { float t = delta * float(i);//插值计算参数t与segment关联起来 vec3 p0 = gl_in[0].gl_Position.xyz; vec3 p1 = gl_in[1].gl_Position.xyz; vec3 p2 = gl_in[2].gl_Position.xyz; vec3 p3 = gl_in[3].gl_Position.xyz; float len = length(p1 - p0)/2.0; // Linear interpolation vec3 p; p.x = (1 - t) * (1 - t) * (1 - t) * p0.x + 3 * t * (1 - t) * (1 - t)* p1.x + 3 * t*t* (1 - t)* p2.x + t * t * t * p3.x; p.y = (1 - t) * (1 - t) * (1 - t) * p0.y + 3 * t * (1 - t) * (1 - t)* p1.y + 3 * t*t* (1 - t)* p2.y + t * t * t * p3.y; p.z = 0; gl_Position = projection * view * model * vec4(p, 1); EmitVertex(); } } void main(){ creatBezier(); }
geometry_shader_bezier.fs
#version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0, 0.0, 0.0, 1.0); }
main.cpp
#include <glad/glad.h> #include <GLFW/glfw3.h> #include <glm/glm.hpp> #include <learnopengl/camera.h> #include <learnopengl/shader.h> #include <iostream> void framebuffer_size_callback(GLFWwindow* window, int width, int height); // settings const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); int main() { // glfw: initialize and configure // ------------------------------ glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif // glfw window creation // -------------------- GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // glad: load all OpenGL function pointers // --------------------------------------- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } // configure global opengl state // ----------------------------- glEnable(GL_DEPTH_TEST); // build and compile shaders // ------------------------- Shader shader("geometry_shader_bezier.vs", "geometry_shader_bezier.fs", "geometry_shader_bezier.gs"); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ float points[] = { 0.1f, 0.1f, 0.0f, 1.0f, 1.0f, 0.0f, 2.0f, 0.1f, 0.0f, 3.0f, 1.5f, 0.0f, }; unsigned int VBO, VAO; glGenBuffers(1, &VBO); glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(points), &points, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0); glBindVertexArray(0); // render loop // ----------- while (!glfwWindowShouldClose(window)) { // render // ------ glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // draw points shader.use(); glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 view = camera.GetViewMatrix(); glm::mat4 model = glm::mat4(1.0f); shader.setMat4("projection", projection); shader.setMat4("view", view); shader.setMat4("model", model); glBindVertexArray(VAO); glDrawArrays(GL_LINES_ADJACENCY, 0, 4); // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) // ------------------------------------------------------------------------------- glfwSwapBuffers(window); glfwPollEvents(); } // optional: de-allocate all resources once they've outlived their purpose: // ------------------------------------------------------------------------ glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glfwTerminate(); return 0; } // glfw: whenever the window size changed (by OS or user resize) this callback function executes // --------------------------------------------------------------------------------------------- void framebuffer_size_callback(GLFWwindow* window, int width, int height) { // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. glViewport(0, 0, width, height); }
效果:
完整项目代码:
https://github.com/mc-liyanliang/OpenGL-Shader/tree/master