定义相机
在3D渲染中,相机的本质是是模型的观察者,通过操纵相机,可以改变观察位置、观察方位,以及如何投影到屏幕上。
相机是场景的渲染通道pass的一个成员对象,可以被渲染通道的所有子对象继承。当场景中存在多个渲染通道时,每个通道可以具备自己的相机参数。
从坐标变换说起
一个模型中往往会存在一个或者多个几何,几何的顶点位置信息是定义在该几何的局部坐标系中,但最终我们需要将它的坐标变换到屏幕空间中,才能绘制到屏幕上。这种变换其实是经历了一系列空间的转换:
- 局部空间,是几何自身的局部坐标系
- 通过Model矩阵,将顶点位置信息从局部空间转换到世界空间
- 通过View矩阵,从世界空间转换到相机的观察空间,或者视觉空间
- 通过Projection矩阵,从观察空间转换到屏幕空间
- 从原始的几何文件中,我们可以获取到这些几何的Model矩阵(也可以看作transform矩阵),Model矩阵通过对几何进行平移、缩放和旋转,将它的顶点位置转换为世界坐标系中。
- 接下来将世界坐标变换为观察空间坐标,使得每个顶点都是从摄像机或者是观察者的角度进行观察。摄像机的View矩阵也是由一系列的平移、缩放和旋转变换组合而成。
- 坐标转换到观察空间后,我们需要将其投影到屏幕空间中,其实可以分为两个小步,第一步是通过Projection矩阵从观察空间坐标转换到裁剪坐标中,坐标值会被处理至标准化设备坐标的-1.0到1.0范围内,因此有些顶点不在该范围内的话,会被裁剪掉,不会出现在屏幕中。第二步则是将-1.0到1.0的范围变换到屏幕的实际坐标范围内。
Projection矩阵可以分为两种不同的形式,分别定义了不同的观察箱:正交投影矩阵和透视投影矩阵。在我们的库中目前只提供了正交投影矩阵。
- 最终得到的坐标会被送到光栅器中,转换为fragments。
我们将上述过程用矩阵公式描述:
当需要调整模型的观察角度、方位和投影时,需要改变模型所在渲染通道中camera的viewMatrix与projectionMatrix,在我们库中,将这些调整都封装到相机的各种操纵方式中,开发者既可以直接修改相机的viewMatrix与projectionMatrix,也可以直接调用操纵相机的函数接口。
定义相机
相机具备了以下几个属性:
View矩阵
View矩阵可以把世界空间的坐标变换成相对于摄像机的观察坐标,当我们要定义一个相机的View矩阵时,需要知道四个参数:
- 相机在世界空间中的位置eye,也就是一个从世界空间的原点指向相机位置的向量
- 观察方向dir,它其实是指向了从相机到渲染场景中心位置的向量的相反方向
- 一个指向相机右侧的向量right,它代表了相机观察空间的X轴的正方向
- 一个指向相机上方的向量up,代表了相机观察空间的Y轴的正方向
使用这些向量,我们就可以创建一个LookAt矩阵,也就是一个看着给定目标的观察矩阵View Matrix。
Projection矩阵
Projection矩阵的作用是把观察空间坐标转化为标准化设备坐标,这里我们只介绍正交投影矩阵,它类似于一个立方体一样的箱子,定义了一个裁剪空间,空间之外的顶点都被裁剪掉。与view矩阵一样,Projection矩阵也需要指定几个参数,在这几个参数作用下形成了Projection矩阵。
- orthoSize:裁剪范围的宽度和高度,即视野在水平方向和垂直方向的范围
- orthoMinMax:裁剪范围的近面和远面
- aspect:裁剪范围的宽度/高度的比值。
使用这些参数,我们就可以创建一个Projection矩阵。我们用left, right, bottom和top表示视野在水平方向和垂直方向的范围,near和far表示深度方向的范围。
旋转中心orbitPoint
当旋转相机时,相机会围绕着自己的orbitPoint进行旋转。您可以在自己的程序中,指定相机的旋转中心,往往这个中心需要放在物体的中心,或者您的鼠标击打到的位置。
修改View矩阵
当您已经确定了相机的位置eye,观察方向dir,指向右侧的向量right以及指向上方的向量up,我们就可以用下面这个接口直接计算出View矩阵。
//定义了结构体viewSpec,包含了相机的四个向量
ViewSpec viewSpec{
// right
std::make_shared<Vector3f>(vec3(data[0], data[1], data[2])),
// up
std::make_shared<Vector3f>(vec3(data[4], data[5], data[6])),
// dir
std::make_shared<Vector3f>(vec3(data[8], data[9], data[10])),
// eye
std::make_shared<Vector3f>(vec3(data[12], data[13], data[14]))
};
// 初始化一个view矩阵
auto viewMatrix = _mat4();
// 利用相机的四个向量,修改了view矩阵
cil::setViewSpec(matrix, viewSpec)
同理,当您已经确定了View矩阵,也可以得到相机的四个向量
cil::getViewSpec(viewMatrix)
修改Projection矩阵
我们提供了以下接口,帮助修改Projection矩阵
setOrthoSize(projectionMatrix, size):将新的裁剪范围的宽度、高度作为输入,修改projection矩阵。setOrthoMinMax(projectionMatrix, minMax):将新的裁剪范围的近远面作为输入,修改projection矩阵。setAspect(projectionMatrix, aspect):裁剪范围的高度不变,按照新的aspect值,更新宽度,修改projection矩阵。
同样地,您也可以通过get函数,获取Projection矩阵的这些参数:
getOrthoSize(projectionMatrix)getOrthoMinMax(projectionMatrix)getAspect(projectionMatrix)