006---OpenGL基础变化

[toc]

image.png

image.png

OpenGL 矩阵向量

向量

一个顶点同时也是一个向量.

  1. 方向
  2. 数量

长度或模为1的 当前这个顶点为 单位向量

math3d库,有2个数据类型,能够表示⼀一个三维或者四维向量量。M3DVector3f可以表示⼀一个三 维向量量(x,y,z),⽽而M3DVector4f则可以表示⼀一个四维向量量(x,y,z,w).在典型情况下,w坐标设为1.0x,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


如图:

image.png

点乘

向量可以进行加减运算,也可以简单的通过加减法进行缩放.
点乘这个操作是在两个单位向量之前进行.

  • 点乘 返回余弦值

  • 必须为单位向量

返回的是-1,1之间的值。它代表这个2个向量量的余弦值。


float m3dDotProduct3(const M3DVector3f u,const
M3DVector3f v);

  • 点乘 返回弧度

返回2个向量量之间的弧度值。


float m3dGetAngleBetweenVector3(const M3DVector3f
u,const M3DVector3f v);

如图:

image.png

叉乘

两个向量之前的叉乘结果是另外一个向量.垂直于两个向量.

V1V2叉乘得到V3,如果V1V2调换顺序,V3将指向与原来相反的方向.


void m3dCrossProduct3(M3DVector3f result,const M3DVector3f
u ,const M3DVector3f v);

如图:

image.png

矩阵

矩阵(matrix),在数学上,矩阵只不过是一组排列在统一的行列中的数字而已.

矩阵之前可以进行乘法和加法,也可以与向量和标量相乘.用一个点(向量)乘以一个矩阵(一次变换)结果得到一个新的变换点(向量);

math3d库中也有这两种维度的矩阵数据.

image.png

许多矩阵都定义了一个二维矩阵作为c语言的二维数组.

OpenGL种通用使用一位数组.因为OpenGL使用一种column-Major(一列为主)矩阵排序的矩阵约定.


typedef float M3DMatrix33f[9];
typedef float M3DMatrix44f[16];

如图:

image.png

`

理解变换

3D数据被压扁2D数据的处理过程叫做投影;

在指定顶点和这些顶点出现在屏幕上直之间这段时间,可能会发生3种类型的几何变换:视图变换,模型变换和投影变换.

视图,模型,模型视图,投影,视口.

变换 应用
视图 指定观察者或相机位置
模型 在场景中移动物体
模型视图 描述视图和模型变换的二元性
投影 改变视景体的大小或重新设置它的形状
视口 这种一种伪变换,只是对窗口上最终输出进行缩放

如图:

视觉坐标

视觉坐标是相对于观察者的视角而言,无论可能进行何种变换,我们都可以将它们视为绝对的屏幕坐标.

视觉坐标表示一个虚拟的固定坐标系,它通常作为参考系使用.

如图:

image.png

a:观察者在z轴方向

视图变换

视图变换是应用到场景中的第一变换,它用来确定场景中的有利位置.

在默认情况下:

  • 透视投影中的观察点位于原点(0,0,0),并沿着z轴的负方向进行观察(向显示器内部'看进去').

  • 正投影中,观察者认为是在z轴正方向无穷远位置,能看到视景体中的任何东西.

视图变换运行我们把观察者放在所希望的位置,并运行在任何方向上观察场景.确定视图变换就像在场景中放置照相机让它指向某个方向.

全局考虑,在应用任何其他模型变换之前,必须先应用视图变换.

模型变换

模型变换用于操作模型和其他特定对象.这些变换将对对象移动到需要的位置,然后再对它们进行旋转和缩放.

  • 平移 旋转 缩放
image.png

场景或对象的最终外观很大程度上取决于应用模型变换顺序

  • 旋转平移 围绕原始坐标旋转
image.png
  • 平移旋转 平移之后围绕新的坐标系
image.png

模型视图的二元性

实际上,视图和模型变换按照他们的内部效果和场景的最终外观来说是一样的.两者分开纯粹为了程序员方便.

将对象后移动和将参考坐标系向前移动在视觉上没有区别.

如图:

image.png

投影变换

投影变换将在模型视图变换之后应用到顶点上.这种投影实际上定义视景并创建裁剪平面.

在正投影中,线和多边形使用平行线直接映射到2D屏幕上,这就意味着,无论物体有多远,他都会按照同样大小进行绘制,仅仅是平贴屏幕上.

透视投影中,知道模型视图变换场景,然后应用到透视投影矩阵.

image.png

视口变换

当以上都完成之后,得到一个场景的二维投影,它将被映射到屏幕上某处的窗口,这种到物理窗口表的映射就是最后的变换,视口变换.

总结

我们生活在一个三维的世界——如果要观察一个物体,我们可以:

  • 从不同的位置去观察它。(视图变换

  • 移动或者旋转它,当然了,如果它只是计算机里面的物体,我们还可以放大或缩小它。(模型变换

  • 如果把物体画下来,我们可以选择:是否需要一种“近大远小”的透视效果。另外,我们可能只希望看到物体的一部分,而不是全部(剪裁)。(投影变换

  • 我们可能希望把整个看到的图形画下来,但它只占据纸张的一部分,而不是全部。(视口变换

模型视图矩阵

模型视图矩阵是一个 4x4 矩阵,它表示一个变换后的坐标系,我们可以用放置对象确定对象的方向.我们为图元提供一个顶点将作为一个单列矩阵(也就是一个向量)的形式来使用.并乘以一个模型视图矩阵来获得一个相对于视觉坐标系的经过变换的新坐标.

image.png

矩阵构造

OpenGL矩阵通常是一个由16个顶点组成的单个数组.

image.png

第二种也可以,但是第一种是一种更加有效地方式.

image.png

行优先矩阵和列优先矩阵互为 转置矩阵

奥秘之处,在于这 16 个值表示空间中⼀一个特定的位置; 这4列列中,每⼀一列列都是有4个元素组成的向量量;

image.png
  • ⼀个4*4矩阵是如何在3D空间中表示⼀一个位置和方向的 列向量进⾏行行了特别的标注:矩阵的最后⼀行都为0,只有最后⼀一个元素为1

  • 如果将⼀个对象所有的顶点向量 乘以这个矩阵,就能让整个 对象变换到空间中给定的位置和⽅向

单位矩阵

将一个向量乘以一个单位矩阵,相当于乘以1,不会发生任何改变.

image.png
  • 单位矩阵构建三种方式
image.png
平移

将顶点沿着3个坐标轴中的一个或多个进行平移.

image.png

inline void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z)

旋转

我们围绕有x,y,z变量指定来进行旋转.旋转角度沿逆时针方向按照弧度计算.

image.png
m3dRotationMatrix44(m3dDegToRad(45.0), floata x, float y, float z);

缩放

缩放矩阵沿着3个坐标轴的方向按照指定因子放大或缩小.

image.png

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();
}

  1. 创建一个平移矩阵 mTranslationMatrix
  2. 创建一个旋转矩阵 mRotationMatrix
  3. 把平移矩阵和选择矩阵相乘得到mFinalTransform
  4. 传入平面着色器中

平面着色器只接受一个矩阵变量,然后他会利用这些顶点乘以这个矩阵.

更多对象

一个批次类容器代表一种图形.

  • 为对象创建一个世纪

GLTriangleBatch     CC_Triangle;

  • 告诉容器打算使用的点数

CC_Triangle.BeginMesh(200);

  • 添加三角形
  1. verts:顶点数
  2. vNorms3个法线数组
  3. vTexCoords三个纹理数组

sphereBatch.AddTriangle(<#M3DVector3f *verts#>, <#M3DVector3f *vNorms#>, <#M3DVector2f *vTexCoords#>)

不用担心重复的顶点数据,GLTriangleBatch会帮我们优化.

  • 添加完三角形 调用end

CC_Triangle.end();

  • 选择想要的着色器并调用Draw函数

CC_Triangle.Draw();

创建一个球体

  1. sphereBatch:三角形批次类对象
  2. fRadius:球体半径
  3. iSlices:从球体底部堆叠到顶部的三角形带的数量;其实球体是一圈一圈三角形带组成
  4. iStacks:围绕球体一圈排列的三角形对数

void gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);

  • gltMakeSphere(sphereBatch, 3.0, 10, 20);
image.png

创建一个花托

  1. torusBatch,三角形批次类对象
  2. majorRadius,甜甜圈中心到外边缘的半径
  3. minorRadius,甜甜圈中心到内边缘的半径
  4. numMajor,沿着主半径的三角形数量
  5. numMinor,沿着内部较小半径的三角形数量

void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);

  • gltMakeTorus(torusBatch, 3.0f, 0.75f, 15, 15);
image.png

创建一个圆柱或圆锥

  1. cylinderBatch,三角形批次类对象
  2. baseRadius,底部半径
  3. topRadius,头部半径
  4. fLength,圆形长度
  5. numSlices,围绕Z轴的三角形对的数量
  6. 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);
image.png
  • gltMakeCylinder(cylinderBatch, 2.0f, 0.0f, 3.0f, 15, 2);
image.png
  • gltMakeCylinder(cylinderBatch, 2.0f, 2.0f, 3.0f, 15, 2);
image.png

创建一个圆盘

  1. diskBatch,三角形批次类对象
  2. innerRadius,内圆半径
  3. outerRadius,外圆半径
  4. nSlices,圆盘围绕Z轴的三角形对的数量
  5. nStacks,圆盘外网到内围的三角形数量

void gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);

  • gltMakeDisk(diskBatch, 1.5f, 3.0f, 13, 3)
image.png

投影矩阵

** 模型视图投影矩阵实际上是在视觉坐标系中移动图形.**

正投影

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 = 模型视图矩阵 * 投影矩阵

在不使用管线的情况下

  1. GLFrustum构造投影矩阵

  2. mTranslate: 平移,mRotate: 旋转, mModelview: 模型视图
    mModelViewProjection: 模型视图投影MVP

  3. 构造旋转矩阵

 m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
  1. 构造旋转矩阵

m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);

  1. 通过矩阵旋转矩阵、移动矩阵相乘,将结果添加到mModerView上
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);

  1. 将投影矩阵乘以模型视图矩阵,将变化结果通过矩阵乘法应用到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();
}

变换管线

image.png
  1. 首先初始化顶点数据,然后顶点数据乘以模型视图矩阵,生成变化的视觉坐标,视觉坐标就是经过一系列变换后得到的坐标

  2. 然后视觉坐标乘以投影矩阵会生成剪裁坐标,剪裁坐标会将非显示数据踢掉,并且转换到单元立方体坐标中

  3. 随后剪裁坐标通过透视除法也就是除以w坐标会转换成设备坐标,w除以坐标的意义在于我们看到渲染物体的深度,对上面的剪裁坐标的点的x、y、z坐标除以它的w分量,除以w的坐标叫做归一化设备坐标。如果w分量大,除以w后的点就接近(0,0,0)

  4. 而在三维空间中,距离我们较远的坐标如果它的w分量较大,进行透视除法后,就距离原点越近,原点作为远处物体的消失点 反之亦然,就有三维场景的效果。

  5. 最后将透视得到的三元坐标经过视口变换就映射到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);           
            }

  1. 拿到平移矩阵
  2. 把栈顶部copy一份
  3. 平移矩阵和单元矩阵相乘放在栈顶
  • 旋转

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函数中,对透视投影进行设置.
  1. 模型视图矩阵加一个单元矩阵(可以忽略这一步)
  2. 初始化 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:
    
    ...
   }

  1. vOrigin空间中的位置
  2. vForward指向前方的向量
  3. 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类成员函数,向上向下向左向右移动.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345