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



