本文根据教程:ogldev进行扩充学习,一步步从零开始,记录学习历程
一、透视投影
1.1 透视投影定义
中心投影法:
- 光线照射物体时,可在预设的地面或墙面上产生影子,根据这一自然现象,经科学的抽象总结,产生了投影法。
- 投射线通过物体,向选定的面投射,并在该面上得到图形的方法称为投影法
- 投射线汇交于投射中心的投影法叫做中心投影法
中心投影法示意图:
透视投影是用中心投影法将形体投射到投影面上,从而获得的一种较为接近视觉效果的单面投影图
透视投影符合人们心理习惯,即离视点近的物体大,离视点远的物体小,远到极点即为消失,成为灭点
透视投影示意图:
1.2 视锥体
当使用投影矩阵,场景中的并行线会在屏幕上的一个消失点处汇聚到一起,物体离得越远,则变得越小。看到的空间区域被称为视椎体。
通过视椎体投影:
1.3 必要的参数
90°视野和45°视野:
屏幕的宽高比: 因为我们在一个宽高相等(-1到1)的单位化窗口里展示坐标系,而通常电脑屏幕的宽度是大于高度的(比如1024*768),所以需要在水平方向上的轴线上步骤更加密集的坐标点,竖直方向上相对稀疏。
- 到远处和近处的距离:需要把离相机太近和太远的物体裁掉
1.4 透视投影矩阵
正如我们之前教程对要绘制的图形进行平移、旋转和缩放操作时,则用平移矩阵、旋转矩阵和缩放矩阵跟图形的位置坐标进行相乘即可得到变换后的图形坐标。(可以点击链接进入教程学习)
OpenGL学习之路6—-平移,旋转和缩放变换
我们对图形进行一个透视投影操作,也其实就是找到透视投影矩阵跟我们的图形位置相乘得到透视投影后的图形位置。
如果想了解投影矩阵的详细推导步骤请移步原教程
Perspective Projection
这里直接给出透视投影矩阵并稍作解释
- a: 相当于焦距,大小为 1/tan(视野/2)
即当视野为90°,则a=1/tan(90°/2)=1
- aspect:屏幕的宽高比,宽度/高度
- f:到远处平面的距离,必须正值且大于到近处平面的距离
- n:到近处平面的距离,必须是正值
1.5 代码中得到一个透视投影矩阵
struct PersProjInfo
{
float FOV;
float Width;
float Height;
float zNear;
float zFar;
};
inline void PersProjectionMatrix44(Matrix44f m, PersProjInfo p)
{
const float ar = p.Width / p.Height;
const float zRange = p.zNear - p.zFar;
const float tanHalfFOV = tanf(DegToRad(p.FOV / 2.0f));
m[0] = 1.0f / (tanHalfFOV * ar); m[4] = 0.0f; m[8] = 0.0f; m[12] = 0.0;
m[1] = 0.0f; m[5] = 1.0f / tanHalfFOV; m[9] = 0.0f; m[13] = 0.0;
m[2] = 0.0f; m[6] = 0.0f; m[10] = (-p.zNear - p.zFar) / zRange; m[14] = 2.0f*p.zFar*p.zNear / zRange;
m[3] = 0.0f; m[7] = 0.0f; m[11] = 1.0f; m[15] = 0.0;
}
- 设置一个结构体用来存放透视投影的配置参数,分别是视野(角度)、屏幕宽度、屏幕高度、到近处平面的距离和到远处平面的距离
- 通过一个内联函数PersProjectionMatrix44()传入要设置的透视投影矩阵和配置参数结构体实例,设置相应参数的透视投影的值
二、代码解释
2.1 增加管线类的相关代码
opengl_pipeline.h:
#ifndef __OPENGL_PIPELINE_H
#define __OPENGL_PIPELINE_H
#include "opengl_math.h"
class Pipeline
{
private:
Vector3f m_scale;
Vector3f m_worldPos;
Vector3f m_rotateInfo;
Matrix44f m_Wtransformation;
Matrix44f m_ProjTransformation;
Matrix44f m_WPtransformation;
PersProjInfo m_persProjInfo;
public:
Pipeline() {
LoadVector3(m_scale,1.0f, 1.0f, 1.0f);
LoadVector3(m_worldPos,0.0f, 0.0f, 0.0f);
LoadVector3(m_rotateInfo,0.0f, 0.0f, 0.0f);
}
void Scale(float ScaleX, float ScaleY, float ScaleZ)
{
LoadVector3(m_scale, ScaleX, ScaleY, ScaleZ);
}
void WorldPos(float x, float y, float z)
{
LoadVector3(m_worldPos, x, y, z);
}
void Rotate(float RotateX, float RotateY, float RotateZ)
{
LoadVector3(m_rotateInfo, RotateX, RotateY, RotateZ);
}
void SetPerspectiveProj(const PersProjInfo& p)
{
m_persProjInfo = p;
}
const Matrix44f* GetWorldTrans();
const Matrix44f* GetProjTrans();
const Matrix44f* GetWPTrans();
};