OpenGL空间坐标变换

首先需要了解矩阵,程序里经常用矩阵做坐标变换
常见的矩阵有:

1.缩放矩阵
在这里插入图片描述


2.位移矩阵
在这里插入图片描述
多一维坐标:齐次坐标(Homogeneous Coordinates)
用途:
1.允许我们在3D向量上进行位移(如果没有w分量我们是不能位移向量的)
2.把x、y和z坐标分别除以w坐标,用w值创建3D视觉效果

如果一个向量的齐次坐标是0,这个坐标就是方向向量(Direction Vector)


3.旋转矩阵

不同轴向的旋转矩阵
在这里插入图片描述
任意轴旋转的复合矩阵
在这里插入图片描述

.

注意:不同的矩阵可以组合,比如:缩放+旋转,但是要注意顺序
在这里插入图片描述


OpenGL如何使用矩阵呢?
使用库: OpenGL Mathematics (GLM)
传送门

矩阵的用法:
详情见代码注释

//引入相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

//矩阵定义,默认里面的数据都是非法数据,需要初始化
glm::mat4 trans;

//初始化方式1
// trans = mat4(1.0f);

//mat4中4行4列的元素是按照列的方式排列的
//	主要是因为opengl在显存中是按列存储的,所以在准备好数据后可以直接将mat4复制到显存中
//可以使用[]操作符来访问mat4的第几列,并使用二级[]来访问某一列的第几个元素,

//初始化方式2,熟悉内存分布
trans[0][0] = 1.0f; trans[1][0] = 0.0f; trans[2][0] = 0.0f; trans[3][0] = 0.0f;
trans[0][1] = 0.0f; trans[1][1] = 1.0f; trans[2][1] = 0.0f; trans[3][1] = 0.0f;
trans[0][2] = 0.0f; trans[1][2] = 0.0f; trans[2][2] = 1.0f; trans[3][2] = 0.0f;
trans[0][3] = 0.0f; trans[1][3] = 0.0f; trans[2][3] = 0.0f; trans[3][3] = 1.0f;

//平移 1,1,0
//0.5 0.0 0.0 1.0 * 1 = 1.5
//0.0 0.5 0.0 1.0 * 0 = 1
//0.0 0.0 0.5 0.0 * 0 = 0
//0.0 0.0 0.0 1.0 * 1 = 1
trans = translate(trans, vec3(1.0f, 1.0f, 0.0f));

//旋转
//0.5 0.0 0.0 0.0 * 1 = 0.5
//0.0 0.5 0.0 0.0 * 0 = 0
//0.0 0.0 0.5 0.0 * 0 = 0
//0.0 0.0 0.0 1.0 * 1 = 1
trans = scale(trans, vec3(0.5f, 0.5f, 0.5f));

//旋转 90度
//trans = rotate(trans, radians(90.0f), vec3(0.0f, 0.0f, 1.0f));

vec4 vec(1.0f, 0.0f, 0.0f, 1.0f); //w是1是坐标,代表位移是有效的

vec = trans * vec;
std::cout << vec.x << "," << vec.y << "," << vec.z << std::endl;

//右乘,先缩放再平移
//最后输出 =====>> 1.5,1,0


一个顶点,要经历几个不同的空间,最后被转化为一个片段

不同坐标系统都有什么?
1.局部空间(Local Space,或者称为物体空间(Object Space))
2.世界空间(World Space)
3.观察空间(View Space,或者称为视觉空间(Eye Space))
4.裁剪空间(Clip Space)
5.屏幕空间(Screen Space)

如图:
在这里插入图片描述
其中需要注意的:
裁剪空间的投影矩阵分成两种:
a)正射投影矩阵(Orthographic Projection Matrix)
b)透视投影矩阵(Perspective Projection Matrix)

OpenGL希望每次顶点着色器运行后,返回的顶点是NDC空间的
但是OpenGL会自动进行透视除法(除以W)和裁剪,然后再进行视口变换(关联到屏幕分辨率上)

所以最后返回的 gl_Position,其实就是转换到上面的 裁剪空间
公式如下:
在这里插入图片描述

V代表顶点,M是矩阵
右乘的公式,从右往左看

绘制3d物体代码如何实现?
假设没有摄像机,相机视角是固定的,自己定义MVP矩阵

1.模型矩阵
包含了位移、缩放与旋转操作,它们会被应用到所有物体的顶点上,以变换到全局的世界空间

//定义model矩阵
glm::mat4 model = glm::mat4(1.0f);
//平移
model = glm::translate(model, cubePositions[i]);
//旋转
model = glm::rotate(model, time * glm::radians(20), glm::vec3(1.0f, 0.3f, 0.5f));

2.观察矩阵
在场景里面稍微往后移动,以使得物体变成可见的(也就是物体向前)
OpenGL是一个右手坐标系(Right-handed System),所以要取反

//定义view矩阵
glm::mat4 view = glm::mat4(1.0f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

3.投影矩阵
拿透视矩阵举例(正交更简单,不用透视除法)
但是也都是glm封装好的方法

//定义projection矩阵
glm::mat4 projection = glm::mat4(1.0f);
//perspective创建透视投影矩阵
//	参数1:视野FOV,默认是45,接近真实
//	参数2:宽高比
//	参数3:近裁剪面
//	参数4:远裁剪面
projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

4.传递给顶点着色器

//Shader.cpp 新增辅助函数
void Shader::setM4(const std::string &name, const glm::mat4 &m4) const
{
	auto location = glGetUniformLocation(ID, name.c_str());
	//设置矩阵值
	//	参数1:location
	//	参数2:几个矩阵
	//	参数3:是否转置
	//	参数4:矩阵数据,要求指针
	glUniformMatrix4fv(location, 1, GL_FALSE, &m4[0][0]);
}

//通过 glUniformMatrix4fv 设置矩阵
testShader.setM4("model", model);
testShader.setM4("view", view);
testShader.setM4("projection", projection);

4.shader进行坐标转换

#version 330 core

layout (location=0) in vec3 aPos;		//0是顶点位置
layout (location=1) in vec2 aTexcoord;	//2是UV坐标

uniform mat4 model; //模型矩阵
uniform mat4 view; //观察矩阵
uniform mat4 projection; //投影矩阵

out vec2 Texcoord; //返回给片段着色器的UV

void main()
{
	//通过变换矩阵右乘
	vec4 pos = projection * view * model * vec4(aPos, 1.0f);

	//返回裁剪坐标
	gl_Position = pos;

	//返回UV
	Texcoord = aTexcoord;
}

5.绘制正方形显示

//定义顶点
	float vertices[] = {
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
		 0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
	};

//通过 GL_TRIANGLES 绘制正方形
glDrawArrays(GL_TRIANGLES, 0, 36);

这样就可以绘制出


注意:
1.3D空间下需要开启深度测试
OpenGL存储它的所有深度信息于一个Z缓冲(Z-buffer)中,也被称为深度缓冲(Depth Buffer)
GLFW会自动为你生成这样一个缓冲,深度值存储在每个片段里面(作为片段的z值)
当片段想要输出它的颜色时,OpenGL会将它的深度值和z缓冲进行比较
如果当前的片段在其它片段之后,它将会被丢弃,否则将会覆盖

//开启深度测试
glEnable(GL_DEPTH_TEST);

//每次渲染循环开始时,清空深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

2.多个物体渲染的话,改变model矩阵即可


//多个立方体,定义世界空间
glm::vec3 cubePositions[] = {
	glm::vec3(0.0f,  0.0f,  0.0f),
	glm::vec3(2.0f,  5.0f, -15.0f),
	glm::vec3(-1.5f, -2.2f, -2.5f),
	glm::vec3(-3.8f, -2.0f, -12.3f),
	glm::vec3(2.4f, -0.4f, -3.5f),
	glm::vec3(-1.7f,  3.0f, -7.5f),
	glm::vec3(1.3f, -2.0f, -2.5f),
	glm::vec3(1.5f,  2.0f, -2.5f),
	glm::vec3(1.5f,  0.2f, -1.5f),
	glm::vec3(-1.3f,  1.0f, -1.5f)
};

// 伪代码
// 渲染循环中 ...

testShader.setM4("view", glm::value_ptr(view));
testShader.setM4("projection", glm::value_ptr(projection));

lBindVertexArray(VAO);
for (unsigned int i = 0; i < 10; i++)
{
	//模型矩阵
	glm::mat4 model = glm::mat4(1.0f);
	model = glm::translate(model, cubePositions[i]);

	float angle = 20.0f * i;
	model = glm::rotate(model, time * glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
	testShader.setM4("model", model);

	glDrawArrays(GL_TRIANGLES, 0, 36);
}

效果图:
在这里插入图片描述

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值