[toc]
OpenGL 矩阵向量
向量
一个顶点同时也是一个向量.
- 方向
- 数量
长度或模为1的 当前这个顶点为 单位向量
math3d库,有2个数据类型,能够表示⼀一个三维或者四维向量量。M3DVector3f
可以表示⼀一个三 维
向量量(x,y,z)
,⽽而M3DVector4f
则可以表示⼀一个四维向量量(x,y,z,w)
.在典型情况下,w
坐标设为1.0
。x,y,z
值通过除以w,来进⾏行行缩放。⽽而除以1.0
则本质上不不改变x,y,z
值.
typedef float M3DVector3f[3];
typedef float M3DVector4f[4];
//声明⼀一个三分量量向量量操作:
M3DVector3f vVector;
//类似,声明⼀一个四分量量的操作:
M3DVector4f vVectro= {0.0f,0.0f,1.0f,1.0f};
//声明⼀一个三分量量顶点数组,例例如⽣生成⼀一个三⻆角形
M3DVector3f vVerts[] = {
};
-0.5f,0.0f,0.0f, 0.5f,0.0f,0.0f, 0.0f,0.5f,0.0f
如图:
点乘
向量
可以进行加减运算,也可以简单的通过加减法进行缩放.
点乘
这个操作是在两个单位向量
之前进行.
点乘 返回余弦值
必须为单位向量
返回的是-1,1之间的值。它代表这个2个向量量的余弦值。
float m3dDotProduct3(const M3DVector3f u,const
M3DVector3f v);
- 点乘 返回弧度
返回2个向量量之间的弧度值。
float m3dGetAngleBetweenVector3(const M3DVector3f
u,const M3DVector3f v);
如图:
叉乘
两个向量之前的叉乘结果是另外一个向量.垂直于两个向量.
V1
和V2
叉乘得到V3
,如果V1
和V2
调换顺序,V3
将指向与原来相反的方向.
void m3dCrossProduct3(M3DVector3f result,const M3DVector3f
u ,const M3DVector3f v);
如图:
矩阵
矩阵(matrix),在数学上,矩阵只不过是一组排列在统一的行列中的数字而已.
矩阵之前可以进行乘法和加法,也可以与向量和标量相乘.用一个点(向量)乘以一个矩阵(一次变换)结果得到一个新的变换点(向量);
在math3d
库中也有这两种维度的矩阵数据.
许多矩阵都定义了一个二维矩阵作为c语言的二维数组.
OpenGL种通用使用一位数组.因为OpenGL
使用一种column-Major
(一列为主)矩阵排序的矩阵约定.
typedef float M3DMatrix33f[9];
typedef float M3DMatrix44f[16];
如图:
`
理解变换
将3D
数据被压扁
成2D
数据的处理过程叫做投影
;
在指定顶点和这些顶点出现在屏幕上直之间这段时间,可能会发生3种类型的几何变换:视图变换,模型变换和投影变换.
视图,模型,模型视图,投影,视口.
变换 | 应用 |
---|---|
视图 | 指定观察者或相机位置 |
模型 | 在场景中移动物体 |
模型视图 | 描述视图和模型变换的二元性 |
投影 | 改变视景体的大小或重新设置它的形状 |
视口 | 这种一种伪变换,只是对窗口上最终输出进行缩放 |
如图:
视觉坐标
视觉坐标
是相对于观察者的视角而言,无论可能进行何种变换
,我们都可以将它们视为绝对的
屏幕坐标.
视觉坐标
表示一个虚拟的固定坐标系,它通常作为参考系使用.
如图:
a:观察者在z
轴方向
视图变换
视图变换是应用到场景中的第一变换,它用来确定场景中的有利位置.
在默认情况下:
透视投影中
的观察点位于原点(0,0,0),并沿着z轴的负方向进行观察(向显示器内部'看进去').正投影中,观察者认为是在z轴正方向无穷远位置,能看到视景体中的任何东西.
视图变换
运行我们把观察者放在所希望的位置,并运行在任何方向上观察场景
.确定视图变换就像在场景中放置照相机让它指向某个方向
.
从全局考虑
,在应用任何其他模型变换
之前,必须先应用视图变换
.
模型变换
模型变换用于操作模型和其他特定对象
.这些变换将对对象移动到需要的位置,然后再对它们进行旋转和缩放.
- 平移 旋转 缩放
场景或对象的最终外观很大程度上取决于应用模型变换顺序
- 旋转平移 围绕原始坐标旋转
- 平移旋转 平移之后围绕新的坐标系
模型视图的二元性
实际上,视图和模型变换按照他们的内部效果和场景的最终外观来说是一样的.两者分开纯粹为了程序员方便.
将对象后移动和将参考坐标系向前移动在视觉上没有区别.
如图:
投影变换
投影变换
将在模型视图变换之后应用到顶点上.这种投影实际上定义视景并创建裁剪平面.
在正投影中,线和多边形使用平行线直接映射到2D屏幕上,这就意味着,无论物体有多远,他都会按照同样大小进行绘制,仅仅是平贴屏幕上.
透视投影中,知道模型视图变换场景,然后应用到透视投影矩阵.
视口变换
当以上都完成之后,得到一个场景的二维投影,它将被映射到屏幕上某处的窗口,这种到物理窗口表的映射就是最后的变换,视口变换
.
总结
我们生活在一个三维的世界——如果要观察一个物体,我们可以:
从不同的位置去观察它。(
视图变换
)移动或者旋转它,当然了,如果它只是计算机里面的物体,我们还可以放大或缩小它。(
模型变换
)如果把物体画下来,我们可以选择:是否需要一种“近大远小”的透视效果。另外,我们可能只希望看到物体的一部分,而不是全部(剪裁)。(
投影变换
)我们可能希望把整个看到的图形画下来,但它只占据纸张的一部分,而不是全部。(
视口变换
)
模型视图矩阵
模型视图矩阵
是一个 4x4
矩阵,它表示一个变换后的坐标系,我们可以用放置对象
和确定对象的方向
.我们为图元提供一个顶点将作为一个单列矩阵(
也就是一个向量)的形式来使用.并乘以
一个模型视图矩阵
来获得一个相对于视觉坐标系的
经过变换的新坐标.
矩阵构造
OpenGL矩阵通常是一个由16个顶点组成的单个数组.
第二种也可以,但是第一种是一种更加有效地方式.
行优先矩阵和列优先矩阵互为 转置矩阵
奥秘之处,在于这 16
个值表示空间中⼀一个特定的位置; 这4
列列中,每⼀一列列都是有4
个元素组成的向量量;
⼀个
4*4
矩阵是如何在3D
空间中表示⼀一个位置和方向的 列向量进⾏行行了特别的标注:矩阵的最后⼀行都为0
,只有最后⼀一个元素为1如果将⼀个对象所有的
顶点向量
乘以这个矩
阵,就能让整个 对象变换到空间中给定的位置和⽅向
单位矩阵
将一个向量乘以一个单位矩阵,相当于乘以1,不会发生任何改变.
- 单位矩阵构建三种方式
平移
将顶点沿着3个坐标轴中的一个或多个进行平移.
inline void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z)
旋转
我们围绕有x,y,z变量指定来进行旋转.旋转角度沿逆时针方向按照弧度计算.
m3dRotationMatrix44(m3dDegToRad(45.0), floata x, float y, float z);
缩放
缩放矩阵沿着3个坐标轴的方向按照指定因子放大或缩小.
void m3dScaleMatrix44(M3DMatrix44f m, floata xScale, float yScale, float zScale);
模型矩阵
旋转矩阵和平移矩阵
的乘积R·T
也是一个4×4的
矩阵,这个矩阵代表了一次平移变换和一次旋转变换效果的叠加;如果这个点还要进行变换,只要将新的变换矩阵按照顺序左乘这个矩阵,得到的新矩阵能够表示之前所有变换效果的叠加,将最初的点坐标左乘这个矩阵就能得到一系列变换后最终的点坐标,这个矩阵称为“模型矩阵”
。一个模型矩阵
乘以
另一个模型矩阵得到的还是一个模型矩阵
,表示先进行右侧模型矩阵代表的变换,再进行左侧模型矩阵代表的变换这一过程的效果之和,因此模型矩阵的乘法又可以认为是闭合的
。
- 模型矩阵之所以称之为“模型矩阵”,是因为该
矩阵与点的位置没有关系
,仅仅包含了一系列变换的信息。而在三维世界中,一个模型里所有的顶点往往共享同一个变换,对应同一个模型矩阵,比如抛在空中的一个木块,运转机器的一个齿轮。
视图矩阵
在模型矩阵中,我们关心的是空间中的点在经历变换后在世界坐标系下的位置。事实上,我们
更加关心空间中的点相对于观察者的位置
。最简单的方案是将观察者置于原点处,面向z轴(或x轴、y轴)正半轴
,那么空间中的点在世界坐标系下的位置就是其相对于观察者的位置
。观察者
的位置和方向会变化,看上去就好像整个世界的位置和方向发生变化了一样
,所以解决的方案很简单,将世界里的所有模型看作一个大模型,在所有模型矩阵的左侧再乘以
一个表示整个世界变换的模型矩阵,就可以了。这个表示整个世界变换的矩阵又称为“视图矩阵”
,因为他们经常一起工作,所以将视图矩阵乘以模型矩阵得到的矩阵称为“模型视图矩阵”。模型视图矩阵的作用是:乘以一个点坐标,获得一个新的点坐标,获得的点坐标表示点在世界里变换,观察者也变换后,点相对于观察者的位置
。
综合变换
将两个变换加在一起,只需将矩阵相乘,
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
运用模型视图矩阵
- 移动一个正方形的同时进行旋转
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
M3DMatrix44f mFinalTransform, mTranslationMatrix, mRotationMatrix;
//平移 xPos,yPos
m3dTranslationMatrix44(mTranslationMatrix, xPos, yPos, 0.0f);
// 每次重绘时,旋转5度
static float yRot = 0.0f;
yRot += 5.0f;
m3dRotationMatrix44(mRotationMatrix, m3dDegToRad(yRot), 0.0f, 0.0f, 1.0f);
//将旋转和移动的结果合并到mFinalTransform 中
m3dMatrixMultiply44(mFinalTransform, mTranslationMatrix, mRotationMatrix);
//将矩阵结果提交到固定着色器(平面着色器)中。
shaderManager.UseStockShader(GLT_SHADER_FLAT, mFinalTransform, vRed);
squareBatch.Draw();
// 执行缓冲区交换
glutSwapBuffers();
}
- 创建一个平移矩阵
mTranslationMatrix
- 创建一个旋转矩阵
mRotationMatrix
- 把平移矩阵和选择矩阵相乘得到
mFinalTransform
- 传入平面着色器中
平面着色器只接受一个矩阵变量,然后他会利用这些顶点乘以这个矩阵.
更多对象
一个批次类容器代表一种图形.
- 为对象创建一个世纪
GLTriangleBatch CC_Triangle;
- 告诉容器打算使用的点数
CC_Triangle.BeginMesh(200);
- 添加三角形
-
verts
:顶点数 -
vNorms
3个法线数组 -
vTexCoords
三个纹理数组
sphereBatch.AddTriangle(<#M3DVector3f *verts#>, <#M3DVector3f *vNorms#>, <#M3DVector2f *vTexCoords#>)
不用担心重复的顶点数据,
GLTriangleBatch
会帮我们优化.
- 添加完三角形 调用
end
CC_Triangle.end();
- 选择想要的着色器并调用Draw函数
CC_Triangle.Draw();
创建一个球体
-
sphereBatch
:三角形批次类对象 -
fRadius
:球体半径 -
iSlices
:从球体底部堆叠到顶部的三角形带的数量;其实球体是一圈一圈三角形带组成 -
iStacks
:围绕球体一圈排列的三角形对数
void gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
gltMakeSphere(sphereBatch, 3.0, 10, 20);
创建一个花托
-
torusBatch
,三角形批次类对象 -
majorRadius
,甜甜圈中心到外边缘的半径 -
minorRadius
,甜甜圈中心到内边缘的半径 -
numMajor
,沿着主半径的三角形数量 -
numMinor
,沿着内部较小半径的三角形数量
void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
gltMakeTorus(torusBatch, 3.0f, 0.75f, 15, 15);
创建一个圆柱或圆锥
-
cylinderBatch
,三角形批次类对象 -
baseRadius
,底部半径 -
topRadius
,头部半径 -
fLength
,圆形长度 -
numSlices
,围绕Z轴的三角形对的数量 -
numStacks
,圆柱底部堆叠到顶部圆环的三角形数量
void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
gltMakeCylinder(cylinderBatch, 2.0f, 1.0f, 3.0f, 15, 2);
gltMakeCylinder(cylinderBatch, 2.0f, 0.0f, 3.0f, 15, 2);
gltMakeCylinder(cylinderBatch, 2.0f, 2.0f, 3.0f, 15, 2);
创建一个圆盘
-
diskBatch
,三角形批次类对象 -
innerRadius
,内圆半径 -
outerRadius
,外圆半径 -
nSlices
,圆盘围绕Z轴的三角形对的数量 -
nStacks
,圆盘外网到内围的三角形数量
void gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);
gltMakeDisk(diskBatch, 1.5f, 3.0f, 13, 3)
投影矩阵
** 模型视图投影矩阵实际上是在视觉坐标系中移动图形.**
正投影
2D
- GLFrustum 创建一个正投影矩阵
void SetOrthographic(GLfloat xMin, GLfloat xMax, GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax)
透视投影
3D = 2D + 透视
- GLFrustum 创建一个透视投影
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
模型视图投影矩阵
ModelviewProjection(模型视图投影矩阵)
ModelviewProjection
= 模型视图矩阵 * 投影矩阵
在不使用管线的情况下
GLFrustum构造投影矩阵
mTranslate: 平移,mRotate: 旋转, mModelview: 模型视图
mModelViewProjection: 模型视图投影MVP构造旋转矩阵
m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
- 构造旋转矩阵
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
- 通过矩阵旋转矩阵、移动矩阵相乘,将结果添加到mModerView上
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
- 将投影矩阵乘以模型视图矩阵,将变化结果通过矩阵乘法应用到
mModelViewProjection
矩阵上
注意顺序: 投影 * 模型 != 模型 * 投影
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(),mModelview);
7.完整代码:
void RenderScene(void)
{
//清除屏幕、深度缓存区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//1.建立基于时间变化的动画
static CStopWatch rotTimer;
//当前时间 * 60s
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//2.矩阵变量
/*
mTranslate: 平移
mRotate: 旋转
mModelview: 模型视图
mModelViewProjection: 模型视图投影MVP
*/
M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
//创建一个4*4矩阵变量,将花托沿着Z轴负方向移动2.5个单位长度
m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
//创建一个4*4矩阵变量,将花托在Y轴上渲染yRot度,yRot根据经过时间设置动画帧率
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
//为mModerView 通过矩阵旋转矩阵、移动矩阵相乘,将结果添加到mModerView上
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
// 将投影矩阵乘以模型视图矩阵,将变化结果通过矩阵乘法应用到mModelViewProjection矩阵上
//注意顺序: 投影 * 模型 != 模型 * 投影
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(),mModelview);
//绘图颜色
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
//通过平面着色器提交矩阵,和颜色。
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack);
//开始绘图
torusBatch.Draw();
// 交换缓冲区,并立即刷新
glutSwapBuffers();
glutPostRedisplay();
}
变换管线
首先初始化顶点数据,然后
顶点数据
乘以模型视图矩阵
,生成变化的视觉坐标
,视觉坐标就是经过一系列变换后得到的坐标然后视觉坐标乘以
投影矩阵
会生成剪裁坐标
,剪裁坐标会将非显示数据踢掉,并且转换到单元立方体坐标中
。随后剪裁坐标通过透视除法也就是除以
w
坐标会转换成设备坐标,w
除以坐标的意义在于我们看到渲染物体的深度,对上面的剪裁坐标的点的x、y、z坐标除以它的w分量,除以w
的坐标叫做归一化设备坐标。如果w
分量大,除以w
后的点就接近(0,0,0)而在三维空间中,距离我们较远的坐标如果它的
w
分量较大,进行透视除法后,就距离原点越近,原点作为远处物体的消失点
反之亦然,就有三维场景的效果。最后
将透视得到的三元坐标经过视口变换就映射到2d
屏幕上,我们就可以看到渲染之后
的效果了
使用矩阵堆栈
矩阵存
储在堆区
,而地址是存储在栈区
,在大量进行变换的应用的场景中,就需要顶点与大量的变换矩阵进行相乘,这时候就需要大量的构造矩阵,这时候有一个便利的矩阵构造函数可以进行构造矩阵操作矩阵乘法会方便很多,在math3d
的这个类被称为GLMatrixsStack
。使用矩阵堆栈进行矩阵的
创建和操作矩阵乘法
很方便,但是我们还要方便的管理这些堆栈,就是说我们可以随时方便取到堆栈矩阵的地址,GLGeometryTransform
可以设置指针指向我们创建好的堆栈矩阵。
这个类的构造函数允许我们制定堆栈的最大的深度,默认堆栈深度为64.在初始化已经包含了单位矩阵 .
GLMatrixStack(int iStackDepth = 64) {
stackDepth = iStackDepth;
pStack = new M3DMatrix44f[iStackDepth];
stackPointer = 0;
m3dLoadIdentity44(pStack[0]);
lastError = GLT_STACK_NOERROR;
}
- 通过调用顶部载入单位矩阵
void LoadIdentity(void);
- 在堆栈的顶部载入任何矩阵
void LoadMatrix(const M3DMatrix44f mMatrix)
- 用一个矩阵
乘以
矩阵堆栈的顶部矩阵,相乘得到的结果随后将储存在堆栈的顶部
.
void MultMatrix(const M3DMatrix44f mMatrix)
- 只要用GetMatrix函数可以获得矩阵堆栈顶部的值.这个函数可以进行两次重载,以适应CLShaderManager的使用,或者仅仅获得顶部的矩阵.
void GetMatrix(M3DMatrix44f matrix, bool bRotationOnly = false)
压栈和出栈
一个矩阵的真正价值在于通过压栈操作存储一个状态,然后通过出栈恢复这个状态.
- 压入单元矩阵
void PushMatrix(void)
- 压入一个
M3DMatrix44f
类型数据
void PushMatrix(const M3DMatrix44f mMatrix)
- 压入一个
GLFrame
类
内部转为矩阵后压栈
void PushMatrix(GLFrame& frame) {
M3DMatrix44f m;
frame.GetMatrix(m);
PushMatrix(m);
}
仿射变化
GLMatrixStack
类也内建了对床架旋转,平移和缩放矩阵的支持.
- 平移
void Translate(GLfloat x, GLfloat y, GLfloat z) {
M3DMatrix44f mTemp, mScale;
m3dTranslationMatrix44(mScale, x, y, z);
m3dCopyMatrix44(mTemp, pStack[stackPointer]);
m3dMatrixMultiply44(pStack[stackPointer], mTemp, mScale);
}
- 拿到平移矩阵
- 把栈顶部copy一份
- 平移矩阵和单元矩阵相乘放在栈顶
- 旋转
void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
M3DMatrix44f mTemp, mRotate;
m3dRotationMatrix44(mRotate, float(m3dDegToRad(angle)), x, y, z);
m3dCopyMatrix44(mTemp, pStack[stackPointer]);
m3dMatrixMultiply44(pStack[stackPointer], mTemp, mRotate);
}
- 缩放
void Scalev(const M3DVector3f vScale) {
M3DMatrix44f mTemp, mScale;
m3dScaleMatrix44(mScale, vScale);
m3dCopyMatrix44(mTemp, pStack[stackPointer]);
m3dMatrixMultiply44(pStack[stackPointer], mTemp, mScale);
}
管理管线
GLGeometryTransform
跟踪记录着模型视图矩阵和投影矩阵堆栈,并快速检索模型视图投影矩阵的顶部和正规矩阵堆栈的顶部.
GLShaderManager shaderManager; //着色管理类
GLMatrixStack modelViewMatrix;//模型视图矩阵
GLMatrixStack projectionMatrix; //投影矩阵
GLFrame cameraFrame; //观察者位置
GLFrame objectFrame;//世界坐标
GLFrustum viewFrustum; //投影体
GLTriangleBatch CC_Triangle; // 批次类
GLGeometryTransform transformPipeline; //几何变换关系
-
ChangeSize
函数中,对透视投影进行设置.
- 模型视图矩阵加一个单元矩阵(可以忽略这一步)
- 初始化
GLGeometryTransform
,管道管理模型视图矩阵堆栈 和 投影矩阵堆栈
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
modelViewMatrix.LoadIdentity();
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
RenderScene
函数中,进行矩阵堆栈的操作,多边形偏移,正背面剔除,抗锯齿操作.最后,通过
GLGeometryTransform
取出GetModelViewProjectionMatrix
,交给着色器,开启多边形的渲染.最最后,变换的矩阵在堆栈顶部,要移除.
PopMatrix()
使用相机和角色进行移动
在3D场景中表示任意对象位置和方向
,可以使用4x4矩阵,但是这样有点笨拙,所以有了一种更简洁的方式表示空间的坐标和方向
.
角色有自己的变换,角色的变换不仅和全局坐标系(视觉坐标系)有关,也与其他角色有关,每个有自己的变换决战都贝晨伟自己的参考帧
.或者本地对象坐标系
.
角色帧
角色帧
也叫做观察者
,通常移动的物体称为角色
,只有角色才会有自己的变化,所以其实GLFrame
是拿来做变化用的。可以用来产生模型视图矩阵
。来产生位置的移动
。
GLframe
可以用来表示一个对象相对于坐标系的位置
和方向
。
无论是相机还是模型,都可以使用GLFrame
来表示。对任意一个使用GLFrame
来表示的物体而言,涉及到的坐标系有两个:永远不变的世界坐标系,针对于自身的物体坐标系(即绘图坐标系)。
- GLframe
class GLFrame
{
protected:
M3DVector3f vOrigin; // Where am I?
M3DVector3f vForward; // Where am I going?
M3DVector3f vUp; // Which way is up?
public:
...
}
-
vOrigin
空间中的位置 -
vForward
指向前方的向量 -
M3DVector3f
指向上方的向量
照相机管理
照相机变换这种方式在OprnGL
中其实是不存在的,只是我们为了形象的形容这种变换。如果给定照相机在坐标系中的一个位置和方向,当我们向前移动照相机就相当于整个场景向后退一样
。
照相机也是角色帧
的一种,这里是更形象的定义,就好比之前说的视图变换与模型变换。这样做的好处就是可以更方便操作矩阵变换。
- 创建一个全局照相机实例
GLFrame cameraFrame;
-
setUpRC
中将观察者坐标位置Z移动往屏幕里移动15个单位位置
表示离屏幕之间的距离
负数
,是往屏幕后面移动;正数
,往屏幕前面移动
cameraFrame.MoveForward(-15.0f);
-
RenderScene
把摄像机入栈
modelViewMatrix.PushMatrix();
//3.获取摄像头矩阵
M3DMatrix44f mCamera;
//从camereaFrame中获取矩阵到mCamera
cameraFrame.GetCameraMatrix(mCamera);
//模型视图堆栈的 矩阵与mCamera矩阵 相乘之后,存储到modelViewMatrix矩阵堆栈中
modelViewMatrix.MultMatrix(mCamera);
-
SpecialKeys
控制摄像机位置
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
cameraFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
cameraFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
cameraFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
cameraFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
glutPostRedisplay();
}
只有方向键按需,机会调用
SpecialKeys
函数,在照相机对象cameraFrame
上调用GLFrame
类成员函数,向上向下向左向右移动.