基于C++的OpenGL 04 之变换
1. 概述
本文基于C++语言,描述OpenGL的变换
前置知识可参考:
笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:
2. 导入GLM
平移、旋转、缩放等变换主要是使用变换矩阵来实现
OpenGL Mathematics(GLM)是一个基于GLSL的只有头文件的C++数学运算库
GLM的GitHub站点为:g-truc/glm: OpenGL Mathematics (GLM) (github.com)
使用GLM只需将其头文件的根目录包含到工程目录中即可
笔者这里使用的是CMake进行构建,所以只需将GLM目录使用CMake语句进行包含即可:
include_directories(lib)
- 笔者将GLM头文件的根目录放置在工程文件夹下的的
lib
目录 - Windows平台上使用VS的可以参考:OpenGL学习笔记三——引入GLM库,实现transform_绿洲守望者的博客-CSDN博客_glm库
引入头文件:
#include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp>
3. 设置变换矩阵
设置一个平移、旋转、缩放的矩阵:
glm::mat4 trans = glm::mat4(1.0f); trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f)*(float)sin(glfwGetTime())); trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f)); trans = glm::scale(trans, glm::vec3(1.0f, 1.0f, 0.0f)*(float)(sin(glfwGetTime()) * 0.5 + 0.5));
在顶点着色器中将变换矩阵与坐标结合:
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aTexCoord; out vec3 ourColor; out vec2 TexCoord; uniform mat4 transform; void main() { gl_Position = transform * vec4(aPos, 1.0); ourColor = aColor; TexCoord = aTexCoord; }
将变换矩阵输入到GPU:
glUniformMatrix4fv(glGetUniformLocation(ourShader.ID, "transform"), 1, GL_FALSE, glm::value_ptr(trans));
如果顺利的话,结果如下:
4. 完整代码
主要文件test.cpp
:
#include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> #include <math.h> #include "Shader.hpp" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> void framebuffer_size_callback(GLFWwindow *window, int width, int height); void process_input(GLFWwindow *window); unsigned int *renderInit(); void render(unsigned int shaderProgram, unsigned int VAO, unsigned int texture1, unsigned int texture2); bool checkCompile(unsigned int shader); bool checkProgram(unsigned int shaderProgram); int main() { glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); GLFWwindow *window = glfwCreateWindow(800, 600, "transformation", nullptr, nullptr); if (window == nullptr) { std::cout << "Faild to create window" << std::endl; glfwTerminate(); } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Faild to initialize glad" << std::endl; return -1; } glad_glViewport(0, 0, 800, 600); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); unsigned int *arr = renderInit(); while (!glfwWindowShouldClose(window)) { process_input(window); glm::mat4 trans = glm::mat4(1.0f); trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f)*(float)sin(glfwGetTime())); trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f)); trans = glm::scale(trans, glm::vec3(1.0f, 1.0f, 0.0f)*(float)(sin(glfwGetTime()) * 0.5 + 0.5)); glUniformMatrix4fv(glGetUniformLocation(arr[0], "transform"), 1, GL_FALSE, glm::value_ptr(trans)); // render std::cout << arr[0] << " " << arr[1] << " " << arr[2] << " " << arr[3] << " " << arr[4] << std::endl; render(arr[0], arr[1], arr[3], arr[4]); glfwSwapBuffers(window); glfwPollEvents(); } glDeleteProgram(arr[0]); glDeleteVertexArrays(1, &arr[1]); glDeleteBuffers(1, &arr[2]); glfwTerminate(); return 0; } void framebuffer_size_callback(GLFWwindow *window, int width, int height) { glViewport(0, 0, width, height); } void process_input(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } } unsigned int *renderInit() { unsigned int VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); unsigned int texture1; glGenTextures(1, &texture1); glBindTexture(GL_TEXTURE_2D, texture1); // 为当前绑定的纹理对象设置环绕、过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 加载并生成纹理 int width, height, nrChannels; unsigned char *data = stbi_load("../container.jpg", &width, &height, &nrChannels, 0); if (data) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); } else { std::cout << "Failed to load texture" << std::endl; } stbi_image_free(data); unsigned int texture2; glGenTextures(1, &texture2); glBindTexture(GL_TEXTURE_2D, texture2); // 为当前绑定的纹理对象设置环绕、过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // // 加载并生成纹理 int width2, height2, nrChannels2; stbi_set_flip_vertically_on_load(true); unsigned char *data2 = stbi_load("../awesomeface.png", &width2, &height2, &nrChannels2, 0); if (data2) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2); glGenerateMipmap(GL_TEXTURE_2D); } else { std::cout << "Failed to load texture" << std::endl; } stbi_image_free(data2); float vertices[] = { // ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 - 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下 -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下 -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上 }; unsigned int VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); unsigned int indices[] = { 0, 1, 3, // first triangle 1, 2, 3 // second triangle }; unsigned int EBO; glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(3 * sizeof(float))); glEnableVertexAttribArray(1); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(6 * sizeof(float))); glEnableVertexAttribArray(2); Shader shaderProgram = Shader("../test.vs.glsl", "../test.fs.glsl"); shaderProgram.use(); glUniform1i(glGetUniformLocation(shaderProgram.ID, "texture1"), 0); glUniform1i(glGetUniformLocation(shaderProgram.ID, "texture2"), 1); return new unsigned int[5]{shaderProgram.ID, VAO, VBO, texture1, texture2}; } void render(unsigned int shaderProgram, unsigned int VAO, unsigned int texture1, unsigned int texture2) { glClearColor(0.2, 0.3, 0.3, 1.0); glClear(GL_COLOR_BUFFER_BIT); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texture2); glUseProgram(shaderProgram); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); }
顶点着色器test.vs.glsl
:
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aTexCoord; out vec3 ourColor; out vec2 TexCoord; uniform mat4 transform; void main() { gl_Position = transform * vec4(aPos, 1.0); ourColor = aColor; TexCoord = aTexCoord; }
片段着色器test.fs.glsl
:
#version 330 core out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; uniform sampler2D texture1; uniform sampler2D texture2; void main() { FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2); }
CMake构建文件CMakeLists.txt
:
cmake_minimum_required(VERSION 3.3) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 14) project(transformation) find_package(glfw3 REQUIRED) find_package( OpenGL REQUIRED ) include_directories( ${OPENGL_INCLUDE_DIRS} lib) file(GLOB project_file glad.c Shader.hpp test.cpp) add_executable(${PROJECT_NAME} ${project_file}) target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARIES} glfw)
5. 参考资料
[1]变换 - LearnOpenGL CN (learnopengl-cn.github.io)
[2]glm/manual.md at master · g-truc/glm (github.com)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!