1.理解变换
变换 | 应用 |
---|---|
视图 | 指定观察者或照相机的位置 |
模型 | 在场景中移动物体 |
模型视图 | 描述视图和模型变换的二元性 |
投影 | 改变视景体的大小或重新设置它的形状 |
视口 | 这是一种伪变换,只是对窗口上的最终输出进行缩放 |
-
视图变换
在默认情况下,透视投影中的观察点位于原点(0,0,0),并沿着z轴的负方向进行观察(向显示器内部“看进去”)。观察点相对于视觉坐标系进行移动,来提供有利位置。当观察点位于原点(0,0,0)时,就像在透视投影中一样,绘制在z坐标为正的位置的对象则位于观察者背后。 -
模型变换
平移.png
模型变换用于操纵模型和其中的特定对象。这些变换将对象移动到需要的位置,然后再对它们进行旋转和缩放。
模型变换效果展示:
平移
旋转
旋转.png
缩放
缩放.png
先旋转后平移
先旋转后平移.png
先平移后旋转
先平移后旋转.png
对比上面2个变换,我们可以发现:场景或对象的最终外观可能很大程度上取决于应用的模型变换顺序。在平移和旋转上尤其如此。 -
投影变换
投影变换指定一个完成的场景(所有模型变换都已完成)是如何投影到屏幕上的最终图像。有正投影和透视投影两种变换方式。 -
视口变换
当所有变换都完成之后就得到了一个场景的二维投影,它将映射到屏幕上某处的窗口上。这种到物理窗口坐标的映射是我们最后要做的变换,称为视口变换。视口变换会将“规范化”设备坐标重新映射到窗口坐标上。
2.模型视图矩阵
模型视图矩阵是一个4*4矩阵,它表示一个变换后的坐标系,我们可以用来放置对象和确定对象的方向。我们为图元提供的顶点将作为一个单列矩阵(也就是一个向量)的形式来使用,并乘以一个模型视图矩阵来获得一个相对于视觉坐标系的经过变换的新坐标。
如下所示:一个包含顶点数据的矩阵乘以模型视图矩阵后得到新的视觉坐标。
┏ X ┓ ┏ X1 ┓
┃ Y ┃ ┏ 4 x 4 ┓ = ┃ Y1 ┃
┃ Z ┃ ┗ M ┛ ┃ Z1 ┃
┗ W ┛ ┗ W1 ┛
2.1 单位矩阵
如下图所示,单位矩阵中除了对角线上的一组元素之外,其他元素均为0.
单位矩阵.png
使用单位矩阵绘制的对象不会发生变换,他们还在原点,并且x轴、y轴、z轴与视觉坐标中一样。同样,单位着色器也完全不会对顶点做任何改变,我们可以将顶点乘以单位矩阵,但是这是一种毫无意义的操作。
2.2 平移
一个平移矩阵仅仅是将我们的顶点沿着3个坐标轴中的一个或多个进行平移。
我们可以调用math3d
库中的m3dTranslationMatrix44
函数来使用变换矩阵。
M3DMatrix44f m;
void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);
2.3 旋转
我们可以调用math3d
库中的m3dRotationMatrix44
函数来使用变换矩阵。
M3DMatrix44f m;
void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z);
2.4 缩放
我们要讲的最后一个矩阵是缩放矩阵。缩放矩阵可以沿着3个坐标轴方向,按照指定因子放大或缩小所有顶点,以改变对象大小。使用math3d
库创建一个缩放矩阵,方法与创建平移和旋转矩阵的方法类似。
M3DMatrix44f m;
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale);
2.5 综合变换
math3d
库函数m3dMatrixMultiply44
用来将两个矩阵相乘并返回运算结果。
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
3.投影矩阵
3.1 正投影
正投影用于平面图形
3.2 透视投影
透视投影用于3D图形,可以控制图形趋于人体眼睛看到事物一样
-
例如:下图透视投影中两个球的位置,从观察者看过去是一个前一个后,这种不需要开发者自己控制,OpenGL会计算好呈现出效果,是由投影矩阵来决定的
正投影和透视投影.png
3.3 模型视图投影矩阵
下面将以绘制一个在屏幕中间旋转的线框花托为例介绍模型视图投影矩阵。
定义全局变量
我们可以使用math3d
库或GLFrustum
类来创建一个投影矩阵。初始化GLFrustum
实例viewFrustum
,我们使用GLFrustum
类的一个叫做viewFrustum
的实例来为渲染设置一个透视投影矩阵。
GLFrustum viewFrustum;
我们使用GLFrustum类来设置透视投影。
/**
参数1:从顶点方向看去的视场角度(用角度值表示)
参数2:宽度和高度的比值
参数3:视角到近裁剪面距离为fNear
参数4:视角到远裁剪面距离为fFar
*/
GLFrustum::SetPerspective(float fFov, float fAspect, float fNear, float fFar);
窗口大小改变回调函数changeSize
ChangeSize
函数展示了我们是如何设置视口和投影矩阵的。
void ChangeSize(int w, int h)
{
//1.防止h变为0
if(h == 0)
h = 1;
//2.设置视口窗口尺寸
glViewport(0, 0, w, h);
//3.setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)
// 我们使用GLFrustum类的一个叫做viewFrustum的实例来为其渲染一个透视投影矩阵
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
}
渲染回调函数RenderScene
//渲染场景
void RenderScene()
{
//1.定义一个使用时间的对象
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//2.清除窗口和深度缓冲区
//可以给学员演示一下不清空颜色/深度缓冲区时.渲染会造成什么问题. 残留数据
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//3.矩阵变量
/*
mTranslate: 平移矩阵
mRotate: 旋转矩阵
mModelView: 模型视图矩阵
mModelViewProjection: 模型视图投影MVP
*/
M3DMatrix44f mTranslate, mRotate, mModelView, mModelViewProjection;
//4.操作矩阵
//创建平移矩阵
m3dTranslationMatrix44(mTranslate,0.0f,0.0f,-8.0f);
//创建旋转矩阵
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f,1.0f,0.0f);
//模型矩阵 = 旋转矩阵 * 平移矩阵
m3dMatrixMultiply44(mModelView, mTranslate, mRotate);
//模型视图矩阵 = 视图矩阵 * 模型矩阵
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(), mModelView);
//5.设置绘图颜色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//6.
//使用平面着色器
//参数1:平面着色器
//参数2:模型视图投影矩阵
//参数3:颜色
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vRed);
//7.绘制
torusBatch.Draw();
//8.交换缓存区
glutSwapBuffers();
//9.重新绘制
glutPostRedisplay();
}
函数中,我们创建了4个4*4矩阵变量。mTranslate变量保存初始变换,这是将花托沿着z轴负方向移动8个单位长度。
m3dTranslationMatrix44(mTranslate,0.0f,0.0f,-8.0f);
然后创建一个旋转矩阵,并将它保存在mRotate中。
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f,1.0f,0.0f);
请注意我们是如何使用CStopWatch
类(GLTools库的一个组成部分)来基于经过的时间长短设置旋转速度的。一开始将旋转速度设在了60°。我们应该总是根据经过的时间来设置动画率,而不是采用单纯的基于帧的方式。例如像下面这样编写动画代码是很有吸引力的。
static GLfloat yRot = 0;
yRot += 1.0f;
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f,1.0f,0.0f);
这样的代码会是对象在帧速率低时旋转得很慢,而在帧速率高时旋转得很快,所以程序员会倾向于改变加到yRot的数字,直到动画看起来正常为止。然而,随着机器、驱动程序版本等因素的改变,帧速率也将随之改变,这将在不同的机器上产生不可预料的动画速率。但是,时间则是以恒定速度流动的,不管帧速率如何。更高的帧速率应该会产生更平滑的动画,而不是更快的动画。
现在让我们回到变换花托的任务上来——我们要进行下一步工作是通过一次矩阵乘法操作来同时添加平移和旋转。请记住,操作顺序非常重要,而在本例中先进行平移,然后再进行旋转。
m3dMatrixMultiply44(mModelView, mTranslate, mRotate);
现在,这个花托应该呈现在我们面前,并在正确的位置上旋转了,至少对于模型视图矩阵来说是这样的。不过,不要忘记为了想要的坐标系而设置了一个透视投影矩阵。现在我们要讲这个坐标系缩减到单元正方体范围,通过用投影矩阵乘以模型视图矩阵来完成这项工作。再次强调,操作的顺序非常重要!
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(), mModelView);
结果得到的矩阵mModelViewProjection
包含所有变换和到屏幕的投影的串联形式。最后一步是将我们的矩阵传递到平面着色器并提交花托属性。平面着色器的工作只是使用提供的矩阵来对顶点进行转换(这是通过向量与矩阵的乘法来完成的),并且使用指定的颜色对几何图形进行着色以得到实心几何图形。在本例中使用的是红色。
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vRed);
torusBatch.Draw();
网友评论