opengl4 + glfw + glad动态绘制贝塞尔曲线程序参考

希望能给使用同样环境的朋友作参考(比如跟着learnopengl一书学下来的)

先上效果

实现了鼠标拖动顶点,添加新点后鼠标右键刷新曲线,backspace清空

下面是代码参考

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include "shader.h"
#include "bezier.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <iostream>

using namespace std;

// 窗口大小变化回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// 鼠标移动回调函数
void mouse_pos_callback(GLFWwindow* window, double posX, double posY);
// 鼠标按键回调函数
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
// 键盘按键回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);

// 窗口大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 最大顶点数和默认步长
const unsigned int MAX_VERTEX_COUNT = 16;
const float DEFAULT_DELTA = 0.02f;

Bezier bezierInstance; // 贝塞尔曲线实例
float vertexes[200] = {}; // 顶点数组

bool first_mouse = true;

float lastX = 800.0f / 2;
float lastY = 600.0f / 2;

void renderQuad();

int main()
{
    // 初始化GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    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);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetCursorPosCallback(window, mouse_pos_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetKeyCallback(window, key_callback);

    // 加载OpenGL函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // 编译着色器程序
    Shader simpleShader("simple.vert", "simple.frag");

    float controlPoints[MAX_VERTEX_COUNT * 2] = {};

    // 生成控制点的顶点缓冲对象和顶点数组对象
    unsigned int controlPointVBO, controlPointVAO;
    glGenVertexArrays(1, &controlPointVAO);
    glGenBuffers(1, &controlPointVBO);

    glBindVertexArray(controlPointVAO);
    glBindBuffer(GL_ARRAY_BUFFER, controlPointVBO);

    glEnableVertexAttribArray(0);

    unsigned int vertexsVBO, vertexsVAO;
    glGenVertexArrays(1, &vertexsVAO);
    glGenBuffers(1, &vertexsVBO);

    glBindVertexArray(vertexsVAO);
    glBindBuffer(GL_ARRAY_BUFFER, vertexsVBO);

    glEnableVertexAttribArray(0);

    // 设置正交投影矩阵
    glm::mat4 projection = glm::ortho(0.0f, (float)SCR_WIDTH, 0.0f, (float)SCR_HEIGHT, -1.0f, 1.0f);
    glm::mat4 model = glm::mat4(1.0f);
    glm::vec4 color = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f);

    simpleShader.use();
    simpleShader.setMat4("projection", projection);

    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window)) {
        // 清空颜色缓冲区和深度缓冲区
        glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        simpleShader.use();
        simpleShader.setVec4("color", color);

        int controlPointCount = bezierInstance.GetControlPointCount();

        // 绘制控制点
        for (int i = 0; i < controlPointCount; i++) {
            float vertex[2];
            bezierInstance.GetControlPoints(i, vertex);
            controlPoints[i * 2] = vertex[0];
            controlPoints[i * 2 + 1] = vertex[1];

            model = glm::mat4(1.0f);
            model = glm::translate(model, glm::vec3(vertex[0], vertex[1], 0.0f));
            model = glm::scale(model, glm::vec3(4.0f, 4.0f, 1.f));
            simpleShader.setMat4("model", model);

            renderQuad();
        }

        // 绘制控制点之间的连线
        model = glm::mat4(1.0f);
        simpleShader.setMat4("model", model);
        simpleShader.setVec4("color", glm::vec4(0.7f, 1.0f, 1.0f, 1.0f));

        glBindVertexArray(controlPointVAO);
        glBindBuffer(GL_ARRAY_BUFFER, controlPointVBO);

        glBufferData(GL_ARRAY_BUFFER, sizeof(controlPoints), controlPoints, GL_DYNAMIC_DRAW);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);

        glDrawArrays(GL_LINE_STRIP, 0, controlPointCount);

        // 绘制贝塞尔曲线
        simpleShader.setVec4("color", glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
        if (bezierInstance.GetControlPointCount() >= 3) {
            glBindVertexArray(vertexsVAO);
            glBindBuffer(GL_ARRAY_BUFFER, vertexsVBO);

            //传输顶点数据
            glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_DYNAMIC_DRAW);
            glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);

            glDrawArrays(GL_LINE_STRIP, 0, 50);
        }

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteVertexArrays(1, &controlPointVAO);
    glDeleteBuffers(1, &controlPointVBO);

    glfwTerminate();
    return 0;
}

// 窗口大小变化回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

// 鼠标移动回调函数
void mouse_pos_callback(GLFWwindow* window, double posX, double posY)
{
    if (first_mouse) {
        lastX = posX;
        lastY = posY;
        first_mouse = false;
    }

    float offestX = posX - lastX;
    float offestY = lastY - posY;
    lastX = posX;
    lastY = posY;

    posY = SCR_HEIGHT - posY;

    if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) != GLFW_PRESS) {
        return;
    }
    int index = bezierInstance.CursorOnControlPoint(posX, posY, 4.0f);
    if (index != -1) {
        bezierInstance.SetControlPoint(index, posX, posY);
        bezierInstance.bezierDeCasteljau(DEFAULT_DELTA, vertexes);
    }
}

// 鼠标按键回调函数
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
    double posX, posY;
    glfwGetCursorPos(window, &posX, &posY);

    if (first_mouse) {
        lastX = posX;
        lastY = posY;
        first_mouse = false;
    }

    float offestX = posX - lastX;
    float offestY = lastY - posY;
    lastX = posX;
    lastY = posY;

    posY = SCR_HEIGHT - posY;

    if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
        if (bezierInstance.CursorOnControlPoint(posX, posY, 4.0f) == -1) {
            bezierInstance.AddControlPoint(posX, posY);
        }
    }
    else if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
        bezierInstance.bezierDeCasteljau(DEFAULT_DELTA, vertexes);
    }
}

// 键盘按键回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_BACKSPACE && action == GLFW_PRESS) {
        bezierInstance.ClearControlPoints(vertexes, 200);
    }
}

unsigned int quadVAO = 0;
unsigned int quadVBO = 0;

// 渲染四边形
void renderQuad()
{
    float quadVertices[] = {
        // positions 
        -1.0f,  1.0f, 0.0f,
        -1.0f, -1.0f, 0.0f,
         1.0f,  1.0f, 0.0f,
         1.0f, -1.0f, 0.0f
    };
    if (quadVAO == 0)
    {
        // 设置四边形的顶点数组对象和顶点缓冲对象
        glGenVertexArrays(1, &quadVAO);
        glGenBuffers(1, &quadVBO);
        glBindVertexArray(quadVAO);
        glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    }
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glBindVertexArray(0);
}
#pragma once
#include<iostream>
using namespace std;

class Bezier
{
private:
    int controlPointCount = 0; // 控制点数量
    float controlPoints[16][2]; // 控制点数组,最多16个点

public:
    // 构造函数
    Bezier() {
        float tmp[1] = { 0 }; // 为了调用ClearControlPoints初始化顶点数组
        this->ClearControlPoints(tmp, 0); // 清空控制点
    }

    // 添加控制点
    void AddControlPoint(float x, float y) {
        if (controlPointCount >= 16) { // 控制点数量已满,无法添加
            return;
        }
        for (int i = 0; i < 16; i++) {
            if (controlPoints[i][0] == -1 && controlPoints[i][1] == -1) {
                controlPoints[i][0] = x;
                controlPoints[i][1] = y;
                break;
            }
        }

        controlPointCount++; // 控制点数量加一
    }

    // 设置控制点的位置
    void SetControlPoint(int i, float x, float y) {
        if (i >= 16 || i < 0) { // 索引超出范围
            return;
        }
        controlPoints[i][0] = x;
        controlPoints[i][1] = y;
    }

    // 清空控制点
    void ClearControlPoints(float* vertexes, int count) {
        for (int i = 0; i < 16; i++) {
            controlPoints[i][0] = -1;
            controlPoints[i][1] = -1;
        }
        // 清空顶点数组(使用VBO)
        for (int i = 0; i < count; i++) {
            vertexes[i] = 0;
        }

        controlPointCount = 0; // 控制点数量清零
    }

    // 计算贝塞尔曲线的顶点,返回顶点数量
    int bezierDefinition(float delta, float* vertexes) {
        if (controlPointCount <= 2) { // 控制点数量小于等于2,无法绘制贝塞尔曲线
            return 0;
        }
        float vertexesCount = 1 / delta;
        for (float t = 0; t <= 1; t += delta) {
            float x = 0;
            float y = 0;
            for (int i = 0; i < controlPointCount; i++) {
                x += controlPoints[i][0] * C(controlPointCount - 1, i) * pow(1 - t, controlPointCount - 1 - i) * pow(t, i);
                y += controlPoints[i][1] * C(controlPointCount - 1, i) * pow(1 - t, controlPointCount - 1 - i) * pow(t, i);
            }
            *vertexes++ = x;
            *vertexes++ = y;
        }
        return vertexesCount;
    }

    // 使用De Casteljau算法计算贝塞尔曲线的顶点,返回顶点数量
    int bezierDeCasteljau(float delta, float* vertexes) {
        if (controlPointCount <= 2) { // 控制点数量小于等于2,无法绘制贝塞尔曲线
            return 0;
        }
        float vertexesCount = 1 / delta;
        for (float t = 0; t <= 1; t += delta) {
            float x = 0;
            float y = 0;
            float tmp[16][2];
            for (int i = 0; i < controlPointCount; i++) {
                tmp[i][0] = controlPoints[i][0];
                tmp[i][1] = controlPoints[i][1];
            }
            for (int i = 0; i < controlPointCount - 1; i++) {
                for (int j = 0; j < controlPointCount - 1 - i; j++) {
                    // 相邻点的线性插值
                    tmp[j][0] = tmp[j][0] * (1 - t) + tmp[j + 1][0] * t;
                    tmp[j][1] = tmp[j][1] * (1 - t) + tmp[j + 1][1] * t;
                }
            }
            *vertexes++ = tmp[0][0];
            *vertexes++ = tmp[0][1];
        }
        return vertexesCount;
    }

    // 获取控制点的位置
    void GetControlPoints(int i, float vertex[2]) {
        if (i >= 16 || i < 0) { // 索引超出范围
            return;
        }
        vertex[0] = controlPoints[i][0];
        vertex[1] = controlPoints[i][1];
    }

    // 获取控制点数量
    int GetControlPointCount() {
        return controlPointCount;
    }

    // 检查鼠标是否在控制点上,如果是返回控制点的索引,否则返回-1
    int CursorOnControlPoint(float x, float y, float halfL) {
        for (int i = 0; i < controlPointCount; i++) {
            if (controlPoints[i][0] - halfL <= x && x <= controlPoints[i][0] + halfL && controlPoints[i][1] - halfL <= y && y <= controlPoints[i][1] + halfL)
                return i;
        }
        return -1;
    }

    // 计算组合数C(m, n)
    int C(int m, int n) {
        if (m < n) {
            return 0;
        }
        if (m == n) {
            return 1;
        }
        if (n == 0) {
            return 1;
        }
        return C(m - 1, n - 1) + C(m - 1, n);
    }
};

顶点着色器和片元着色器main函数各只有一行

gl_Position = projection * model * vec4(aPos, 0.0, 1.0);
frag_color = vec4(1.0f, 1.0f, 1.0f, 1.0f) * color;

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用 GLFWGLAD绘制四边形的示例代码: ```c++ #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void processInput(GLFWwindow* window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } } int main() { glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(800, 600, "Quad", nullptr, nullptr); if (window == nullptr) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } glViewport(0, 0, 800, 600); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); while (!glfwWindowShouldClose(window)) { processInput(window); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.5f, 0.5f, 0.0f, -0.5f, 0.5f, 0.0f }; unsigned int indices[] = { 0, 1, 3, 1, 2, 3 }; unsigned int VBO, VAO, EBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0; } ``` 在 `main()` 函数中,我们首先初始化 GLFW 库和 OpenGL 上下文。然后使用 GLAD 库初始化 OpenGL 函数指针。 在主循环中,我们首先处理输入事件,然后清空颜色缓冲区。接着定义四边形的顶点坐标和顶点索引,生成 VBO、VAO 和 EBO,绑定并填充数据。最后绘制四边形并交换缓冲区。 在 `framebuffer_size_callback()` 回调函数中,我们重新设置视口大小。 在 `processInput()` 函数中,我们检查 ESC 键是否被按下,如果是则设置窗口关闭标志,退出主循环。 这段代码使用了现代 OpenGL 的方式,使用了顶点缓冲对象、顶点数组对象和索引缓冲对象。同时也使用了 GLAD 库来加载 OpenGL 函数指针。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值