首页 > 其他分享 >用GLSL 和 GLM 实现OpenGL trackball

用GLSL 和 GLM 实现OpenGL trackball

时间:2024-11-09 18:31:51浏览次数:1  
标签:GLSL 1.0 glm int 0.0 0.5 vec3 trackball GLM

以下是一个使用GLSL(OpenGL Shading Language)和GLM(OpenGL Mathematics)库来实现OpenGL trackball功能的示例。这个示例将展示如何通过鼠标操作来旋转场景中的物体,就像在操作一个虚拟的轨迹球一样。

1. 准备工作

  • 包含必要的头文件和库
    • 需要包含GLFW头文件用于创建窗口和处理事件,GLAD头文件用于加载OpenGL函数指针,GLM头文件用于数学运算,以及fstream用于读取着色器文件(这里假设着色器代码存储在外部文件中)。
#include <GLFW/glfw3.h>
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
  • 设置窗口相关参数
    • 定义窗口的宽度和高度,创建GLFW窗口,并初始化GLAD。
// 窗口大小
const int WIDTH = 800;
const int HEIGHT = 600;

int main(int argc, char** argv) {
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL Trackball with GLSL and GLM", 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;
        glfwTerminate();
        return -1;
    }

    // 其他初始化步骤将在后续添加
    //...

    // 主循环
    while (!glfwWindowShouldClose(window)) {
        // 渲染和事件处理逻辑将在后续添加
        //...

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

2. 加载着色器程序

  • 读取着色器文件内容
    • 创建函数来读取顶点着色器和片段着色器文件的内容。
std::string readShaderFile(const std::string& filePath) {
    std::ifstream shaderFile(filePath);
    if (!shaderFile.is_open()) {
        std::cout << "Failed to open shader file: " << filePath << std::endl;
        return "";
    }

    std::stringstream buffer;
    buffer << shaderFile.rdbuf();
    shaderFile.close();

    return buffer.str();
}
  • 编译和链接着色器程序
    • 创建函数来编译顶点着色器和片段着色器,并将它们链接成一个完整的着色器程序。
unsigned int createShaderProgram(const std::string& vertexShaderPath, const std::string& fragmentShaderPath) {
    // 读取顶点着色器文件内容
    std::string vertexShaderCode = readShaderFile(vertexShaderPath);
    const char* vertexShaderSource = vertexShaderCode.c_str();

    // 编译顶点着色器
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 检查顶点着色器编译错误
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
        std::cout << "Failed to compile vertex shader:\n" << infoLog << std::endl;
        return 0;
    }

    // 读取片段着色器文件内容
    std::string fragmentShaderCode = readShaderFile(fragmentShaderPath);
    const char* fragmentShaderSource = fragmentShaderCode.c_str();

    // 编译片段着色器
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 检查片段着色器编译错误
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
        std::cout << "Failed to compile fragment shader:\n" << infoLog << std::endl;
        return 0;
    }

    // 链接着色器程序
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // 检查着色器程序链接错误
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
        std::cout << "Failed to link shader program:\n" << infoLog << std::endl;
        return 0;
    }

    // 删除已编译的顶点着色器和片段着色器,因为它们已经链接到程序中
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    return shaderProgram;
}

main函数中,可以调用createShaderProgram函数来创建着色器程序,例如:

unsigned int shaderProgram = createShaderProgram("vertexShader.glsl", "fragmentShader.glsl");

这里假设顶点着色器文件名为vertexShader.glsl,片段着色器文件名为fragmentShader.glsl

3. 设置顶点数据和缓冲

  • 定义顶点数据结构
    • 这里简单定义一个立方体的顶点数据结构,包含位置和颜色信息。
struct Vertex {
    glm::vec3 position;
    glm::vec3 color;
};
  • 设置立方体顶点数据
    • 创建一个数组来存储立方体的顶点数据。
Vertex vertices[] = {
    // 前面
    {glm::vec3(-0.5f, -0.5f,  0.5f), glm::vec3(1.0f, 0.0f, 0.0f)},
    {glm::vec3( 0.5f, -0.5f,  0.5f), glm::vec3(0.0f, 1.0f, 0.0f)},
    {glm::vec3( 0.5f,  0.5f,  0.5f), glm::vec3(0.0f, 0.0f, 1.0f)},
    {glm::vec3(-0.5f,  0.5f,  0.5f), glm::vec3(1.0f, 1.0f, 0.0f)},

    // 后面
    {glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 0.0f, 0.0f)},
    {glm::vec3( 0.5f, -0.5f, -0.5f), glm::vec3(0.0f, 1.0f, 0.0f)},
    {glm::vec3( 0.5f,  0.5f, -0.5f), glm::vec3(0.0f, 0.0f, 1.0f)},
    {glm::vec3(-0.5f,  0.5f, -0.5f), glm::vec3(1.0f, 1.0f, 0.0f)},

    // 左面
    {glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 0.0f, 0.0f)},
    {glm::vec3(-0.5f, -0.5f,  0.5f), glm::vec3(0.0f, 1.0f, 0.0f)},
    {glm::vec3(-0.5f,  0.5f,  0.5f), glm::vec3(0.0f, 0.0f, 1.0f)},
    {glm::vec3(-0.5f,  0.5f, -0.5f), glm::vec3(1.0f, 1.0f, 0.0f)},

    // 右面
    {glm::vec3( 0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 0.0f, 0.0f)},
    {glm::vec3( 0.5f, -0.5f,  0.5f), glm::vec3(0.0f, 1.0f, 0.0f)},
    {glm::vec3( 0.5f,  0.5f,  0.5f), glm::vec3(0.0f, 0.0f, 1.0f)},
    {glm::vec3( 0.5f,  0.5f, -0.5f), glm::vec3(1.0f, 1.0f, 0.0f)},

    // 上面
    {glm::vec3(-0.5f,  0.5f,  0.5f), glm::vec3(1.0f, 0.0f, 0.0f)},
    {glm::vec3( 0.5f,  0.5f,  0.5f), glm::vec3(0.0f, 1.0f, 0.0f)},
    {glm::vec3( 0.5f,  0.5f, -0.5f), glm::vec3(0.0f, 0.0f, 1.0f)},
    {glm::vec3(-0.5f,  0.5f, -0.5f), glm::vec3(1.0f, 1.0f, 0.0f)},

    // 下面
    {glm::vec3(-0.5f, -0.5f,  0.5f), glm::vec3(1.0f, 0.0f, 0.0f)},
    {glm::vec3( 0.5f, -0.5f,  0.5f), glm::0f, 1.0f, 0.0f)},
    {glm::vec3( 0.5f, -0.5f, -0.5f), glm::vec3(0.0f, 0.0f, 1.0f)},
    {glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 1.0f, 0.0f)}
};
  • 创建顶点缓冲对象(VBO)和顶点数组对象(VAO)
    • 使用OpenGL函数创建VBO和VAO,并将顶点数据绑定到它们上面。
unsigned int VBO, VAO;

glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

glBindVertexArray(VAO);

glBindBuffer(GL_VERTEX_BUFFER, VBO);
glBufferData(GL_VERTEX_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// 绑定顶点位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
glEnableVertexAttribArray(0);

// 绑定顶点颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color));
glEnableVertexAttribArray(1);

glBindVertexArray(0);

4. 处理鼠标事件和计算旋转矩阵

  • 定义全局变量
    • 定义用于存储鼠标位置、旋转矩阵等的全局变量。
// 鼠标按下时的坐标
glm::vec2 mousePosInitial;
// 旋转矩阵
glm::mat4 rotationMatrix = glm::mat4(1.0f);
  • 鼠标按下事件处理
    • 当鼠标按下时,记录鼠标的初始位置。
void mousePressed(GLFWwindow* window, int button, int state, int x, int y) {
    if (button == GLFW_LEFT_BUTTON && state == GLFW_DOWN) {
        mousePosInitial.x = x;
        mousePosInitial.y = y;
    }
}
  • 鼠标移动事件处理
    • 当鼠标在按下状态下移动时,根据鼠标的初始位置和当前位置计算旋转轴和旋转角度,然后更新旋转矩阵。
void mouseMoved(GLFWwindow* window, int x, int y) {
    if (glfwGetMouseButton(window, GLFW_LEFT_BUTTON) == GLFW_PRESS) {
        glm::vec2 mousePosCurrent(x, y);

        // 计算鼠标移动的偏移量
        glm::vec2 mouseDelta = mousePosCurrent - mousePosInitial;

        // 计算旋转角度
        float angle = glm::length(mouseDelta) * 0.01f;

        // 计算旋转轴
        glm::vec3 axis = glm::vec3(mouseDelta.y, -mouseDelta.x, 0.0f);

        // 规范化旋转轴向量
        axis = glm::normalize(axis);

        // 创建临时旋转矩阵
        glm::mat4 tempMatrix = glm::rotate(rotationMatrix, angle, axis);

        // 更新旋转矩阵
        rotationMatrix = tempMatrix;

        // 重新渲染场景以显示旋转后的效果
        glfwPostRedraw(window);
    }
}

5. 渲染场景

  • 设置视图和投影矩阵
    • 在渲染场景之前,需要设置视图矩阵和投影矩阵。
void drawScene() {
    // 清除颜色缓冲和深度缓冲
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置视图矩阵
    glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

    // 设置投影矩阵
    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WIDTH / (float)HEIGHT, 0.1f, 100.0f);

    // 获取着色器程序中的uniform变量位置
    int viewLoc = glGetUniformLocation(shaderProgram, "view");
    int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
    int rotationLoc = glGetUniformLocation(shaderProgram, "rotation");

    // 上传视图矩阵到着色器程序
    glUniformMatrix4f(viewLoc, 1, GL_FALSE, glm::value_ptr(view));

    // 上传投影矩阵到着色器程序
    glUniformMatrix4f(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

    // 上传旋转矩阵到着色器程序
    glUniformMatrix4f(rotationLoc, 1, GL_FALSE, glm::value_ptr(rotationMatrix));

    // 绑定顶点数组对象
    glBindVertexArray(VAO);

    // 绘制立方体
    glDrawArrays(GL_TRIANGLES, 0, 36);

    // 解除顶点数组对象的绑定
    glBindVertexArray(0);
}

6. 主函数整合

  • 在主函数中,需要注册鼠标事件回调函数,设置主循环来处理渲染和事件处理逻辑。
int main(int argc, char** argv) {
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL Trackball with GLSL and GLM", 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;
        glfwTerminate();
        return -1;
    }

    unsigned int

标签:GLSL,1.0,glm,int,0.0,0.5,vec3,trackball,GLM
From: https://www.cnblogs.com/DesertCactus/p/18537097

相关文章

  • OpenGL 如何实现 trackball
    在OpenGL中实现trackball(轨迹球)功能可以让用户通过鼠标操作来旋转场景中的物体,就好像在操作一个虚拟的轨迹球一样。以下是一种常见的实现方式的步骤:基本原理Trackball的基本思想是将二维的鼠标移动映射到三维空间中的旋转操作。当用户在屏幕上按下鼠标并移动时,根据鼠标的起......
  • Performance Improvements in .NET 9 [翻译 by chatglm]
    .NET9中的性能提升StephenToub-MSFT合作伙伴软件工程师目录基准测试设置即时编译(JIT)性能优化编译(PGO)层级0循环边界检查Arm64ARMSVEAVX1.0AVX512向量化分支写屏障对象栈分配内联垃圾回收(GC)虚拟机(VM)Mono本地AOT编译多线程反射数值计算基本类型Big......
  • 开源模型应用落地-glm模型小试-glm-4-9b-chat-tools使用(五)
    一、前言  GLM-4是智谱AI团队于2024年1月16日发布的基座大模型,旨在自动理解和规划用户的复杂指令,并能调用网页浏览器。其功能包括数据分析、图表创建、PPT生成等,支持128K的上下文窗口,使其在长文本处理和精度召回方面表现优异,且在中文对齐能力上超过GPT-4。与之前的GLM系列......
  • WebRL:让 AutoGLM 自我进化
    WebRL:让AutoGLM自我进化智谱技术团队 GLM大模型 2024年11月06日18:44 北京......
  • ChatGLM3-6B模型分析
    ChatGLM3是清华、智谱2023年开源的一款大模型。ChatGLM3-6B模型代码,目前还在研读中,尚未全部读完。图1为ChatGLM3-6B模型简图,其结构基于TransformerEncoder架构的Encoder,大体上与BERT架构类似。ChatGLM3实现模型架构时,已预置支持P-tuningv2微调结构,图7中的PrefixEncoder......
  • GLM - 4 - Plus:智谱 AI 最新推出的大型基座模型
    目录引言一、GLM-4-Plus简介二、GLM-4-Plus的技术优势1.卓越的语言能力2.多模态交互能力3.强大的推理能力三、GLM-4-Plus的功能特点1.丰富的知识储备2.准确的语言理解3.代码辅助能力四、GLM-4-Plus的应用场景1.智能客服2.内容创作3.教育辅助4.智能办公......
  • 重采样方法(交叉验证法)——基于glm与LOOCV法(Weekly数据集分析)
    Chapter5:Exercise7读取数据集Weekly数据集通常指的是在统计、数据分析或机器学习领域中,一个以周为单位进行记录的数据集合。以下是对Weekly数据集的一个详细介绍:一、数据来源与背景Weekly数据集可能来源于多个领域,如金融、经济、市场营销等,这些领域通常需要按周跟踪......
  • AutoGLM:智谱 AI 推出模拟人类操作手机的 AI 产品,内附官方演示和申请通道!
    ❤️如果你也关注大模型与AI的发展现状,且对大模型应用开发非常感兴趣,我会快速跟你分享最新的感兴趣的AI应用和热点信息,也会不定期分享自己的想法和开源实例,欢迎关注我哦!......
  • GLM-4v-9B-源码解析-五-
    GLM-4v-9B源码解析(五)license:otherlicense_name:glm-4license_link:https://huggingface.co/THUDM/glm-4v-9b/blob/main/LICENSElanguage:zhentags:glmchatglmthudminference:falseGLM-4V-9B源码解析ReadthisinEnglish2024/08/12,本仓库代码已更新并使......
  • GLM-4v-9B-源码解析-四-
    GLM-4v-9B源码解析(四)GLM-4-9BChatdialoguemodelfine-tuningInthisdemo,youwillexperiencehowtofine-tunetheGLM-4-9B-Chatopensourcemodel(visualunderstandingmodelisnotsupported).Pleasestrictlyfollowthestepsinthedocumenttoavoidunne......