最近在学习OpenGL,把学习的一些过程写在这里,希望与大家共同分享讨论。欢迎光临我的个人网站Orient.ren一起讨论学习。这里是我的GitHub果您喜欢,不妨点个赞?☺
坐标系统分为五大类:
局部空间(Local Space)或被称为物体空间(Object Space)
指的是单个物体的坐标系统。
世界空间(World Space)
指多个物体同时放在一个世界的坐标系统。物体坐标从局部空间变换到世界空间是由模型矩阵(Model Matrix)实现的。
模型矩阵:是一种变幻矩阵,它通过对物体进行位移、缩放、旋转来将它放置在预期的位置或朝向。
观察空间(View Space)或被称为视觉空间(Eye Space)、摄像机空间(Camera Space)
将世界空间坐标转化为用户视野前方的坐标而产生的结果。也就是从摄像机视角所观察到的空间。通常由一系列的唯一和旋转的组合来完成。
这些组合被存储在一个观察矩阵(View Matrix)里,它被用来将世界坐标变幻到观察空间坐标。
裁剪空间(Clip Space)
将规定范围之外的物体裁剪掉(不可视化)。它由投影矩阵(Projection Matrix)实现。
投影分为正射投影和透视投影。
创建正射投影矩阵可以用GLM的内置函数glm::ortho
:
/*p1、p2:指定了平截头体的左右坐标,
*p3、p4:制订了平截头体的底部和顶部,
*p5、p6:定义了近平面和远平面的距离
*/
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
创建透视投影矩阵可以用以下函数创建:
/*p1:定义了fov(视野:`Field of View`)的值,并且设置了观察空间的大小,一般为了真实效果,设置为45.0f,
*p2:设置宽高比,由视口宽除以高所得,
*p3、p4:设置了平截头体的近、远平面距离,通常按以下值设置。
*/
glm::nat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
屏幕空间(Screen Space)
屏幕空间也就是我们在屏幕上所能看见的一个窗口空间
一个物体的顶点坐标起始于局部空间(Local Space
),在这里被称为局部坐标(Local Coordinate
),之后会变味世界坐标(World Coordinate
),观察坐标(View Coordinate
),裁剪坐标(Clip Coordinate
),在最后以屏幕坐标(Screen Coordinate
)的形式结束
一个顶点坐标将会根据以下过程变换到裁剪坐标:
V(clip) = M(projection) · M(view) · M(model) · V(local)
最后顶点应该被赋值到顶点着色器中的gl_Position
, OpenGL将会自动进行透视除法和裁剪将其变化到标准化设备坐标,然后通过glViewPort
内部的参数来将标准化设备坐标映射到屏幕坐标。
3D
模型矩阵
创建一个包含位移、缩放与旋转的模型矩阵,将物体变换到全局的世界空间,下面这个模型矩阵使得物体绕x轴旋转,使它看起来像放在地上一样:
glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
观察矩阵
创建一个观察矩阵,将物体在场景里稍微后移,以使得物体变成可见的:
glm::mat4 view
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
投影矩阵
最后定义一个投影矩阵,在这里使用透视投影:
glm::mat4 projection
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);
申明uniform变换矩阵
创建变换矩阵后,将它们传入着色器。首先申明一个uniform变换矩阵然后将它乘以顶点坐标:
#version 410 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
然后将矩阵传入着色器(通常在每次的渲染迭代中进行,因为变换矩阵会经常变动):
int modelLoc = glGetUniformLocation(ourShader.ID, "model");
glUnifromMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
...
//观察矩阵和投影矩阵类似
绘制旋转的立方体
随时间旋转函数:
model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5, 1.0f, 0.0f));
使用glDrawArrays
绘制立方体,总共36个顶点。
glDrawArrays(GL_TRIANGLES, 0, 36);
开始深度测试
z缓冲也叫深度缓冲(Depth Buffer),所有的深度信息都存储在其中。OpenGL会根据深度值来判断物体是否被遮挡,若是则丢弃。这个过程称为深度测试(Depth Testing),开启它需要添加以下函数:
glEnable(GL_DEPTH_TEST);
在每次迭代前需要清除深度缓冲:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
绘制更多立方体
首先在一个glm::vec3
数组中定义10个立方体位置:
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)
};
在渲染循环当中调用glDrawArrays
10次:
for (unsigned int i = 0; i < 10; i++) {
// calculate the matrix for each object and pass it to shader before drawing
glm::mat4 model;
model = glm::translate(model, cubePositions[i]);
float angle = 20.0f * (i + 1);
model = glm::rotate(model, (float)glfwGetTime() * glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
ourShader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
最终绘制效果如下:
动图加载比较慢,请耐心等待
网友评论