OpenGL笔记十七之正交投影变换实验-glm::ortho函数

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
);

参数详解

  1. left: 视锥体的左边界。
  2. right: 视锥体的右边界。
  3. bottom: 视锥体的下边界。
  4. top: 视锥体的上边界。
  5. zNear: 视锥体的近剪裁面。
  6. zFar: 视锥体的远剪裁面。

返回值

glm::ortho 返回一个 glm::mat4 类型的 4x4 正交投影矩阵。

工作原理

正交投影矩阵用于将3D坐标转换为2D屏幕坐标,它不会像透视投影矩阵那样产生距离缩放效果。正交投影矩阵的生成通过以下步骤实现:

  1. 缩放变换

    • 将对象的坐标从视锥体的范围 [left, right] 映射到标准化设备坐标的范围 [-1, 1]
    • 同理,将对象的坐标从 [bottom, top][zNear, zFar] 映射到 [-1, 1]
  2. 平移变换

    • 将对象的中心移到原点。

正交投影矩阵公式

正交投影矩阵的公式如下:
在这里插入图片描述

示例代码

以下是一个实际使用 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.1100-11
  • 第四行: 齐次坐标。

通过理解 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只野生的善逸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值