OpenGL教程翻译 第六课 平移变换

OpenGL教程翻译 第六课 平移变换

原文地址:http://ogldev.atspace.co.uk/(源码请从原文主页下载)

Background                                                  

这一教程我们来看一看如何在三维场景中对物体进行各式各样的变换,并且在保持场景的立体感的情况下将其渲染到屏幕上去!常见的方法是用矩阵表示每一个变换, 之后将它们挨个儿相乘,最后乘以顶点位置产生最后的结果!接下来的每一章都将着重介绍一种转变。

       这里我们来看一种沿着一个任意长度和方向的向量移动物体的平移转变!假定你想将左图的三角形移到右图的位置:

一个方法就是提供一种偏移向量(本例中是(1,1))作为一个一致变量传递给着色器,仅仅为每个需要处理的顶点加上这个偏移向量即可。然而,这与我们所想要的将一组矩阵通过相乘得到一个综合矩阵的思想背道而驰。除此之外,稍后你将看到平移通常并不是第一个变换,所以你得在平移前让位置向量乘上代表平移之前的变化的矩阵,然后乘上这个位置向量,最后乘上代表平移之后的转变的矩阵。所以说上面的方法太笨拙了。最好的方法是找到一个代表平移的矩阵,让它参与到所有矩阵的相乘中。但是你能找到一个矩阵,使得这个矩阵和左图三角形中底部的点(0,0)相乘后得到结果(1,1)吗?事实是你使用2D矩阵无法做到这一点,对于(0,0,0)用3D矩阵也不能做到这一点!总的来说,我们需要的是一个矩阵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)的变化。让我们来看看这个被改进的单位矩阵的样子:


从这个计算我们可以得出两个结论:

1.      a,b,c,d,e和f必须是0,否则任意两个分量都将影响第三个分量(这样我们就背离了单位矩阵);

2.      因为x,y,z对所有三个分量都有影响,当他们全部为0时结果也将是0向量(所以我们仍然不能转化0向量)。

我们需要找到一个矩阵以满足下列右侧的计算:


所以我们需要在已知a-f为0的情况下找到上图所示加上v1-v3的方法。这样最后的结果将是我们平移后的向量。从这个等式看上去貌似我们需要给矩阵增加第四列,但是这样做的话我们的计算将是无效的,因为我们不能用3*4的矩阵乘上3*1的向量!矩阵相乘的规则是只允许n*m和m*n形式的矩阵相乘。所以我们不得不给我们的向量增加第四个分量。我们最好将向量的第四个分量设置为1,因为这样当我们将v1-v3放到矩阵的第四列时,相乘之后并不会改变v1、v2、v3的大小,因为它们与1相乘!但是由上面的规则可知,反过来4*1矩阵和3*4矩阵相乘仍然是无效的!通过给矩阵增加第四行使它成为一个4*4矩阵,这样就能够实现了!最后,这就是我们的平移矩阵:


现在,即使x、y和z是0我们仍然可以平移它们到任何位置。

       使用四维向量来表示三维向量的做法称作齐次坐标,这对3D图形来说普遍而又实用。向量的第四个分量称之为“w”。事实上,我们在以前教程中见到的着色器中的内置变量 gl_Position就是一个四维向量,w分量会在将3D场景投影到2D平面时起重要作用!通用的做法是:w=1表示点,为0表示向量。原因是点也可以被平移但是向量不行。你可以改变向量的长度和方向但是所有具有相同长度或方向的向量是被视为相同的,无论他们的起点在哪里!所以你可以简单地用原点作为所有向量的起点!设置w=0,乘以变换矩阵后的向量将是相同的向量。

Code Walkthru

structMatrix4f {
float m[4][4];
};

我们将一个4*4的矩阵定义添加到math_3d.h中。从现在起,它将被运用到大多数转变矩阵中。

GLuintgWorldLocation;

我们使用这个句柄来获取着色器中的世界矩阵一致变量!我们称之为“世界”是因为我们所做的是,在我们的虚拟世界坐标系统中将一个物体移动(转变)到我们想要的位置。

Matrix4fWorld;
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为sin函数的结果,这样可以将X坐标的值变换到在-1到1之间波动。现在我们需要把矩阵加载到着色器中。

glUniformMatrix4fv(gWorldLocation,1, GL_TRUE, &World.m[0][0]);

这是另外一个glUniform*函数的例子,用来加载数据到一致着色器变量中。这个特定的函数可以加载4*4的矩阵,也有用于加载2x2, 3x3, 3x2, 2x4, 4x2, 3x4 和 4x3的版本。第一个参数是一致变量的位置(在着色编译器用了glGetUniformLocation()函数后恢复)。第二个参数代表我们更新的矩阵的数量。一个矩阵我们用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 45 6(1在最低地址)

所以我们设定函数glUniformMatrix4fv()第三个参数是GL_TRUE是因为我们以行优先的方式提供矩阵的。我们也可以将第三个参数为GL_FALSE,但是这样的话我们需要转置矩阵的值,因为C/C++中内存的排列仍然是行优先,但是OpenGL将认为我们提供的前四个值实际上是一个矩阵的列,并做相应处理。第四个参数是矩阵在内存的开始地址!

其余的代码是着色器的编码。

Uniform  mat4  gWorld;

这是一个4*4矩阵一致变量。也有mat2 和mat3。

gl_Position= gWorld * vec4(Position, 1.0);

我们添加到顶点缓冲区中的三角形顶点的位置是三元向量,但是之前我们知道对于一个点,其W分量应该为1。所以这里有两种选择:将四元顶点放入顶点缓冲区中,或者在顶点着色器中增加第四部分。第一个选择没有优势,因为每个顶点位置需要消耗额外的四位内存,而因为我们知道那部分的内容一直是1。较之而言,在VBO中维持三个分量的点,之后再着色器中为其添加第四个分量的方法就高效很多。在GLSL中通过使用'vec4(Position, 1.0)'完成这个扩充。我们将这个位置向量与顶点相乘,最后将其结果传入gl_Position中。总之本例中,每一帧我们生成一个变换矩阵使得所有沿着X轴平移,并且这个平移的距离在[-1,1]之间波动。着色器将每个顶点的位置与此矩阵相乘,结果使物体左右移动。在大多数情况下,在顶点着色器完成处理后,三角形的一边会超出规范化盒子,这时候裁剪器将把超出的那边剪掉。这样我们就只能看到在规范化盒子里面的部分。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值