具有局部 y 方向的向量与另一个向量的叉积
Cross product of a vector with local y direction and another vector
所以我有一个用 glm::mat4 m_yellow_mat
表示的立方体
我围绕 z-axis
进行了 30 degree
旋转
m_yellow_mat = glm::rotate(m_yellow_mat, glm::radians(30), glm::vec3(0, 0, 1));
现在参考下图
我想求蓝色向量和红色向量的叉积
我知道蓝色矢量,但不知道如何找到红色矢量?红色向量表示立方体局部y轴的方向?
cross( ?????? , blue_vector);
世界space中的局部y轴存储在矩阵的第二行:
vec3 yaxis_world = normalize(m_yellow_mat[1][0], m_yellow_mat[1][1], m_yellow_mat[1][2]);
解释:
对象 space 中的 y 轴(我们称之为 yaxis)根据定义为 [0,1,0]。为了将向量从对象 space 转换为世界 space,我们将向量与模型矩阵相乘。由于我们对方向感兴趣,因此齐次坐标必须为 0:
axisy_world = modelMatrix * yaxis
axisy_world = modelMatrix * [0,1,0,0]
查看矩阵乘法时,我们注意到这 return 正好是矩阵的第二行。
请注意,必须对结果进行归一化,以抵消矩阵中的比例因子。如果保证只包含translations/rotations,则可以跳过归一化。
BDL 的回答已经明确指出您可以获得这样的 up 向量 代表您的对象的局部坐标,如从其第二列变换矩阵在场景中看到的那样。
我想更进一步,因为它可能对其他读者有益,从另一个角度了解为什么我们可以从变换矩阵本身中提取此类向量。
首先,我们可以从变换矩阵(矩阵运算的结果矩阵,即平移、旋转、缩放)中提取以下方向向量
- 它的第 1 列代表对象左向量
- 它的第2列表示对象
的向上向量
- 它的第3列表示对象前向向量
这与视图矩阵的构造方式有些关系。这将有助于回答为什么这些信息会在那里。
为了立即使用,如果我们有 4x4 矩阵,我们可以忽略最后一列,它是位置分量,如果你知道这样的矩阵是缩放的或者只是想确保在使用提取的列之前对其进行归一化来自矩阵的向量。
构建视图矩阵
从构建视图矩阵我们知道它只是反转我们通常对世界上的某个物体所做的事情。由于 OpenGL 中没有像相机这样的东西,也可能没有任何渲染 API,它只是一个虚拟的东西。
为了达到相机的效果,并且能够模拟玩家的移动或者看向特定的方向,我们恰恰相反(这里使用OpenGL在世界中使用的右手规则space,并通过GLM 中的默认值)
- 通过 dx,dy,dz 移动相机 =>
0,0,10
:我们所做的是通过 0,0,-10
. 平移场景中的所有对象
- 对于旋转,它有点棘手,但它只是旋转矩阵的逆。
将上面的两个相乘我们将得到
view_matrix = inverse(M_rotation) * M_translation
你可以read more,因为 Song Ho.
写得很好
他文章的重要摘录,我们会得到
注意左、上和前向量。综上所述,视图矩阵就像操纵或模拟世界以满足相机交互,它仍然在某种意义上与世界交互space。
现在怎么办?
正如我们看到的视图矩阵构造,它应用了类似的概念。我们可以将它与场景本身中的对象一起使用,但有 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 轴滚动)仅当 我们手边没有 up vector;但大多数情况下,对于像小发明、平面或本身这样的简单对象来说,只要您知道它的初始起始方向必须面向 +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;
}
我的例子中也使用了这个函数
又一个视角
支持直接从矩阵中提取局部坐标的想法。
我引用了 F.Dunn 3D Math Primer for Graphics and Game development 一书的 7.2.1 部分(注意:本书使用行优先列)
If we interpret the rows of a matrix as the basis vectors of a coordinate space, then multiplication by the matrix performs a coordinate space transformation. If aM=b
, we say that M
tranformed a
to b
.
在我们的例子中,它适用于矩阵的列。
同一部分的另一个引述,
The bottom line is that there's nothing especially magical about matrices. They simply provide a compact way to represent the mathematical operations required to perform a coordinate space transformation.
例子
我创建了两个示例来验证这一点。
- 使用矩阵运算得到结果矩阵,然后提取它的3个方向向量在屏幕上绘制。这使用平面作为几何体。查看 haxpor/lgl - 1
- 使用单个 lookAt 向量来定位平面,而无需维护多个矩阵。利用上面的
computeLookAtForObject()
。查看 haxpor/lgl - 2
这两个例子,直接进入这个目录后直接点击make
即可。在 Ubuntu 18.04,Linux.
上测试
学分
- 感谢 Song Ho 为这些主题提供的信息性文章。
所以我有一个用 glm::mat4 m_yellow_mat
我围绕 z-axis
30 degree
旋转
m_yellow_mat = glm::rotate(m_yellow_mat, glm::radians(30), glm::vec3(0, 0, 1));
现在参考下图
我想求蓝色向量和红色向量的叉积
我知道蓝色矢量,但不知道如何找到红色矢量?红色向量表示立方体局部y轴的方向?
cross( ?????? , blue_vector);
世界space中的局部y轴存储在矩阵的第二行:
vec3 yaxis_world = normalize(m_yellow_mat[1][0], m_yellow_mat[1][1], m_yellow_mat[1][2]);
解释:
对象 space 中的 y 轴(我们称之为 yaxis)根据定义为 [0,1,0]。为了将向量从对象 space 转换为世界 space,我们将向量与模型矩阵相乘。由于我们对方向感兴趣,因此齐次坐标必须为 0:
axisy_world = modelMatrix * yaxis
axisy_world = modelMatrix * [0,1,0,0]
查看矩阵乘法时,我们注意到这 return 正好是矩阵的第二行。
请注意,必须对结果进行归一化,以抵消矩阵中的比例因子。如果保证只包含translations/rotations,则可以跳过归一化。
BDL 的回答已经明确指出您可以获得这样的 up 向量 代表您的对象的局部坐标,如从其第二列变换矩阵在场景中看到的那样。
我想更进一步,因为它可能对其他读者有益,从另一个角度了解为什么我们可以从变换矩阵本身中提取此类向量。
首先,我们可以从变换矩阵(矩阵运算的结果矩阵,即平移、旋转、缩放)中提取以下方向向量
- 它的第 1 列代表对象左向量
- 它的第2列表示对象 的向上向量
- 它的第3列表示对象前向向量
这与视图矩阵的构造方式有些关系。这将有助于回答为什么这些信息会在那里。
为了立即使用,如果我们有 4x4 矩阵,我们可以忽略最后一列,它是位置分量,如果你知道这样的矩阵是缩放的或者只是想确保在使用提取的列之前对其进行归一化来自矩阵的向量。
构建视图矩阵
从构建视图矩阵我们知道它只是反转我们通常对世界上的某个物体所做的事情。由于 OpenGL 中没有像相机这样的东西,也可能没有任何渲染 API,它只是一个虚拟的东西。
为了达到相机的效果,并且能够模拟玩家的移动或者看向特定的方向,我们恰恰相反(这里使用OpenGL在世界中使用的右手规则space,并通过GLM 中的默认值)
- 通过 dx,dy,dz 移动相机 =>
0,0,10
:我们所做的是通过0,0,-10
. 平移场景中的所有对象
- 对于旋转,它有点棘手,但它只是旋转矩阵的逆。
将上面的两个相乘我们将得到
view_matrix = inverse(M_rotation) * M_translation
你可以read more,因为 Song Ho.
写得很好他文章的重要摘录,我们会得到
注意左、上和前向量。综上所述,视图矩阵就像操纵或模拟世界以满足相机交互,它仍然在某种意义上与世界交互space。
现在怎么办?
正如我们看到的视图矩阵构造,它应用了类似的概念。我们可以将它与场景本身中的对象一起使用,但有 3 个不同
- 前向矢量现在从对象本身指向目标(与视图矩阵构造相反),因为现在将旋转的是对象而不是其他对象。
- 我们不需要位置矩阵(本身就是位置信息),我们只关心方向(这样就不需要调整位置分量,因为我们不需要在位置分量前面加上负数)
我们不需要将逆矩阵应用于旋转矩阵,因为我们不是在相机上操作,而是对象本身
- 和 3. 结合起来我们可以推导出一个 3x3 旋转矩阵,或者 4x4 矩阵,最后一列有
0,0,0,1
。
- 和 3. 结合起来我们可以推导出一个 3x3 旋转矩阵,或者 4x4 矩阵,最后一列有
简而言之,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 轴滚动)仅当 我们手边没有 up vector;但大多数情况下,对于像小发明、平面或本身这样的简单对象来说,只要您知道它的初始起始方向必须面向 +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;
}
我的例子中也使用了这个函数
又一个视角
支持直接从矩阵中提取局部坐标的想法。
我引用了 F.Dunn 3D Math Primer for Graphics and Game development 一书的 7.2.1 部分(注意:本书使用行优先列)
If we interpret the rows of a matrix as the basis vectors of a coordinate space, then multiplication by the matrix performs a coordinate space transformation. If
aM=b
, we say thatM
tranformeda
tob
.
在我们的例子中,它适用于矩阵的列。
同一部分的另一个引述,
The bottom line is that there's nothing especially magical about matrices. They simply provide a compact way to represent the mathematical operations required to perform a coordinate space transformation.
例子
我创建了两个示例来验证这一点。
- 使用矩阵运算得到结果矩阵,然后提取它的3个方向向量在屏幕上绘制。这使用平面作为几何体。查看 haxpor/lgl - 1
- 使用单个 lookAt 向量来定位平面,而无需维护多个矩阵。利用上面的
computeLookAtForObject()
。查看 haxpor/lgl - 2
这两个例子,直接进入这个目录后直接点击make
即可。在 Ubuntu 18.04,Linux.
学分
- 感谢 Song Ho 为这些主题提供的信息性文章。