signed

QiShunwang

“诚信为本、客户至上”

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

2021/3/20 23:54:10   来源:

目录

3D数学

向量

点乘

叉乘

矩阵

理解变换

视觉坐标

视图变换

模型变换

模型视图的二元性

投影变换

视口变换

模型视图矩阵

矩阵构造

单位矩阵

平移

旋转

缩放

综合变换

运用模型视图矩阵


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结果。

运用模型视图矩阵

后续