使用4x4矩阵转换3D平面

17

我有一个由多个三角形组成的形状,它具有缩放、旋转和平移,位于世界空间中的某个位置。我还有一个平面,我想要在上面投影(正交投影)这个形状。

我可以将形状中每个三角形的每个顶点乘以对象的变换矩阵,以找出其在世界坐标系中的位置,然后将此点投影到平面上。

但是我不需要绘制投影,而是希望使用形状的逆变换矩阵来转换平面,然后将所有顶点投影到(逆转换后的)平面上。因为这只需要我对平面进行一次变换,而不是对每个顶点进行变换。

我的平面有一个法线(xyz)和一个距离(d)。如何使用一个4x4变换矩阵与其相乘,并使其正常工作?

你可以创建一个由xyzd组成的vec4并进行乘法计算。或者创建一个由xyz1组成的向量,然后再怎么处理d呢?

3个回答

28
你需要将平面转换为另一种表示方法。其中 N 是法向量,O 是平面上的任意一点。你已经知道了法向量,即 (xyz)。平面上的一个点也很容易计算,它是法向量 N 与距离 d 的乘积。

以正常方式使用 4x4 矩阵来转换 O,这将成为你的新 O。你需要一个 Vector4 来与 4x4 矩阵相乘,将 W 分量设置为 1(x、y、z、1)。

同时,也用 4x4 矩阵来变换 N,但将 W 分量设置为 0(x、y、z、0)。将 W 分量设置为 0 意味着你的法向量不会被平移。如果你的矩阵不仅包括旋转和平移,那么这一步就不那么简单了。相反,要乘以矩阵的 逆矩阵转置,即 Matrix4.Transpose(Matrix4.Invert(Transform)),在这里有一个很好的解释。

现在你有了一个新的法向量 N 和一个新的位置向量 O。但我想你可能还希望将它们转换回 xyzd 的形式?没问题。和之前一样,xyz 是你的法向量 N,唯一需要计算的是 d。d 是平面沿着法向量到原点的距离,因此它只是 ON 的点积。

就这样!如果你告诉我你使用的是哪种语言,我很乐意以代码形式为你编写。

编辑,伪代码如下:

平面为vector3 xyznumber d,矩阵为matrix4x4 M

vector4 O = (xyz * d, 1)
vector4 N = (xyz, 0)
O = M * O
N = transpose(invert(M)) * N
xyz = N.xyz
d = dot(O.xyz, N.xyz)

xyzd代表新平面


嗨Hannesh,非常感谢您的清晰解释!我同时在Lua和C ++中使用不同的库进行编程,但代码不是问题。我困扰于概念,而您刚刚让它变得非常清晰。干杯 - Thijs Koerselman
谢谢,我已经为此挣扎了几个晚上了。 - cmannett85
有人能以更易懂的方式重新编写这个答案吗?最好附带一些GLM代码。 - Jubei
如果涉及到缩放,请不要忘记在之后对您的平面进行归一化。 - Bluenuance
在计算机图形学中,如果使用列向量,则可以使用逆转置矩阵;如果使用行向量,则可以使用逆矩阵。但是从数学上讲,这通常是不正确的(请参见下面的答案)。 - Matthias

12
这个问题有点老了,但我想更正被接受的答案。
你不需要转换平面表示。
对于任何点 v=(x,y,z,1),如果 ax+by+cz+d,那么它位于平面 p=(a,b,c,d) 上。
可以写成点积: pt v=0
你正在寻找由你的 4x4 矩阵 M 转换的平面 p'
出于同样的原因,必须满足 p't Mv=0
因此,p't Mv=pt v,通过一些安排,可以得到 p'=M^T p
简而言之:如果 p=(a,b,c,d),则 p' = transpose(inverse(M))*p

谢谢,但我恐怕没有足够的知识来正确评判你的答案。我的数学技能仍然很差,而且自从这个问题发布后没涉足过相关领域。我想我唯一能做的就是接受你的答案并看看是否会有人因此责骂我...但这不是马蒂亚斯早些时候试图解释的吗? - Thijs Koerselman
在这样做之后,规范化平面方程可能是个好主意,特别是如果矩阵包含非单位基向量(缩放)。 - neeh

2

注释:

  • n 是表示法向量的 (1x3) 行向量
  • n' 是根据变换矩阵 T 转换后的法向量 n
  • (n|d) 是表示平面的 (1x4) 行向量(其中 n 是平面的法向量,d 是平面到原点的距离)
  • (n'|d') 是根据变换矩阵 T 转换后的平面 (n|d)
  • T 是一个 (4x4) (仿射)列优先的变换矩阵(即将列向量 t 进行变换的定义为 t' = T t)。

转换法向量 n:

n' = n adj(T)

转换平面(n|d):

(n'|d') = (n|d) adj(T)

在这里,adj是矩阵的伴随矩阵,它是通过矩阵的逆和行列式定义的:

T^-1 = adj(T)/det(T)

注意:

  • 伴随矩阵通常不等于变换矩阵 T 的逆矩阵。如果T包括反射,det(T) = -1,会将顺序反转!

  • 数学上不需要重新规范化n'(但是可能取决于实现而需要在数值上重新归一化)因为缩放由行列式处理。感谢Adrian Leonhard的建议。

  • 您可以直接变换平面,而无需首先分解和重组平面(法线和点)。

1
@Leonhard 我将法线和平面表示为行向量,而将点和方向表示为列向量,因为我使用列主转换矩阵(请参见“符号”中的第5个点)。如果您想在任何地方都使用列向量,请只需取n' = n adj(T)两边的转置,这将导致伴随的转置或者是转置的伴随。如果您使用逆代替伴随,则需要在存在缩放分量的情况下进行重新归一化。或者您可以除以行列式,它是尺寸变化的度量。 - Matthias
1
@AdrianLeonhard 更详细的解释/推导请参见游戏引擎开发基础:第1卷:数学。然而,注意,在这种情况下,伴随算子是一些数学上的技巧,以获得精确和通用的单行表达式。在实践中(游戏/渲染引擎),人们将通过逆转置乘以它(甚至消除非均匀缩放,通过转置转置乘以变换矩阵本身+由于均匀缩放而进行重新归一化)。 - Matthias
1
此外,在使用变换/场景图时,将不会计算行列式或逆矩阵。 - Matthias
1
感谢澄清,但是考虑到一个2倍缩放的变换T = [2 0 0 0 | 0 2 0 0 | 0 0 2 0 | 0 0 0 1],adj(T) = T^-1 * det(T) = [4 0 0 0 | 0 4 0 0 | 0 0 4 0 | 0 0 0 8]。对于平面X = 0:(1 0 0 0) * adj(T) = (4 0 0 0),这仍然需要进行归一化吗? - Adrian Leonhard
1
  1. 如果您的法向量是通过叉积计算得出的,则会多出一个因子,即行列式(或者,您最终会得到伴随矩阵)。在这两种情况下,您都需要重新归一化,如果行列式> 0,则标量行列式将变得无关紧要。如果行列式为负(反射),则应该颠倒法线的方向。作为一个经验法则,所有源自叉积(平面、法线等)的内容都需要进行伴随矩阵的转换。
- Matthias
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接