opengl教程翻译 #6平移变换

背景知识:
在这个课程中我们开始关注一个物体在3D中的各种变换,并使它在屏幕上显示时呈现一种纵深的错觉。通常的做法是分别用一种矩阵表示变换,逐个相乘,用最终的式子再乘顶点位置。每节课会专门讲解一个变换。
这里我们看一下这个平移变换,这个变换负责沿着一个任意长和方向的向量移动物体。就说你想把三角形从左图移到右图吧。


其中一个方法是提供一个向量偏移(例如-1,1)作为统一变量给着色器然后简单的添加到处理中的顶点。然而,这打破了一组矩阵相乘的、从而成为一个完整变换的整体。此外,你之后会看到平移常常不是第一个变换,可能在平移前要用点乘一堆其他变换的矩阵,加上位置,最后再乘随后的平移变换矩阵。这是很尴尬的。一个比较好的方法是找到另一种平移矩阵然后加入所有其他矩阵的相乘中。不过你能找到一个矩阵,使其乘(0,0)得到(1,1)吗?事实是你不能用一个2D的矩阵(同样不能用3D矩阵对(0,0,0))。通常我们会说我们需要的是一个矩阵M,使其可以在给出点P(x,y,z)和一个向量V(v1,v2,v3)的情况下提供M*P=P1(x + v1, y + v2, z + v3)。简单的说这意味帧矩阵M把P平移到位置P+V。在P1我们可以看到每个分量是P和V对应分量的和。相加式子的左边是一个单位矩阵:I * P = P(x, y, z)。因此看起来我们应该先基于单位矩阵,再找出导致式子右边部分(...+v1, ...+v2, ...+v3)的改变。单位矩阵看起来如下:

我们想把单位矩阵修改成这个样子:


如果我们拘泥于3*3的矩阵,还真没有容易的方法,可是我们可以改成4*4的矩阵,如下图所示:
像这样用一个4-向量表示一个3-向量被称作【齐次坐标】,对于3D图像非常流行和管用。第四个分量被称为'w'。事实上,在我们之前课程中的那个着色器内部符号gl_Position就是一个4-向量而且w分量对于3D到2D的投影有着重要的作用。通常的表示方法是,对于点用w=1,对于向量用w=0。原因在于点是可以被平移的,向量却不可以。你可以改变向量的长度或者方向,但所有有相同长度和方向的向量被认为是等价的,并不关心所谓的“开始位置”。所以对于所有的向量,你可以简单的使用源点(译者注:作为它们的开始位置)。把一个平移矩阵的w设为0,用一个向量乘这个平移矩阵,得到的还是一样的向量。

源码实践:

struct Matrix4f {
float m[4][4];
};
我们给math_3d.h添加了一个4*4矩阵的定义。这从现在开始对于我们大部分的变化矩阵有很大作用。

GLuint gWorldLocation;
我们用这个句柄来访问着色器中的统一变量世界矩阵。起名为“世界”是因为我们正在做的是把一个物体的位置移动到我们的虚拟“世界”坐标系统中。

Matrix4f World;
World.m[0][0] = 1.0f; World.m[0][1] = 0.0f; World.m[0][2] = 0.0f; World.m[0][3] = sinf(Scale);
World.m[1][0] = 0.0f; World.m[1][1] = 1.0f; World.m[1][2] = 0.0f; World.m[1][3] = 0.0f;
World.m[2][0] = 0.0f; World.m[2][1] = 0.0f; World.m[2][2] = 1.0f; World.m[2][3] = 0.0f;
World.m[3][0] = 0.0f; World.m[3][1] = 0.0f; World.m[3][2] = 0.0f; World.m[3][3] = 1.0f;
在渲染函数中,我们准备一个4*4的矩阵,然后按照上面的说明填充它。我们把v2和v3设为0,因此我们期望物体在Y和Z轴上没有改变,我们设v1为正弦函数的返回结果。这将会借助一个在-1和1间波动的平滑的值来平移X坐标。现在我们需要把矩阵加载如着色器。

glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &World.m[0][0]);
这是另一个glUniform*函数把数据加载进统一着色器变量的例子。这个特定的函数加载了4*4矩阵,当然也有2*2,3*3,3*2,2*4,4*2,3*4和4*3的版本。第一个参数是统一变量的位置(在着色器编译后由glGetUniformLocation()返回)。第二个参数指代我们正在更新的矩阵的数量。我们使用1代表1个矩阵,但是我们也使用这个函数在一次调用中去更新相乘矩阵。第三参数经常困扰着新人。它指代提供的矩阵是主行还是主列命令。主行意味着提供的矩阵是一行行的,从最上端开始。主列同理。问题是C/C++默认是主行的语言。这意味着当你用值填充一个二维数组时,它们被一行行地排布进内存,以最上端为低地址位。例如,我们看下面的数组:
int a[2][3];
a[0][0] = 1;
a[0][1] = 2;
a[0][2] = 3;
a[1][0] = 4;
a[1][1] = 5;
a[1][2] = 6;
可以看见,数组很像下面的矩阵:
1 2 3
4 5 6
内存布局像这样1 2 3 4 5 6(1在低地址位)。
所以我们给glUniformMatrix4fv()的第三个参数是GL_TRUE因为我们提供了主行命令的矩阵。我们也可以使第三个参数为GL_FALSE但是我们需要反转矩阵的值(C/C++内存布局保留不变,而OpenGL会“认为”我们提供的前4个值实际上是一个矩阵的列以及它会根据这点去解析)。第四个参数就是简单的矩阵的内存起始地址。

剩下就是着色器代码了。

uniform mat4 gWorld;

gl_Position = gWorld * vec4(Position, 1.0);
在顶点缓冲中三角形的顶点位置是一个3分量的向量,不过我们都同意了还需要第4个向量,它的值为1。有两个方法:把4个分量的顶点放入顶点缓冲中,或者在顶点缓冲中添加第4个分量。第一种方法并没有优势。对于每个顶点位置,1个分量会消耗额外4bytes,可这个分量却总是1。更有效率的做法是保留3分量的向量,然后在着色器中串联w分量。在GLSL中使用 'vec4(Position, 1.0)'来完成。我们用向量乘矩阵,把结果放入gl_Position中。做个总结,对于每一帧我们产生一个平移矩阵,通过一个在-1和1之间来回走动的值平移X坐标。着色器用这个矩阵乘每个顶点的位置,造成整个物体向左或向右移动。在大多数情况下,在顶点着色器起作用后,三角形的边会跑到标准盒子外面,接着被裁剪器剪掉外面的部分。我们仅仅能够看到在标准盒子内部的区域。

猜你喜欢

转载自www.cnblogs.com/alphaGo/p/9208548.html
今日推荐