OpenGL笔记十七之正交投影变换实验-glm::ortho函数
—— 2024-07-30 晚上
bilibili赵新政老师的教程看后笔记
code review!
文章目录
- OpenGL笔记十七之正交投影变换实验-glm::ortho函数
-
- 1.glm::ortho函数
-
- 参数详解
- 返回值
- 工作原理
- 正交投影矩阵公式
- 示例代码
- 输出结果
- 解释
- 2.实验一:使用glm的ortho函数
- 2.实验二:使用非NDC数据
- 3.实验三:将顶点向世界坐标系的-z方向推进较大范围(-5)(剪裁)
- 4.实验四:将可视范围盒子向相机坐标系的+x方向推进1个单位
- 5.实验五:保持可视范围盒子不动,动相机(1.0,0.0,0.1)
- 5.实验六:保持可视范围盒子不动,动相机(1.0,0.0,1.0-剪裁)
- 6.vs
- 7.fs
- 8.main.cpp
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
);
参数详解
返回值
glm::ortho 返回一个 glm::mat4 类型的 4×4 正交投影矩阵。
工作原理
正交投影矩阵用于将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;
}
评论前必须登录!
注册