BDL 的回答已经确定了您可以从第二列变换矩阵中获得表示对象局部坐标的 向上向量。
我想更进一步,因为它可能对其他读者有所帮助,从另一个角度了解为什么我们可以从变换矩阵本身中提取此类向量。
首先,我们可以从变换矩阵(矩阵运算的结果矩阵,即平移、旋转、缩放)中提取以下方向向量
- 它的第一列代表对象的左向量
- 它的第 2 列代表对象的向上向量
- 它的第 3 列代表对象的前向向量
这与视图矩阵的构造方式有些相关。这将有助于回答为什么这些信息在那里。
为了立即使用,如果我们有 4x4 矩阵,我们可以忽略作为位置分量的最后一列,如果您知道这样的矩阵已缩放或只是想确保在使用从提取的列向量之前对其进行归一化矩阵。
构造视图矩阵
通过构造视图矩阵,我们知道它只是逆向我们通常对世界上某个对象所做的事情。由于 OpenGL 中没有相机之类的东西,也没有任何渲染 API,它只是一个虚拟的东西。
为了达到摄像头的效果,并且能够模拟玩家运动或观察特定方向,我们完全相反(这里使用右手法则,OpenGL 在世界空间中使用,默认情况下在 GLM 中使用)
- 让相机移动 dx,dy,dz =>
0,0,10 :我们所做的是将场景中的所有对象通过 0,0,-10 平移。
- 对于旋转,它有点棘手,但它只是旋转矩阵的逆矩阵。
将上面的两个相乘得到
view_matrix = inverse(M_rotation) * M_translation
您可以read more 讨论这个问题,因为Song Ho 写得很好。
他文章的重要摘录,我们会得到
注意 Left、up 和 forward 向量在其中。综上所述,视图矩阵就像操纵或模拟世界以满足相机交互,在某种意义上它仍然与世界空间进行交互。
现在呢?
正如我们看到的视图矩阵构造,它应用了类似的概念。我们可以将它与场景中的对象一起使用,但有 3 个不同
-
前向向量现在从对象本身指向目标(与视图矩阵构造相反),因为现在它将旋转的是对象而不是其他对象。
- 我们不需要位置矩阵(本身的位置信息),我们只关心方向(因此这消除了调整位置组件的需要,因为我们不需要位置组件前面的负数)
我们不需要将逆矩阵应用于旋转矩阵,因为我们不是在相机上操作,而是对象本身
- 和 3. 结合起来,我们可以推导出一个 3x3 旋转矩阵,或 4x4 矩阵,最后一列有
0,0,0,1。
简而言之,m_yellow_mat 包含代表对象局部轴的 left、up 和 forward 向量。可以如下直接提取出来
glm::vec3 left = glm::vec3(m_yellow_mat[0]);
glm::vec3 up = glm::vec3(m_yellow_mat[1]);
glm::vec3 forward = glm::vec3(m_yellow_mat[2]);
(额外)再进一步:一个向量可以表示物体的方向
了解视图矩阵是如何构造的更多好处。单个方向向量可以表示对象的方向,而无需我们维护每个轴 3 个矩阵旋转,但它会丢失一个单一信息(主要围绕 z 轴滚动)只有在我们不这样做时'此时手边没有向上向量;但大多数情况下,对于像 gizmo、平面或本身这样的简单对象来说,只要你知道它的初始起始方向必须朝向 +z 轴,在 y-轴。
执行此操作的代码是构造视图矩阵的更简单形式,因为我从 Song Ho 的实现中学到了这一点,稍作修改以适合我的示例。
glm::mat4 computeLookAtForObject(const glm::vec3& pos, const glm::vec3& target)
{
glm::vec3 forward = glm::normalize(target - pos);
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
// handle if forward is nearly the same as up vector
// then we choose another direction for forward
if (std::abs(forward.x) < kEpsilon && std::abs(forward.z) < kEpsilon)
{
if (forward.y > 0.0f)
{
up.x = 0.0f;
up.y = 0.0f;
up.z = -1.0f;
}
else
{
up.x = 0.0f;
up.y = 0.0f;
up.z = 1.0f;
}
}
glm::vec3 left = glm::normalize(glm::cross(up, forward));
up = glm::cross(forward, left);
glm::mat4 m = glm::mat4(1.0f);
m[0] = glm::vec4(left, 0.0f);
m[1] = glm::vec4(up, 0.0f);
m[2] = glm::vec4(forward, 0.0f);
//m[3] = glm::vec4(pos, 1.0f); // <--- (optional) we can completely ignore this, or build up full-feature matrix from this function as well by uncommenting this line
return m;
}
我的示例中也使用了这样的函数。
也是另一种观点
支持直接从矩阵中提取局部坐标的想法。
我在第 7.2.1 节引用了 F.Dunn 3D Math Primer for Graphics and Game development 一书中的以下内容(注意:该书使用行主要列)
如果我们将矩阵的行解释为坐标空间的基向量,那么乘以矩阵会执行坐标空间变换。如果aM=b,我们说M 将a 转换为b。
在我们的例子中,它适用于矩阵的列。
同一部分的其他引用,
底线是矩阵没有什么特别神奇的地方。它们只是提供了一种简洁的方式来表示执行坐标空间变换所需的数学运算。
示例
我创建了两个示例来验证这一点。
- 使用矩阵运算得到结果矩阵,然后提取其 3 个方向向量以将其绘制在屏幕上。这使用平面作为几何图形。检查haxpor/lgl - 1
- 使用单个lookAt 向量来定向平面,而无需维护多个矩阵。使用上面的
computeLookAtForObject()。检查haxpor/lgl - 2
这两个例子都可以在直接进入此类目录后直接点击make。在 Ubuntu 18.04、Linux 上测试。
学分