齐次坐标(Homogeneous coordinates)
目前为止,我们仍然把三维顶点视为三元组(x,y,z)。现在引入一个新的分量w,得到向量(x,y,z,w)。请先记住以下两点(稍后我们会给出解释):
(请务必将此牢记在心。)
二者有什么区别呢?对于旋转,这点区别倒无所谓。当您旋转点和方向时,结果是一样的。但对于平移(将点沿着某个方向移动)情况就不同了。”平移一个方向”是毫无意义的。
齐次坐标使得我们可以用同一个公式对点和方向作运算。
简而言之,矩阵就是一个行列数固定的、纵横排列的数表。比如,一个2x3矩阵看起来像这样:
三维图形学中我们只用到4x4矩阵,它能对顶点(x,y,z,w)作变换。这一变换是用矩阵左乘顶点来实现的:
矩阵x顶点(记住顺序!!矩阵左乘顶点,顶点用列向量表示)= 变换后的顶点
这看上去复杂,实则不然。左手指着a,右手指着x,得到ax。 左手移向右边一个数b,右手移向下一个数y,得到by。依次类推,得到cz、dw。最后求和ax + by + cz + dw,就得到了新的x!每一行都这么算下去,就得到了新的(x, y, z, w)向量。
这种重复无聊的计算就让计算机代劳吧。
用C++,GLM表示:
glm::mat4 myMatrix;
glm::vec4 myVector;
// fill myMatrix and myVector somehow
glm::vec4 transformedVector = myMatrix * myVector; // Again, in this order ! this is important.
用GLSL表示:
mat4 myMatrix;
vec4 myVector;
// fill myMatrix and myVector somehow
vec4 transformedVector = myMatrix * myVector; // Yeah, it's pretty much the same than GLM
(还没把这些代码粘贴到程序里调试吗?赶紧试试!)
平移矩阵是最简单的变换矩阵。平移矩阵是这样的:
其中,X、Y、Z是点的位移增量。
例如,若想把向量(10, 10, 10, 1)沿X轴方向平移10个单位,可得:
(算算看!一定得亲手算!!)
这样就得到了齐次向量(20,10,10,1)!记住,末尾的1表示这是一个点,而不是方向。经过变换计算后,点仍然是点,这倒是挺合情合理的。
下面来看看,对一个代表Z轴负方向的向量作上述平移变换会得到什么结果:
还是原来的(0,0,-1,0)方向,这也很合理,恰好印证了前面的结论:”平移一个方向是毫无意义的”。
那怎么用代码表示平移变换呢?
用C++,GLM表示:
#include // after
glm::mat4 myMatrix = glm::translate(glm::mat4(), glm::vec3(10.0f, 0.0f, 0.0f));
glm::vec4 myVector(10.0f, 10.0f, 10.0f, 0.0f);
glm::vec4 transformedVector = myMatrix * myVector; // guess the result
用GLSL表示:呃,实际中我们几乎不用GLSL计算变换矩阵。大多数情况下在C++代码中用glm::translate()算出矩阵,然后把它传给GLSL。在GLSL中只做一次乘法:
vec4 transformedVector = myMatrix * myVector;
单位矩阵很特殊,它什么也不做。单位矩阵的身份和自然数”1”一样基础而重要,因此在这里要特别提及一下。
用C++表示:
glm::mat4 myIdentityMatrix = glm::mat4(1.0);
缩放矩阵也很简单:
例如把一个向量(点或方向皆可)沿各方向放大2倍:
w还是没变。您也许会问:”缩放一个向量”有什么用?嗯,大多数情况下是没什么用,所以一般不会去缩放向量;但在某些特殊情况下它就派上用场了。(顺便说一下,单位矩阵只是缩放矩阵的一个特例,其(X, Y, Z) = (1, 1, 1)。单位矩阵同时也是旋转矩阵的一个特例,其(X, Y, Z)=(0, 0, 0))。
用C++表示:
// Use #include and #include
glm::mat4 myScalingMatrix = glm::scale(2.0f, 2.0f ,2.0f);