(第一次发博客,格式上可能有些不好看…)
问题
在学习openGL变换的时候,我们知道一个规则就是:先缩放再旋转再平移,关于这个规则以及变换关系建议去看learnopengl的教程。
但是我们还有另外一种情景,就是比如我们希望变换是基于某个点进行的,如一个三角形基于它的一个顶点进行缩放,那这个时候我们的做法就是先把三角形平移到原点处(假设该顶点坐标为(x,y),则平移为T(-x,-y)),然后缩放(比如,S(2,2)),然后平移回去T(x,y)。这时我产生了一个疑问就是,为什么这里的平移不会受到缩放的影响?如果按照上面规则中的相互影响,第一次平移被缩放放大了两倍,那最后平移回去是不是应该平移两倍关系(即最后一步,T(2x,2y))?
结论是,上述两种写法都是正确的,只是为了达到不同的目的而已。
演示
假设有
A
(
2
,
3
)
A(2,3)
A(2,3),我们先让它平移再缩放,然后让它缩放再平移试试:
第一,先平移
T
(
−
1
,
−
1
)
T(-1,-1)
T(−1,−1),再缩放
S
(
2
,
2
)
S(2,2)
S(2,2):
T
(
−
1
,
−
1
)
=
[
1
0
−
1
0
1
−
1
0
0
1
]
T(-1,-1)= \begin{bmatrix} 1 & 0 & -1\\ 0 & 1 & -1 \\ 0 & 0 & 1 \end{bmatrix}
T(−1,−1)=⎣⎡100010−1−11⎦⎤
S
(
2
,
2
)
=
[
2
0
0
0
2
0
0
0
1
]
S(2,2)= \begin{bmatrix} 2& 0 & 0 \\ 0 & 2 & 0 \\ 0 & 0 & 1 \end{bmatrix}
S(2,2)=⎣⎡200020001⎦⎤
T ( − 1 , − 1 ) ∗ A ( 2 , 3 ) T(-1,-1)*A(2,3) T(−1,−1)∗A(2,3)得到 A ′ ( 1 , 2 ) A'(1,2) A′(1,2),这个跟我们平移一个点得到结果一致;
S ( 2 , 2 ) ∗ A ′ ( 1 , 2 ) S(2,2)*A'(1,2) S(2,2)∗A′(1,2)得到 A ′ ′ ( 2 , 4 ) A''(2,4) A′′(2,4),这个跟我们直接放大 A ′ ( 1 , 2 ) A'(1,2) A′(1,2)得到结果一致。
第二,先缩放 S ( 2 , 2 ) S(2,2) S(2,2),再平移 T ( − 1 , − 1 ) T(-1,-1) T(−1,−1):
S ( 2 , 2 ) ∗ A ( 2 , 3 ) S(2,2)*A(2,3) S(2,2)∗A(2,3)得到 A ′ ( 4 , 6 ) A'(4,6) A′(4,6),这个跟我们放大 A A A 点得到结果一致;
T ( − 1 , − 1 ) ∗ A ′ ( 4 , 6 ) T(-1,-1)*A'(4,6) T(−1,−1)∗A′(4,6)得到 A ′ ′ ( 3 , 5 ) A''(3,5) A′′(3,5),这个跟我们直接平移 A ′ ( 4 , 6 ) A'(4,6) A′(4,6)得到结果一致。
上述结果至少可以明确一点,就是无论矩阵变换的顺序,矩阵本身会切实地完成我们想要的目的(只要输入的参数正确),比如我们想要平移,那用平移矩阵结果就会正确(这点应该是显然的)。
我们再来模拟一下完整的平移、缩放、反向平移的过程:
T
(
−
1
,
−
1
)
∗
A
(
2
,
3
)
T(-1,-1)*A(2,3)
T(−1,−1)∗A(2,3)得到
A
′
(
1
,
2
)
A'(1,2)
A′(1,2),
S
(
2
,
2
)
∗
A
′
(
1
,
2
)
S(2,2)*A'(1,2)
S(2,2)∗A′(1,2)得到
A
′
′
(
2
,
4
)
A''(2,4)
A′′(2,4),
T ( 1 , 1 ) ∗ A ′ ( 2 , 4 ) T(1,1)*A'(2,4) T(1,1)∗A′(2,4)得到 A ′ ′ ( 3 , 5 ) A''(3,5) A′′(3,5)。
A ′ ′ ( 3 , 5 ) A''(3,5) A′′(3,5)这个结果对于我们来说似乎在几何意义上并没有任何意义,原因是我们使用这种写法通常都是与把某个位置放到原点进行操作整个目的结合在一起!所以这里我们把 A ( 2 , 3 ) A(2,3) A(2,3) 改成 A ( 1 , 1 ) A(1,1) A(1,1),再重新来一遍。
T ( − 1 , − 1 ) ∗ A ( 1 , 1 ) T(-1,-1)*A(1,1) T(−1,−1)∗A(1,1)得到 A ′ ( 0 , 0 ) A'(0,0) A′(0,0), S ( 2 , 2 ) ∗ A ′ ( 0 , 0 ) S(2,2)*A'(0,0) S(2,2)∗A′(0,0)得到 A ′ ′ ( 0 , 0 ) A''(0,0) A′′(0,0),
T ( 1 , 1 ) ∗ A ′ ( 1 , 1 ) T(1,1)*A'(1,1) T(1,1)∗A′(1,1)得到 A ′ ′ ( 1 , 1 ) A''(1,1) A′′(1,1)。
这个过程是不是就是完美表现了我们要达到的目的?即基点本身会保持不变,而跟它相关的点(比如三角形另外的点)基于它发生变化。
显然可以得出结论是上述的第二种写法是没有问题的,似乎也很显然,毕竟两次平移是互逆的,可以抵消影响。而这个又是基于上述中间的一个结论就是:矩阵变换本身的效果是不会受到其他干扰的!我们让它平移就平移了,让它旋转就旋转了。
总结
小结一下,第一种写法(即遵守先缩放再旋转再平移),是为了看到一个最终的效果,举个例子,比如我们建立了一个模型,想看到的结果是让它平移到某一个位置,并且旋转了90度,且比例缩放了一倍,这个时候变换的顺序就必须遵循上述规则,不然它们就可能彼此干涉。理由是它们虽然同样达到了各自独立的效果(比如平移,缩放),但是先平移的那个距离会被后续的缩放所影响,原来我们预计平移一个单位长度,但是这个1可能会被放大成2(或缩小为1/2)。即矩阵变换本身不受干扰,只是它们之间的参数发生扰动。
第二种写法的目标是每一个子矩阵所产生的结果,如我们要把
A
A
A 平移到原点,那
T
(
−
1
,
−
1
)
T(-1,-1)
T(−1,−1) 这个矩阵就达到了我们的目的,相应的为了抵消这个影响,我们需要
T
(
1
,
1
)
T(1,1)
T(1,1) 这个矩阵的效果。总而言之,这个写法使用了每个子矩阵的效果,而这个效果显然是相互独立的。
ps:似乎挺啰里啰嗦的,可是我在这该死的地方纠结了考试中的一个小时!可能就是越简单的地方越把它当做理所当然的事情越难理清楚其中的关系吧(逃