【OpenGL】蓝宝书第四章学习知识

目录

3D数学

向量

点乘

叉乘

矩阵

理解变换

视觉坐标

视图变换

模型变换

模型视图的二元性

扫描二维码关注公众号,回复: 12907918 查看本文章

投影变换

视口变换

模型视图矩阵

矩阵构造

单位矩阵

平移

旋转

缩放

综合变换

运用模型视图矩阵

更多对象

使用三角形批次类(GLTriangleBatch)

实例案例

投影矩阵

正投影

透视投影

模型视图投影矩阵

变换管线

使用矩阵堆栈

管理管线

使用照相机和角色进行移动

角色帧

欧拉角:“卢克!请使用帧”

照相机管理

添加更多角色

关于光线


3D数学

向量

typedef float M3DVector3f[3];

typedef float M3DVector4f[4];

声明初始化四分量:  M3DVector4f vVertex = { 0.0f, 0.0f, 0.0f, 1.0f };

三分量数组:   M3DVector3f vVerts[] = {  -0.5f, 0.0f, 0.0f,  0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f }; 

点乘

float m3dDotProduct3(const M3DVector3f u, const M3DVector3f v); 传入的是2个三维单位向量,返回结果是u和v向量夹角的余弦值[-1,1]范围。

float m3dGetAngleBetweenVectors3(const M3DVector3f u, const M3DVector3f v);  获取u和v夹角的弧度值[0,2π)

叉乘

void m3dCrossProduct3(M3DVector3f result, const M3DVector3f u, const M3DVector3f v); 两个向量u和v进行叉乘得到垂直于它们的一个新向量result。

值得注意的是,result朝向是遵循右手法则,即u->v顺序大拇指方向则为result朝向。

矩阵

矩阵用于坐标变化,例如一个点经过一系列旋转、缩放或平移操作后,我们需要知道它最终会变成什么样,这就需要矩阵进行对坐标点进行数学运算得到正确结果。

矩阵可看作一组列向量。矩阵之间可进行乘法和加法,也能与向量或者标量相乘。用一个点(向量)乘以一个矩阵(一次变换)结果得到一个新的变换点(向量)。

后续经常会用的math3d.h math3d.cpp提供了不少API来进行这些变换操作。3*3  4*4矩阵是最常用的矩阵 它们类型如下:

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

为什么矩阵看似二维却定义为一维数组,是因为OpenGL使用一种叫做Column-Major(以列为主的)矩阵排序的矩阵约定。在后续使用中可以理解。

理解变换

3D物体实际上最终会压缩为2D,这种压缩的处理过程叫“投影”(projection),目前已知有正投影和透视投影。投影只是变换中的一种,变换允许我们旋转、移动、缩放对象(图元)。

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

视觉坐标

视觉坐标是相对于观察者的视角而言的,笛卡儿坐标系在OpenGL的视觉坐标系情况为+x右,+y朝上,  -z往内延伸,+z往外延伸.

视图变换

视图变换是应用到场景中的第一种变换。确定视图变换就相当于确定摄像机的朝向和位置。

默认情况下透视投影的观察点在(0,0,0)原点并沿着-z方向进行观察;而正交投影,观察点则在z轴正方向无穷远位置,它能看到视椎体的任何东西。

因此,第一个确定的变换就应该是确定视图变换,而后续所有变换都基于它进行。

模型变换

模型变换用于操纵模型和其中的特定对象。这些变换将对象移动到相应的位置,然后再对它们进行旋转和缩放。平移和旋转的顺序会影响最终结果。

模型视图的二元性

指的是模型变换和视图变换本质上对场景的最终外观来说是一样的,指视图变换将观察点向+z移动100单位 和 模型变换将观察物体向-z移动100单位 是一样的效果,为了区分开只是为了方便程序。

模型和视图变换应用于场景中,一个物体先进行视图变换后再单独进行模型变换。术语“模型视图”是指它们的组合,即模型视图矩阵。为啥不叫视图模型呢,因为矩阵的相乘是后先乘的,即先进行视图后进行模型。

视图变换本质上还是模型变换,只是在绘制对象之前应用到一个虚拟对象(观察者)之上的一种模型变换,它是最开始进行的变换。通俗来讲就是物体初始化在观察点位置,然后进行一系列平移,旋转,缩放变换,这些变换浓缩成一个矩阵称为“视图变换”!

投影变换

它紧着模型视图变换之后应用到顶点上,注意所有变换都是应用于顶点上的。这种投影实际上定义了视椎体并创建了裁剪平面。

裁剪平面是3D空间中的平面方程式,OpenGL用它确定几何图形对于观察者来说是否可见。投影变换是指定一个已完成的场景(所有模型变换都已完成)是如何投影到屏幕上的最终图像。

正投影(平行投影)中,所有多边形都是精确地按照指定的相对大小来在屏幕上绘制的,线和多边形使用平行线来直接映射到2D屏幕上,意味着所有3D物体无论它在远处还是近处都是一个大小渲染出来,平贴在屏幕上而已。这种投影常用于渲染二维图片,例如UI。

透视投影,透视投影的特点是透视缩短(foreshortening),这种特性使得远处的物体看起来比近处同样大小的物体更小一些。两根平行的铁轨使用透视投影下,就看起来不是那么平行,而是往远处汇聚成一点。常用于3D场景的投影。

视口变换

当投影变换后,得到了一个场景的二维投影,它即将被映射到屏幕上某处的窗口上。这种到物理窗口的映射是我们最后要做的变换,成为视口变换。通常,颜色缓冲区和窗口像素之间存在一一对应关系,但情况并非一定如此,某些情况下,视口变换会将“规范化”设备坐标重新映射到窗口坐标上,但这不用我们操心,图形硬件会自动处理这件事情。
此处让我想到了,glViewport(x,y,w,h)函数指定视口,它就是进行了这一项变换。

模型视图矩阵

它是一个4*4矩阵,表示一个变换后的坐标系,可以用于防止对象和确定对象的方向。图元提供的顶点作为一个单列矩阵(也就是一个(x,y,z,w)向量)的形式来使用,并乘以一个模型视图矩阵来获得一个相对于视觉坐标系的经过变换的新坐标。

w通常为1.0,表示一个缩放因子,我们很少改动它。

[x,y,z,w] [ 4*4 M] = [Xe,Ye,Ze,We]  为什么?!

矩阵构造

之前提及到为什么矩阵被定义为一维数组,而不是二维数组,这种方式与许多数学库不同,它们都定义为二维数组。

GLfloat matrix[16];   这是OpenGL的,  GLfloat matrix[4][4]; 其他数学库定义的。

因为矩阵是按列优先排序的,而存储器中,4*4二维数组是按行优先排序的,而一维数组是按列优先排序?!是这样么?好像是因为GPU用一维数组比较好实现列优先排序,仅仅将索引+1 * 4即可跳到下一个列头遍历,而二维数组可能就固定死为行优先排序了吧???(迷惑点1)

4*4矩阵每一列的前三个元素分别为 X轴方向 、 Y轴方向 、 Z轴方向, 第四列的前三个元素为 变换位置。 第四行 前三个元素为0,最后一个为1。X、Y、Z轴方向总是90°角形成。

用一个4*4矩阵乘以一个不同坐标系的顶点位置(x,y,z,w)则得到了一个转到新坐标系下的新向量。

这里可能不太明白4*4矩阵的X、Y、Z轴方向到底指的是什么!在之前Unity Shader学习里我理解到是在新坐标系下的原坐标系X、Y、Z轴的表达方式,第四列前三位是新坐标系下的原坐标系偏移向量。
我对第四列前三位元素的理解:假设新坐标系原点仅仅是在原坐标系原点(0,0,0)偏移了(1,0,0),其他保持不变,那么新坐标系下的原坐标X、Y、Z轴表达方式,依然是(1,0,0)、(0,1,0)、(0,0,1)即4*4矩阵的前三列前三个元素。而第四列的前三位元素发生了变化,变成了(-1,0,0),原因是在新坐标下看原坐标往(-1,0,0)偏移了。什么情况会使得前三列的前三个元素变化?只有在旋转时会发生改变。想象一下新坐标系是原坐标系旋转45°角,此时的原坐标系X轴在新坐标系下的表达就肯定不是(1,0,0),而是(cos(45), sin(45), 0)。x为余弦,y为正弦旋转值,而缩放虽然也会影响前三列前三个元素,但他们只会影响第一列第一个,第二列第二个,第三列第三个元素,这个和1*4向量 与 4*4矩阵的相乘 有关,被设定成就是如此的。 即 整体缩放2倍时, 那么 前三列变成 (2, 0, 0),  (0,2,0)  , (0,0,2) 除非之前已经进行了旋转,这里我就不太理解了,还请各位自行努力理解 好告诉我~。

单位矩阵

单位矩阵 指和原向量相差还是得到原来的向量。 

M3DMatrix44f m = { 1.0f, 0.0f, 0.0f, 0.0f,     0.0f, 1.0f, 0.0f, 0.0f,    0.0f, 0.0f, 1.0f, 0.0f,   0.0f, 0.0f, 0.0f, 1.0f };

单位着色器是用单位矩阵进行对顶点变换的着色器,这将这些顶点渲染在默认的坐标系中[-1, 1]范围内,但这是一种毫无意义的操作。

平移

void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);  使用这个函数来获取一个平移(x,y,z)的变换矩阵m

旋转

m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z); 使用这个函数来获取一个围绕轴(x,y,z)向量旋转angle角度对应弧度的变换矩阵m。旋转是绕轴逆时针进行的(右手法则,大拇指与轴平行,弯曲四指为旋转朝向)

M3DMatrix44f m;
m3dRotationMatrix44(m, m3dDegToRad(45.0), 1.0f, 1.0f, 1.0f); 围绕(1,1,1)轴旋转45°,注意angle竟然传入的是弧度值!那你起名angle干嘛!!!用m3dDegToRad将角度转弧度值。

优化:可使用宏来将m3dDegToRad(45.0)替换,这样做是能将这个转换消耗仅仅发生一次在编译时。避免运行时每次都会转换而带来损耗。

缩放

M3DMatrix44f m;
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale);

沿着三个轴进行制定缩放大小得到m矩阵。可进行非等比缩放即只进行(2,2,1)对X和Y缩放2倍,Z保持不变为1。

综合变换

指的是将矩阵相乘来达到多个矩阵融合在一起的效果,例如平移*旋转矩阵 得到的是一个能进行先旋转后平移的矩阵。

void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);  a和b相乘返回product结果。

运用模型视图矩阵

在Move实例程序中,控制红色方块随着按键方向移动的做法是强制性的,即更新三角形扇的坐标,然后重建图元批次。

更好的做法是一次性创建批次(以原点为中心),然后再渲染这个批次时对顶点应用一个矩阵(模型视图矩阵),在原案例中使用的是单位着色器,它不会使用任何矩阵进行变换,我们可以用平面着色器FLAT,接收一个4*4变换矩阵(模型视图矩阵)作为参数之一,对顶点进行变换来达到移动效果。

GLshaderManager::UseStockShader(GLT_SHADER_FLAT, M3DMatrix44f m , GLfloat vColor[4]);

使用上面认识到的 m3dTranslationMatrix44(mTranslationMatrix, xPos, yPos, 0.0f); 获取M3DMatrix44f类型的mTranslationMatrix模型变换矩阵

shaderManager.UseStockManager(GLT_SHADER_FLAT, mTranslationMatrix, vRed); 进行使用平面着色器来使用模型变换矩阵对顶点进行变换,再用vRed颜色绘制。
squareBatch.Draw();

平移同时还可以进行旋转,RenderScene函数如下进行即可:

void RenderScene(void)
{
    glClear(CL_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;

    m3dTranslationMatrix44(mTranslationMatrix, xPos, yPos, 0.0f);

    static float yRot = 0.0f;
    yRot += 5.0f;
    m3dRotationMatrix44(mRotationMatrix, m3dDegToRad(yRot), 0.0f, 0.0f, 1.0f);

    shaderMatrixMultiply44(mFinalTransform, mTranslationMatrix, mRotationMatrix);
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT, mFinalTransform, vRed);
    squareBatch.Draw();
    
    glutSwapBuffers();
}

使用GLT_SHADER_FLAT只是进行了模型视图矩阵变换,之后输出到的坐标系是[-1,1]范围内的,所以它还会进行一个投影变换,后续会了解到。

更多对象

GLBatch仅仅是为了解决容纳一个顶点列表并将它们作为一个特定类型的图元批次进行渲染的。

GLTriangleBatch类是专门作为三角形容器的,每一个顶点都可以有一个表面法线,以进行光照计算和纹理坐标。它以更高效的方式组织三角形(索引顶点数组),并且将多边形存储在图形卡(顶点缓冲区)上就够了。 这个才是UnityShader所熟悉的一个类,有索引顶点数组。

使用三角形批次类(GLTriangleBatch)

GLTriangleBatch myCoolObject;
myCoolObject.BeginMesh(200); //最多打算用的顶点数200,开始创建网格。
使用 void GLTriangleBatch::AddTriangle(M3DVector3f verts[3], M3DVector3f vNorms[3], M3DVector2f vTexCoords[3])

不用担心重复顶点,它会在每次添加顶点时进行搜索重复项并对批次进行优化,对于非常大的批次来说,每次添加一个新的三角形时都会越来越慢。

myCoolObject.End(); //添加完结束

myCoolObject.Draw(); //选择想要的存储着色器并调用Draw函数绘制。第六章会介绍自己做出自己的着色器来渲染批次。

实例案例

链接如下:https://blog.csdn.net/qq_39574690/article/details/115048619

投影矩阵

默认情况是[-1,1]无论x,y,z轴的坐标系下进行模型视图变换,对顶点进行平移、旋转、缩放。如果我们需要在其他坐标系下进行的话就需要如下两个投影矩阵,实际上投影矩阵做的事情就是将我们实际上是在其他坐标系下工作的,最后会利用投影矩阵转回[-1,1]坐标系下,因为硬件唯一能接收的就是[-1,1]坐标系。

正投影

正投影的所有面都是正方形的,即逻辑宽度都是相等的。会产生一个平行投影,这种投影绘制远处和近处的相同大小物体时是一样的,绘制2D的物体能精准地绘制大小和尺寸。

实例案例:https://blog.csdn.net/qq_39574690/article/details/115050010

透视投影

适合渲染3D物体的投影,它会将远处的物体缩小 近处的物体放大。

实例案例:https://blog.csdn.net/qq_39574690/article/details/115050084

模型视图投影矩阵

结合模型、视图、投影矩阵的实例:https://blog.csdn.net/qq_39574690/article/details/115050274

变换管线

一个顶点视为[1*4]矩阵,前三个是(x,y,z)第四个是w,w一般默认为1,可能被模型视图矩阵或投影矩阵改变。首先进行的是模型视图矩阵 变换为视觉坐标,视觉坐标再乘以投影矩阵,生成裁剪坐标,裁剪坐标值是在[-1,1]单位坐标系内的,随后裁剪坐标会除以w坐标(即x,y,z除以w),得到规范化的设备坐标。w是否被改变取决于所发生的变换。透视除法将作为图元装配过程的一部分进行。最后,坐标三元组将通过视口变换被映射到2D平面上(注意是视口变换最后一步变换!)这个操作也是一个视口矩阵来完成的,但无法直接指定或修改这个矩阵,OpenGL是用glViewport来设置这个矩阵的。

总结:初始顶点数据  -> [模型视图变换] -> 变换后的视觉坐标 -> [投影矩阵] -> 裁剪坐标 -> 透视除法 -> 规范化的设备坐标[(x,y,z)/w] ->  ... -> 视口变换 -> 窗口坐标

其中还有...是啥米目前不清楚

使用矩阵堆栈

GLMatrixStack矩阵堆栈,默认深度为64.  GLMatrixStack::GLMatrixStack(int iStackDepth = 64);

可通过调用 GLMatrixStack::LoadIdentity(void); 来在顶部载入单位矩阵。

可载入任何矩阵在顶部  GLMatrixStack::LoadMatrix(const M3DMatrix44f m);

可以用一个矩阵乘以顶部矩阵并放置于顶部 GLMatrixStack::MulMatrix(const M3DMatrix44f m); 

可以获取顶部矩阵 或 仅仅获取其顶部矩阵的副本

const M3DMatrix44f GLMatrixStack::GetMatrix(void);   获取顶部矩阵
void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix); 获取顶部副本mMatrix矩阵

压栈和出栈

void GLMatrixStack::PushMatrix(void); 压栈当前矩阵值,指的是复制了当前矩阵放入栈顶。 PopMatrix则是移除顶部矩阵,恢复它下面的值。
void PushMatrix(const M3DMatrix44f mMatrix);压栈指定矩阵
void PushMatrix(GLFrame& frame);   压栈GLFrame (后续会了解到)

void GLMatrixStack:PopMatrix(void); 出栈 ,类似也有2个PopMatrix指定矩阵和GLFrame的出栈。

仿射变换: GLMatrixStack类 内建了对创建旋转、平移和缩放矩阵的支持。相应的函数列出如下:

void MatrixStack::Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
void MatrixStack::Translate(GLfloat x, GLfloat y, GLfloat z);
void MatrixStack::Scale(GLfloat x, GLfloat y, GLfloat z);

这些函数是直接创建一个对应的矩阵并和堆栈顶部矩阵相乘,相当于是追加变换,与之前我们直接用m3dMatrixMultiply44一样。

管理管线

GLGeometryTransform用来管理矩阵堆栈的,例如:
GLGeometryTransform transformPipeline; //几何变换管线
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix); 设置了模型视图矩阵堆栈和投影矩阵堆栈进去。
最后用于 shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vColor);进行直接从GLGeometryTransform拿到模型视图矩阵使用。

关于矩阵堆栈的使用:在RenderScene时,想要进行模型视图矩阵变换时 要先用 模型视图矩阵堆栈对象  modelViewMatrix.PushMatrix(); 拷贝一个堆栈顶部矩阵出来放置到顶部,保护原来的顶部矩阵元素,默认原顶部是一个单位矩阵,然后进行各种Translate,Rotate,Scale操作都只会影响到拷贝出来的顶部元素,最终在使用着色器指定了模型视图投影矩阵后并批次Draw后,就能够调用modelViewMatrix.PopMatrix()来出栈顶部元素,恢复顶部为单位矩阵以提供下一次RenderScene使用了! 实际上和我们之前做的事情差不多,它们都是拷贝了一个新的矩阵来进行变换的,只不过这里的操作就会方便不少,少写很多代码。

相关实例部分:https://blog.csdn.net/qq_39574690/article/details/115051937

使用照相机和角色进行移动

地形一般是不变的,角色是会变化的,角色一般有本地坐标系和世界坐标系的区分,本地转世界,世界转本地是常用的情况,对于非渲染相关的几何图形测试来说也是如此。每一个角色都有自己的变换,这称为参考帧。

角色帧

使用一个数据结构(C++中的类)进行表示参考帧(角色帧),这个类包含空间中的一个位置、一个指向前方的向量和一个指向上方的向量。它叫GLFrame,来自GLTools library,利用了math3d库进行组装。

class GLFrame
{
    protected:
        M3DVector3f vLocation;
        M3DVector3f vUp;
        M3DVector3f vForward;
    public:
        ... ..
};

使用这样的对象来表示位置和方向是一种牛逼的机制。
1、可使用这些数据直接创建一个4*4变换矩阵。
回顾之前讲到的矩阵的前三列前三个元素分别是x,y,z列,此处我们已经知道了vUp(y列)和vForward(z列),x列不知道,但可以通过计算y和z的叉乘cross来得到x列向量,其中第四列前三个是vLocation(平移列向量)

可能有人会感到迷惑!?为什么会是这样子的呢? 再说一遍, 矩阵前三列分别都表达的是 在世界空间坐标系下的本地空间坐标系x、y、z轴向量表达方式,第四列前三个元素是在世界坐标系下的本地坐标系空间原点的位置表达。假设本地坐标系没有进行任何的旋转和缩放(本地做标系可看成一个物体,物体中心点为原点,↑为+y轴,前方为+z轴,→为+x轴)那么前三列前三个元素为(1,0,0), (0,1,0), (0,0,1) ,如果这个本地坐标系相对于世界坐标系进行了偏移(3,0,0) 那么此时 第四列前三个元素 就是(3,0,0)! 放到Unity3D引擎上理解就非常简单,它就是相对于世界坐标原点 进行了一个平移(3,0,0) 的物体,那个物体的局部坐标系就是指本地坐标系。所有物体都是在世界坐标系原点初始化,然后乘以这个坐标系矩阵得到最终的位置和朝向结果的!而这个GLFrame就记录着 y和z列 以及 坐标信息(目前我认为是世界坐标系的)

GLFrame的GetMatrix方法 就是用于完成这个根据vLocation , vUp, vForward 得到一个矩阵的,它表示了 一个物体在世界坐标系下的变换。【注:vLocation 换为 vOrigin  新版变量名字换~】

///
		// Just assemble the matrix
        void GetMatrix(M3DMatrix44f matrix, bool bRotationOnly = false)
			{
			// Calculate the right side (x) vector, drop it right into the matrix
			M3DVector3f vXAxis;
			m3dCrossProduct3(vXAxis, vUp, vForward);

			// Set matrix column does not fill in the fourth value...
            m3dSetMatrixColumn44(matrix, vXAxis, 0);
            matrix[3] = 0.0f;
           
            // Y Column
			m3dSetMatrixColumn44(matrix, vUp, 1);
            matrix[7] = 0.0f;       
                                    
            // Z Column
			m3dSetMatrixColumn44(matrix, vForward, 2);
            matrix[11] = 0.0f;

            // Translation (already done)
			if(bRotationOnly == true)
				{
				matrix[12] = 0.0f;
				matrix[13] = 0.0f;
				matrix[14] = 0.0f;
				}
			else
				m3dSetMatrixColumn44(matrix, vOrigin, 3);

            matrix[15] = 1.0f;
			}

代码很简单,先进行了cross对y和z进行叉乘得到x,然后存入矩阵列第一列,第一列第四个元素是matrix[3] ,还记得它是一个一维数组[16]吧? 接着y和z分别入第二和第三列,第四个元素都是0,第四列会判断是否已经完成了平移,这可能指的是GLFrame已经在世界坐标系中的vOrigin坐标了,所以此时获取到的矩阵就不应该有平移向量列,故设置为(0,0,0)不进行平移vOrigin了,否则就设置为vOrigin为第四列前三个元素,第四列第四个元素[15]设置为1。

矩阵堆栈有三个对应操作GLFrame的函数如下:

void GLMatrixStack::LoadMatrix(GLFrame& frame);
void GLMatrixStack::MultMatrix(GLFrame& frame);
void GLMatrixStack::PushMatrix(GLFrame& frame);

欧拉角:“卢克!请使用帧”

struct EULER{
    M3DVector3f vPosition;
    GLfloat fRoll;
    GLfloat fPitch;
    GLfloat fYaw;
};

可用来表达一架飞机的位置和方向,欧拉角所需的空间更少。第一个问题是一个给定的位置和方向可以用多个欧拉角来表示,这种可以用多个欧拉角表示的情况可能会导致问题。

欧拉角使用起来比较繁琐,例如绕本地轴旋转并计算新的欧拉角时;

还有一个万向锁问题,目前有一个叫四元组(四元数)的数据工具来解决这种问题。 这些都不深入研究,此处只是介绍一下。

照相机管理

当照相机概念出现时,可理解为3D场景中出现了照相机物体,此时当前的参考帧系统在3D环境中即可以用角色帧表示,也可以用照相机帧表示。(其实就是不同坐标系下的表示)

以我们的人眼来说明一下,当我们注视一个物体并往后移动,物体是向前移动,当你头部往左旋转时,物体往右旋转了。这说明了我们可以将照相机的角色变换反转来得到应用于整个场景物体的角色变换。(注意照相机的角色变换是指照相机本身的变换)

案例文章:https://blog.csdn.net/qq_39574690/article/details/115056233

添加更多角色

准备一个球体批次sphereBatch和50个GLFrame作为球体角色的帧,在模型视图矩阵堆栈的顶端应用了照相机变换后,进行循环执行50次进行PushMatrix拷贝堆栈顶部元素后,调用MultMatrix(GLFrame[i])应用角色帧在栈顶元素后,用这个新的模型视图投影矩阵进行渲染球体,渲染后出栈(这过程执行50遍)就完成了案例结果。

案例文章:https://blog.csdn.net/qq_39574690/article/details/115056772

关于光线

变换光线,光线不是几何图形,但光线也需要转换到视觉坐标系。具体内容在案例里说明。

案例文章:https://blog.csdn.net/qq_39574690/article/details/115056920

猜你喜欢

转载自blog.csdn.net/qq_39574690/article/details/115035102