阅读量:0
OpenGL笔记十七之正交投影变换实验-glm::ortho函数
—— 2024-07-30 晚上
bilibili赵新政老师的教程看后笔记
code review!
文章目录
1.glm::ortho函数
glm::ortho
函数是 OpenGL 数学库 GLM (OpenGL Mathematics) 中用于生成正交投影矩阵的函数。正交投影矩阵在渲染2D场景或需要保持对象真实尺寸的3D场景时非常有用。glm::ortho
函数的定义如下:
glm::mat4 glm::ortho( float left, float right, float bottom, float top, float zNear, float zFar );
参数详解
- left: 视锥体的左边界。
- right: 视锥体的右边界。
- bottom: 视锥体的下边界。
- top: 视锥体的上边界。
- zNear: 视锥体的近剪裁面。
- zFar: 视锥体的远剪裁面。
返回值
glm::ortho
返回一个 glm::mat4
类型的 4x4 正交投影矩阵。
工作原理
正交投影矩阵用于将3D坐标转换为2D屏幕坐标,它不会像透视投影矩阵那样产生距离缩放效果。正交投影矩阵的生成通过以下步骤实现:
缩放变换:
- 将对象的坐标从视锥体的范围
[left, right]
映射到标准化设备坐标的范围[-1, 1]
。 - 同理,将对象的坐标从
[bottom, top]
和[zNear, zFar]
映射到[-1, 1]
。
- 将对象的坐标从视锥体的范围
平移变换:
- 将对象的中心移到原点。
正交投影矩阵公式
正交投影矩阵的公式如下:
示例代码
以下是一个实际使用 glm::ortho
函数的示例:
#include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <iostream> int main() { float left = -1.0f; float right = 1.0f; float bottom = -1.0f; float top = 1.0f; float zNear = 0.1f; float zFar = 100.0f; glm::mat4 orthoMatrix = glm::ortho(left, right, bottom, top, zNear, zFar); // 打印正交投影矩阵 for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { std::cout << orthoMatrix[i][j] << " "; } std::cout << std::endl; } return 0; }
输出结果
运行上述代码将生成并打印正交投影矩阵,输出如下:
1 0 0 0 0 1 0 0 0 0 -0.0200202 -1.002 0 0 0 1
解释
- 第一行和第二行: 映射 x 和 y 坐标范围从 [-1, 1] 到 [-1, 1],因为
left = -1
,right = 1
,bottom = -1
,top = 1
。 - 第三行: 映射 z 坐标范围从
0.1
到100
到-1
到1
。 - 第四行: 齐次坐标。
通过理解 glm::ortho
函数的原理和使用方法,可以方便地在 OpenGL 程序中实现正交投影,从而渲染出符合预期的2D或3D场景。
2.实验一:使用glm的ortho函数
NDC坐标
viewMatrix = glm::lookAt(glm::vec3(0.0f,0.0f,1.0f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f)); orthoMatrix = glm::ortho(-2.0f, 2.0f, -2.0f, 2.0f, 2.0f, -2.0f); float positions[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, };
运行
2.实验二:使用非NDC数据
viewMatrix = glm::lookAt(glm::vec3(0.0f,0.0f,1.0f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f)); orthoMatrix = glm::ortho(-2.0f, 2.0f, -2.0f, 2.0f, 2.0f, -2.0f); float positions[] = { -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, };
运行
3.实验三:将顶点向世界坐标系的-z方向推进较大范围(-5)(剪裁)
float positions[] = { -1.0f, 0.0f, -5.0f, 1.0f, 0.0f, -5.0f, 0.0f, 1.0f, -5.0f, };
运行
4.实验四:将可视范围盒子向相机坐标系的+x方向推进1个单位
orthoMatrix = glm::ortho(-1.0f, 3.0f, -2.0f, 2.0f, 2.0f, -2.0f); float positions[] = { -1.0f, 0.0f, -5.0f, 1.0f, 0.0f, -5.0f, 0.0f, 1.0f, -5.0f, };
运行
5.实验五:保持可视范围盒子不动,动相机(1.0,0.0,0.1)
viewMatrix = glm::lookAt(glm::vec3(1.0f,0.0f,0.1f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f)); orthoMatrix = glm::ortho(-2.0f, 2.0f, -2.0f, 2.0f, 2.0f, -2.0f); float positions[] = { -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, };
运行
5.实验六:保持可视范围盒子不动,动相机(1.0,0.0,1.0-剪裁)
viewMatrix = glm::lookAt(glm::vec3(1.0f,0.0f,1.0f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f)); orthoMatrix = glm::ortho(-2.0f, 2.0f, -2.0f, 2.0f, 2.0f, -2.0f); float positions[] = { -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, };
运行
6.vs
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aUV; out vec3 color; out vec2 uv; uniform mat4 transform; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; //aPos作为attribute(属性)传入shader //不允许更改的 void main() { vec4 position = vec4(aPos, 1.0); position = projectionMatrix * viewMatrix * transform * position; gl_Position = position; color = aColor; uv = aUV; }
7.fs
#version 330 core out vec4 FragColor; in vec3 color; in vec2 uv; uniform sampler2D sampler; void main() { FragColor = texture(sampler, uv); }
8.main.cpp
#include <iostream> #include "glframework/core.h" #include "glframework/shader.h" #include <string> #include <assert.h>//断言 #include "wrapper/checkError.h" #include "application/Application.h" #include "glframework/texture.h" /* *┌────────────────────────────────────────────────┐ *│ 目 标: 学习使用正交投影矩阵 *│ 讲 师: 赵新政(Carma Zhao) *│ 拆分目标: * -1 学会使用glm的ortho函数 (orthographic) ***ortho的数据是摄像机坐标系下*** 1.1 使用glm的ortho函数,生成了一个正交投影矩阵 此矩阵的作用是:生成一个投影盒子,将内部顶点转化到NDC坐标系 1.2 在vertexShader当中,添加了projectionMatrix的uniform变量 1.3 在每一帧渲染之前,更新projectionMatrix这个uniform * -2 学习使用非NDC数据 * 1 按照标准案例进行构建(ppt上) * 2 将顶点向世界坐标系的-z方向推进较大范围(-5)(剪裁) * 3 将可视范围盒子向相机坐标系的+x方向推进1个单位 * 4 保持可视范围盒子不动,动相机(1.0,0.0,0.1)(1.0,0.0,1.0-剪裁) * * -3 理解剪裁 *└────────────────────────────────────────────────┘ */ GLuint vao; Shader* shader = nullptr; Texture* texture = nullptr; glm::mat4 transform(1.0f); glm::mat4 viewMatrix(1.0f); glm::mat4 orthoMatrix(1.0f); void OnResize(int width, int height) { GL_CALL(glViewport(0, 0, width, height)); std::cout << "OnResize" << std::endl; } void OnKey(int key, int action, int mods) { std::cout << key << std::endl; } void prepareVAO() { //1 准备positions colors // float positions[] = { // -0.5f, -0.5f, 0.0f, // 0.5f, -0.5f, 0.0f, // 0.0f, 0.5f, 0.0f, // }; float positions[] = { -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; float colors[] = { 1.0f, 0.0f,0.0f, 0.0f, 1.0f,0.0f, 0.0f, 0.0f,1.0f, }; float uvs[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f, }; unsigned int indices[] = { 0, 1, 2, }; //2 VBO创建 GLuint posVbo, colorVbo, uvVbo; glGenBuffers(1, &posVbo); glBindBuffer(GL_ARRAY_BUFFER, posVbo); glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW); glGenBuffers(1, &colorVbo); glBindBuffer(GL_ARRAY_BUFFER, colorVbo); glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW); glGenBuffers(1, &uvVbo); glBindBuffer(GL_ARRAY_BUFFER, uvVbo); glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW); //3 EBO创建 GLuint ebo; glGenBuffers(1, &ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //4 VAO创建 glGenVertexArrays(1, &vao); glBindVertexArray(vao); //5 绑定vbo ebo 加入属性描述信息 //5.1 加入位置属性描述信息 glBindBuffer(GL_ARRAY_BUFFER, posVbo); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0); //5.2 加入颜色属性描述数据 glBindBuffer(GL_ARRAY_BUFFER, colorVbo); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0); //5.3 加入uv属性描述数据 glBindBuffer(GL_ARRAY_BUFFER, uvVbo); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0); //5.4 加入ebo到当前的vao glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBindVertexArray(0); } void prepareShader() { shader = new Shader("assets/shaders/vertex.glsl","assets/shaders/fragment.glsl"); } void prepareTexture() { texture = new Texture("assets/textures/goku.jpg", 0); } void prepareCamera() { //lookat:生成一个viewMatrix //eye:当前摄像机所在的位置 //center:当前摄像机看向的那个点 //up:穹顶向量 viewMatrix = glm::lookAt(glm::vec3(1.0f,0.0f,1.0f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f)); // viewMatrix = glm::lookAt(glm::vec3(1.0f,0.0f,0.1f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f)); // viewMatrix = glm::lookAt(glm::vec3(0.0f,0.0f,1.0f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f)); } void prepareOrtho() { orthoMatrix = glm::ortho(-2.0f, 2.0f, -2.0f, 2.0f, 2.0f, -2.0f); } void render() { //执行opengl画布清理操作 GL_CALL(glClear(GL_COLOR_BUFFER_BIT)); //绑定当前的program shader->begin(); shader->setInt("sampler", 0); shader->setMatrix4x4("transform", transform); shader->setMatrix4x4("viewMatrix", viewMatrix); shader->setMatrix4x4("projectionMatrix", orthoMatrix); //绑定当前的vao GL_CALL(glBindVertexArray(vao)); //发出绘制指令 GL_CALL(glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0)); GL_CALL(glBindVertexArray(0)); shader->end(); } int main() { if (!app->init(800, 600)) { return -1; } app->setResizeCallback(OnResize); app->setKeyBoardCallback(OnKey); //设置opengl视口以及清理颜色 GL_CALL(glViewport(0, 0, 800, 600)); GL_CALL(glClearColor(0.2f, 0.3f, 0.3f, 1.0f)); prepareShader(); prepareVAO(); prepareTexture(); prepareCamera(); prepareOrtho(); while (app->update()) { render(); } app->destroy(); return 0; }