【计算机图形学(译)】 二、各种各样的数学
2 各种各样的数学 Miscellaneous Math
很多图形学只是将数学直接转换为代码。数学越简洁,得到的代码就越简洁;这本书的大部分内容都集中在如何使用正确的数学方法来完成这项工作。本章回顾了高中和大学数学中的各种工具,更多的是作为参考而不是作为教程。它可能看起来是一个大杂烩的主题,事实也的确如此:选择每个主题是因为它在“标准”数学课程中有点不同寻常,因为它在图形学中具有核心重要性,或者因为它通常不是从几何的角度来处理的。除了建立在这本书中使用的表示法的回顾,本章还强调了一些在标准本科课程中有时会跳过的点,如三角形上的重心坐标。本章并不打算对材料进行严格的处理,而强调直觉和几何解释。线性代数的讨论推迟到第6章讨论变换矩阵。鼓励读者略读本章以熟悉所涵盖的主题,并在需要时参考它。本章末尾的练习可能有助于确定哪些主题需要复习。
2.1 集合和映射 Sets and Mapping
映射,也称为函数,是数学和编程的基础。就像程序中的函数一样,数学中的映射接受一种类型的参数,并将其映射到(返回 returns)特定类型的对象。在程序中,我们说“type”;在数学中,我们要确定集合。当我们有一个对象是集合中的成员时,我们使用∈符号。例如:
a
∈
S
a∈S
a∈S 可以读作“a是集合s的成员”。
给出任意两个集合 A和B, 我们可以通过取这两个集合的笛卡尔积来创建第三个集合,表示为A×B。
该集合A×B由所有可能的有序对(a,b)组成,其中a∈ A和b∈ B。作为缩写,我们使用符号
A
2
A^2
A2 表示
A
×
A
A×A
A×A。我们可以扩展笛卡尔积,从三个集合中创建一组包括所有可能的有序三元组,以此类推,从任意多个集合中创建任意长有序元组。
通常集合包括:
- R R R—实数
- R + R+ R+—非负实数(包含0)
- R 2 R^2 R2—在2D平面上的有序的实数对
- R n R^n Rn—在N维笛卡尔空间中的一个点
- Z Z Z—整数
- S 2 S^2 S2—在一个单位球面上的所有点的集合
这里需要注意的是即便S^2是由三维空间中的点构成的,但是它们都在一个可以用2个参数去表达的一个表面上,所以它可以被想象成为一个2D的集合。映射的表示方法为双冒号+一个箭头,比如:
这可以理解为“有一个名为f的函数,它可以把输入的实数映射到一个整数上去。”在这里,箭头前面的集合称为函数的域,右侧的集合称为目标。计算机程序员可能更习惯使用以下表达:“有一个名为f的函数,它有一个实数参数,并返回一个整数。”换句话说,上述集合表示的方式等同于通用的编程表示法:
所以这种冒号-箭头的表达方式可以被想象成为编程的语法。它是如此的简单。
我们用
f
(
a
)
f(a)
f(a)来表示一个a的映射,那么一个集合A(它是参数值范围的一个子集)的映射就是函数返回值的范围的一个子集。所有参数值范围的映射构成的就是函数返回值的范围。
2.1.1 反向映射 Inverse Mappings
如果我们有一个函数
f
:
A
→
B
f: A→B
f:A→B,则可能存在一个逆函数
f
−
1
:
B
→
A
f^-1: B→A
f−1:B→A,存在这样一个规则:如果
b
=
f
(
a
)
b = f(a)
b=f(a),有
f
−
1
(
b
)
=
a
f^-1(b) = a
f−1(b)=a。
这个定义只适用于每个b∈B的时候有效 ,这样的映射方式,我们给它一个专用名词叫:双射(bijection)。一个双射会把每一个 a ∈ A 映射到一个唯一的 b ∈ B,并且对于每一个 b ∈ B,都有一个 a ∈ A 与之对应。举个例子,如果你把双射的概念套用在一组骑手和一群马之间的话,那么就意味着每一个骑手都有他自己独一无二的马,而且每一匹马都有它的主人,在这个例子中,我们可以把这两个映射的方程分别当成是:rider(horse)和horse(rider)。这两个函数互为逆函数。没有双射属性的函数没有逆函数 (即。如果只有一个这样的点(即
f
(
a
)
=
b
f(a) = b
f(a)=b的只有一个a),这样的映射或函数称为双射。
另一个双向单射的例子就是 f : R − > R f:R->R f:R−>R,假设 f ( x ) = x 3 f(x) = x^3 f(x)=x3 那么它的逆函数就是 f − 1 ( x ) = 3 x f^-1(x)=^3\sqrt{x} f−1(x)=3x 。该例子中展示的标准的函数表述的方式通常来说都会给人造成困惑,因为x这个符号在 f f f和 f − 1 f^-1 f−1 中都被用到了。有时候在不同的函数中使用不同的记号去描述一个函数会让人更好理解函数的意思,举个例子: y = f ( x ) y=f(x) y=f(x) 与 x = f − 1 ( y ) x=f^-1(y) x=f−1(y) 。那么换成是最开始介绍的那个例子就是 y = x 3 y = x^3 y=x3 与 x = 3 y x= ^3\sqrt{y} x=3y。现在举一个没有逆函数的函数的例子就是开平方操作的函数sqr: R − > R R->R R−>R,这里 s q r ( x ) = x 2 sqr(x) = x^2 sqr(x)=x2。有两个原因:首先 x 2 = ( − x ) 2 x^2 = (-x)^2 x2=(−x)2 ,第二个原因就是对于函数返回值的范围来说,它不会映射到负数的范围里去。这里我们注意到,当我们限定参数的范围为R+的时候,这个表达式是有逆函数的,这时候 x \sqrt{x} x 就是一个合法的逆函数。
2.1.2 区间 Intervalsoft
通常我们想指定一个函数处理值受限的实数,其中一个约束就是指定一个间隔。区间的一个例子是0到1之间的实数,不包括0和1。我们表示为(0, 1)。因为它不包括它的端点,所以它被称为开区间。对应的封闭区间包含它的端点,用方括号表示:[0,1]。这个符号可以混合使用;即[0,1)包括0但不包括1。在写区间[a, b]时,我们假设a小于等于b。表示区间的三种常用方法如图2.3所示。经常使用间隔的笛卡尔积。例如,要表示点x在3D的单位立方体中,我们称为
x
∈
[
0
,
1
]
3
x ∈ [0, 1]^3
x∈[0,1]3
图2.1 双射
f
f
f和逆函数
f
−
1
f^-1
f−1。注意
f
−
1
f^-1
f−1也是一个双射
图2.2 函数g没有逆因为d中的两个元素映射到了相同元素E中。函数h没有逆因为F中的T没有d的元素映射到它。
图2.3 有三种等价的方法来表示包含 b 的区间a到b不是一个。
区间在集合操作中特别有用:交集、并集和差集。例如,两个区间的交点就是它们共有的点的集合。符号∩用于表示求交集。例如,[3,5)∩[4,6] =[4,5)。对于并集,符号∪用于表示任意区间内的点。例如,[3,5)∪[4,6] =[3,6]。与前两个操作符不同,求差集的操作的结果跟参与求差集的两个区间的顺序有关。负号用于差值运算符,它返回左边区间内不在右边区间内的点。例如,[3、5) - [4、6]=[3、4)和[4、6] - (3、5)=[5、6]。使用区间图(图2.4),这些可视化后理解非常容易见图2-4。
2.1.3 对数 Logarithms
虽然在计算器出现,对数不像之前那么普遍了,但对数经常被使用在有指数项的方程中很有用。根据定义,每一个对数以a为底。x的“log以a为底数”写为
a
x
a^x
ax,定义为“x对a取对数"即,
l
o
g
a
X
loga^X
logaX
根据定义,每一个对数都有一个基a。那么也可以这么定义这个对数:对数就是哪个“使得a的幂等于x”的幂指数。换成数学表达式
图2.4。对[3,5)和[4,6]进行间隔操作。注意,以a为底的对数和取a的a次方的函数互为倒数。这个基本定义有以下几个推论:
当我们将微积分应用于对数时,特殊的数字e = 2.718…经常出现。以e为底的对数称为自然对数。我们采用常用的简写In来表示它:
I
n
x
≡
l
o
g
e
x
Inx ≡ loge^x
Inx≡logex
注意,“≡”符号可以读为“根据定义是等效的”。和π一样,特殊的数字e在很多情况下都会出现。许多领域使用除e以外的特殊底数来进行操作,并在他们的符号中省略底数,即
l
o
g
x
log x
logx。例如,天文学家经常使用以10为底,理论计算机科学家经常使用以2为底。由于计算机图形学借鉴了许多领域的技术,我们将避免这种简写。对数和指数的导数说明了为什么自然对数是“自然的”:
上面的常数乘数只适用于a = e
2.2 解二次方程 Solving Quadratic Equations
一个二次方程的形式通常为
A
x
2
+
B
x
+
C
=
0
Ax^2 + Bx +C = 0
Ax2+Bx+C=0
其中x为未知数,A、B、C为已知常数。如果你考虑一个二维xy图,
y
=
A
x
2
+
B
x
+
C
y = Ax^2 + Bx+ C
y=Ax2+Bx+C,解就是任何与X轴相交的数量。因为它是一条抛物线,所以根据抛物线是否偏离、相切或相交x轴(图2.5),将会有0、1或2个实解。
为了解析求解二次方程,我们首先让方程两边除以A:
x
2
+
B
A
x
+
C
A
=
0
x2 + \frac{B}{A} x +\frac{C}{A}= 0
x2+ABx+AC=0
然后,我们使用“完全平方公式”:
(
x
+
B
2
A
)
2
−
B
2
4
A
2
+
C
A
=
0
(x +\frac{B}{2A})^2- \frac{B^2}{4A^2} + \frac{C}{A}= 0
(x+2AB)2−4A2B2+AC=0
将常数部分移到右边,取平方根,再从等式两边同时减去,,这样未知数x的解可以写成下面这个样子:
x
=
−
B
±
B
2
−
4
A
C
2
A
x = \frac{-B±\sqrt{B^2-4AC}} {2A}
x=2A−B±B2−4AC
这里,“±”符号表示有两个解,一个带正号,一个带负号。因此,3±1等于“2或4”。还需要注意到的一点就是决定了解是否是实数的是开平方的那坨常数计算出来的结果:
D ≡ B 2 − 4 A C D≡ B^2 - 4AC D≡B2−4AC
这个也就是二次方程的判别式。如果D > 0,有两个实解(也称为根)。如果D = 0,则存在一个实数解(一个“二重”根)。如果D <0,则没有实解。
例如,
2
x
2
+
6
x
+
4
=
0
2x^2 +6x +4 = 0
2x2+6x+4=0的根是x= -1和x= -2,方程
x
2
+
x
+
1
x^2 + x+1
x2+x+1没有实数解。这些方程的判别式分别是D = 4和D = -3,所以我们期望给出的解的数量。在程序中,首先计算D,如果D为负,则返回“无根”而不取平方根,这通常是一个好的解决方法
2.3 三角学 Trigonometry
在图形学中,我们在许多情况下都会使用基本三角学。通常情况下,三角学并不是什么稀奇古怪的东西,记住它的基本定义往往是有帮助的。
2.3.1 角度 Angles
我们应该回到角度的定义上来,这样我们才能把角度的概念推广到球上。在两条线段(来自原点的无限射线)或方向之间形成一个角,必须使用一些转换概念来决定它们之间所形成的角的具体的值,并且这个角度有两种可能,如图所示。角度由它在单位圆上切出的弧段的长度来定义。一个常见的惯例是使用较小的弧长,角度的符号由两条半直线指定的顺序决定。使用这种惯例,所有的角都在[-π, π]范围内。
两条半径线段将单位圆切成两条弧。任何一条弧的长度都是两条半径线段之间的有效角度。或者我们可以按照约定,以较小的长度为角,或者两条半径线段按一定的顺序指定,决定角的弧是从第一条半径线段逆时针延伸到第二条半线的弧。这些角中的每一个都是被两个方向“切割”的单位圆的弧长。因为单位圆的周长是2n,所以两个可能的角和是2n。弧长单位为弧度。另一个常见的单位是度,圆的周长是360°。因此,π弧度的角度为180°,通常表示为180°。度与弧度的换算公式为:度= 10弧度;度=1度,转换关系如下:
D
e
g
r
e
s
s
=
180
π
r
a
d
i
a
n
s
Degress = \frac{180}{π} radians
Degress=π180radians
R a d i a n s = π 180 d e g r e s s Radians= \frac{π}{180} degress Radians=180πdegress
2.3.2 三角函数 Trigonometric Functions
图2.7 毕达哥拉斯定理的几何演示
给定一个边长为a、o和h的直角三角形,其中h是最长边(总是对着直角)或斜边的长度,一个重要的关系可以用毕达哥拉斯定理来描述:
a
2
+
o
2
=
h
2
a^2 +o^2 = h^2
a2+o2=h2
从图2.7中可以看出这是正确的,其中大正方形的面积为
(
a
+
o
)
2
(a+o)^2
(a+o)2,四个三角形的总面积为2ao,中心正方形的面积为
h
2
h^2
h2。因为三角形和内方框将较大的方框均匀地细分,所以我们有
2
a
o
+
h
2
=
(
a
+
o
)
2
2ao + h^2 = (a+ o)^2
2ao+h2=(a+o)2,这很容易被转换成上面的形式。我们定义了sin和cos,以及其他基于比值的三角表达式:
这些定义允许我们建立极坐标,其中一个点被编码为距离原点的距离和相对于正x轴的有符号角度(图2.8)。请注意,角度在φ E (-π, π] 范围内,并且正角是从正x轴逆时针方向。这个逆时针映射到正数的约定是任意的,但它在图形的很多情况下都是通用的,所以值得我们记下来。
三角函数是周期性的,可以取任何角度作为参数。例如,sin(A) = sin(A +2π)。这意味着当考虑定义域R时,函数是不可逆的。这个问题是通过限制标准逆函数的范围来避免的,这在几乎所有的现代数学库中都是标准的方式。域和范围为
最后一个函数atan2(s, c)通常非常有用。它取一个与sina成正比的s值和一个与cos A成同样倍数的c值,并返回A,这个倍数被假设为正。一种思考方法是,它返回极坐标下二维笛卡尔点(s, c)的角度
2.3.3 有用的恒等式 Useful Identities
本节列出了各种有用的三角恒等式,在这里我们就不去推导这些公式了。
变换等式:
s
i
n
(
−
A
)
=
−
s
i
n
(
A
)
sin(-A)=-sin(A)
sin(−A)=−sin(A)
c o s ( − A ) = c o s ( A ) cos(-A)=cos(A) cos(−A)=cos(A)
t a n ( − A ) = − t a n ( A ) tan(-A)=-tan(A) tan(−A)=−tan(A)
s i n ( π / 2 − A ) = c o s ( A ) sin(π/2 - A)=cos(A) sin(π/2−A)=cos(A)
c o s ( π / 2 − A ) = s i n ( A ) cos(π/2 - A)=sin(A) cos(π/2−A)=sin(A)
t a n ( π / 2 − A ) = c o t ( A ) tan(π/2 - A)=cot(A) tan(π/2−A)=cot(A)
毕达哥拉斯恒等式:
s
i
n
2
A
+
c
o
s
2
A
=
1
sin^2A + cos^2A = 1
sin2A+cos2A=1
s e c 2 A − t a n 2 A = 1 sec^2A - tan^2A = 1 sec2A−tan2A=1
c s c 2 A − c o t 2 A = 1 csc^2A - cot^2A = 1 csc2A−cot2A=1
加法和减法公式:
s
i
n
(
A
+
B
)
=
s
i
n
A
c
o
s
B
+
s
i
n
B
c
o
s
A
sin(A + B) = sin A cos B + sin B cos A
sin(A+B)=sinAcosB+sinBcosA
s i n ( A − B ) = s i n A c o s B − s i n B c o s A sin(A - B) = sin A cos B - sin B cos A sin(A−B)=sinAcosB−sinBcosA
s i n ( 2 A ) = 2 s i n A c o s A sin(2A) = 2 sin A cos A sin(2A)=2sinAcosA
c o s ( A + B ) = c o s A c o s B − s i n A s i n B cos(A + B) = cos A cos B - sin A sin B cos(A+B)=cosAcosB−sinAsinB
c o s ( A − B ) = c o s A c o s B + s i n A s i n B cos(A - B) = cos A cos B + sin A sin B cos(A−B)=cosAcosB+sinAsinB
c o s ( 2 A ) = c o s 2 A − s i n 2 A cos(2A) =cos^2A - sin^2A cos(2A)=cos2A−sin2A
t a n ( A + B ) = t a n A + t a n B 1 − t a n A t a n B tan(A + B)= \frac{tanA+tanB}{1-tanAtanB} tan(A+B)=1−tanAtanBtanA+tanB
t a n ( A − B ) = t a n A − t a n B 1 + t a n A t a n B tan(A - B)= \frac{tanA-tanB}{1+tanAtanB} tan(A−B)=1+tanAtanBtanA−tanB
t a n ( 2 A ) = 2 t a n A 1 − t a n 2 A tan(2A)= \frac{2tanA}{1-tan^2A} tan(2A)=1−tan2A2tanA
半角公式:
s
i
n
2
(
A
/
2
)
=
(
1
−
c
o
s
A
)
/
2
sin^2(A/2) = (1 -cosA) / 2
sin2(A/2)=(1−cosA)/2
c
o
s
2
(
A
/
2
)
=
(
1
+
c
o
s
A
)
/
2
cos^2(A/2) = (1 +cosA) / 2
cos2(A/2)=(1+cosA)/2
乘法公式:
s i n A s i n B = − ( c o s ( A + B ) − c o s ( A − B ) ) / 2 sin A sin B = -(cos(A + B) - cos(A - B))/2 sinAsinB=−(cos(A+B)−cos(A−B))/2
s i n A c o s B = ( s i n ( A + B ) + s i n ( A − B ) ) / 2 sin A cos B = (sin(A + B) + sin(A - B))/2 sinAcosB=(sin(A+B)+sin(A−B))/2
c o s A c o s B = ( c o s ( A + B ) + c o s ( A − B ) ) / 2 cos A cos B = (cos(A + B) + cos(A - B))/2 cosAcosB=(cos(A+B)+cos(A−B))/2
对于边长为a, b, c的任意三角形,每个三角形的对角分别由A, B, C给出(图2.10),三角形的面积也可以用这些边长来计算:
2.3.4 立体角和球面三角 Solid Angles and Spherical Trigonometry
本节中的传统三角函数处理平面上的三角形。三角形也可以在非平面上定义,在很多领域都有,比如天文学,单位半径球面上的三角形。这些球面三角形的边是球面上大圆(单位半径圆)的片段。对这些三角形的研究是一个叫做球面三角的领域,在图形学中不常用,但有时,当它出现时,它是至关重要的。我们不会在这里讨论它的细节,但希望读者意识到,当这些问题出现时,这些领域是存在的,并且有很多有用的规则,如球面余弦定律和球面正弦定律。有关使用球面三角函数的机械示例,请参阅关于三角形灯采样(投射到球面三角形)的论文(which project to a spherical triangle)(Arvo, 1995b)。
在计算机图形学中更为重要的是立体角。虽然角度可以让我们量化诸如“我的视野中这两个极点的距离是多少”之类的事情,但立体角可以让我们量化诸如“那架飞机覆盖了我的视野的多少”之类的事情。对于传统角度,我们将两根柱子投影到单位圆上,并在单位圆上测量它们之间的长度。我们经常与角度打交道,以至于很多人都忘记了这个定义,因为它对我们来说太直观了。立体角同样简单,但它们似乎更令人困惑,因为我们大多数人都是在成年时学习它们的。对于立体角,我们投射“看到”飞机的可见方向,并将其投射到单位球体上并测量面积。这个面积是立体角,同样弧长是角。角度以弧度为单位,和为2π(单位圆的总长度),立体角以球面度为单位,和为4π(单位球面的总面积)。
2.4 向量 Vectors
图2.11。这两个向量是相同的因为它们的长度和方向相同。向量描述了长度和方向,它可以用箭头来表示。
如果两个向量的长度和方向相同,则它们是相等的,即使我们认为它们位于不同的位置(图2.11)。你应该尽可能地把矢量想象成箭头,而不是坐标或数字。在某些情况下,我们必须在程序中将向量表示为数字,但即使在代码中,它们也应该作为对象进行操作,并且只有低级向量操作应该知道它们的数字表示(DeRose, 1989)。向量用粗体表示,如A。向量的长度用llall表示。单位向量是任何长度为1的向量。零向量是长度为零的向量。零向量的方向是没有定义的。向量可以用来表示许多不同的东西。例如,它们可以用来存储偏移量,也称为位移。如果我们知道“宝藏埋在秘密会面地点向东两步,向北三步的地方”,那么我们就知道偏移量,但我们不知道从哪里开始。向量也可以用来存储位置,位置或点的另一个词。位置可以表示为来自另一个位置的位移。通常,存在一个已知的源位置,所有其他位置都存储为偏移量。注意,位置不是向量。正如我们将要讨论的,你可以将两个向量相加。然而,添加两个位置通常没有意义,除非它是计算位置加权平均时的中间操作(Goldman,1985)。两个偏移量相加是有意义的,这就是偏移量是向量的原因之一。但这强调了位置不是偏移量;它是从特定的原点位置开始的偏移量。偏移量本身并不是位置。
2.4.1 向量操作 Vectors Operations
2.12 两个向量通过首尾相连的排列来相加。这可以按任何前后顺序来做
2.13 向量-a 有相同的长度但是相对a相反的方向
向量具有大多数与实数相关的常用算术运算。两个向量相等当且仅当它们的长度和方向相同。根据平行四边形法则,两个向量相加。该规则指出,两个向量的和是通过将其中一个向量的尾部对着另一个向量的头部来找到的(图2.12)。和向量是由两个向量开始的“完成三角形”的向量。平行四边形由任意一个顺序的和组成。这强调了向量加法是可交换的: a + b = b + a a+b=b+a a+b=b+a。注意,平行四边形规则只是形式化了我们对位移的直觉。想象一下沿着一个向量走,从尾到头,然后沿着另一个向量走。净位移就是平行四边形对角线。您还可以为一个向量创建一个一元减号:-a(图2.13)是一个与a长度相同但方向相反的向量。这也允许我们定义减法: b − a = − a + b b-a=-a+b b−a=−a+b。你可以用一个平行四边形来可视化矢量减法(图2.14)。我们可以写: b − a ≡ − a + b b-a ≡ -a+b b−a≡−a+b
你可以用一个平行四边形来可视化矢量减法(图2.14)。我们可以写为: a + ( b − a ) = b a +(b-a) =b a+(b−a)=b
向量也可以相乘。事实上,有几种与向量有关的积。首先,我们可以通过将向量乘以实数k来缩放它,这只是乘以向量的长度而不改变它的方向。
例如,3.5a是一个与a方向相同的向量,但它的长度是a的3.5倍。我们将在本节后面讨论两个向量的乘积,即点积和叉积,以及三个向量的乘积,即行列式,在第6.2.4.2章中讨论
2.4.2 矢量的笛卡尔坐标 Cartesian Coordinates of a Vector
一个向量的笛卡尔坐标一个二维向量的坐标可以写成任意两个不平行的非零向量的组合。这两个向量的性质叫做线性无关。两个线性无关的向量构成一个二维基,因此这些向量被称为基向量。例如,向量c可以表示为两个基向量a和b的组合(图2.15):
2.15 任意二维向量c是任意两个非平行二维向量a和b的加权和。
注意,权重
a
c
a_c
ac和
b
c
b_c
bc是唯一的。如果两个向量是正交的,基就特别有用;也就是说,它们彼此成直角。如果它们也是单位向量就更有用了因为它们是标准正交的。如果我们假设已知两个这样的“特殊”向量x和y,那么我们可以用它们来表示笛卡尔坐标系中的所有其他向量,其中每个向量都表示为两个实数。例如,向量a可以表示为
a
=
X
a
x
+
Y
a
y
a =\mathcal{X}_ax + \mathcal{Y}_ay
a=Xax+Yay,
其中
x
a
x_a
xa和
y
a
y_a
ya是二维向量a的真实笛卡尔坐标(图2.16)。请注意,这在概念上与式(2.3)没有任何不同,其中基向量不是标准正交的。但是笛卡尔坐标系有几个优点。例如,根据勾股定理,a的长度:
图2.16 向量的二维笛卡尔基。
∣
∣
a
∣
∣
=
x
a
2
+
y
a
2
||a|| = \sqrt{x^2_a+y^2_a}
∣∣a∣∣=xa2+ya2
在笛卡尔坐标系中计算点积、叉积和向量坐标也很简单,我们将在下面几节中看到。按照惯例,我们可以将a的坐标写成有序对 ( x a , y a ) (x_a,y_a) (xa,ya)
a = [ x a y a ] a =\left[ \begin{array}{cc}x_a\\y_a\end{array} \right] a=[xaya]
我们使用的形式取决于排版的便利性。我们偶尔也会将向量写成行矩阵,我们将其表示为 a T a^T aT:
a T = [ x a y a ] a^T = \left[ \begin{array}{cc}x_a &y_a\end{array} \right] aT=[xaya]
我们也可以用笛卡尔坐标表示3D, 4D等向量。对于3D情况,我们使用一个基向量z,它与x和y都正交。
2.4.3 点积 Dot Prodct
点积两个向量相乘最简单的方法是点积。a和b的点积记为a ·b,通常称为标量积,因为它返回一个标量。点积返回的值与参数的长度和它们之间的角度 ϕ 相关(图2.17):
点积在图形程序中最常见的用途是计算两个向量夹角的余弦。点积也可以用来求一个向量在另一个向量上的投影。这是向量a的长度a→b,它以直角投影到向量b上(图2.18):
图2.17 点积与长度和角度有关,是图形学中最重要的公式之一。
点积遵循我们在实际算术中熟悉的结合律和分配律:
图 2.18 a在b上的投影是由公式(2.5)得到的长度。
如果二维向量a和b用笛卡尔坐标表示,我们可以利用
x
⋅
x
=
y
⋅
y
=
1
x·x =y·y = 1
x⋅x=y⋅y=1 和
x
⋅
y
=
0
x·y = 0
x⋅y=0,推导出它们的点积为
同样在三维中我们可以发现:
a
⋅
b
=
x
a
x
b
+
y
a
y
b
+
z
a
z
b
a·b=x_ax_b + y_ay_b+z_az_b
a⋅b=xaxb+yayb+zazb
2.4.4 叉乘 Cross Prodct
叉乘a x b通常只用于三维向量;广义叉乘在本章附注中的参考文献中讨论。
叉乘返回一个垂直于叉乘的两个参数的三维向量。结果向量的长度与sin ϕ相关:
∣
∣
a
∗
b
∣
∣
=
∣
∣
a
∣
∣
∣
∣
b
∣
∣
s
i
n
ϕ
||a*b||=||a|| ||b||sinϕ
∣∣a∗b∣∣=∣∣a∣∣∣∣b∣∣sinϕ
lla x bll 的大小等于由向量a和b组成的平行四边形的面积。此外,a x b与a和b都垂直(图2.19)。注意,这样的向量只有两个可能的方向。根据定义,x、y和z轴方向上的向量为:
x
=
(
1
,
0
,
0
)
x=(1,0,0)
x=(1,0,0)
y
=
(
0
,
1
,
0
)
y= (0,1,0)
y=(0,1,0)
z
=
(
0
,
0
,
1
)
z=(0,0,1)
z=(0,0,1)
我们约定x * y必须在正负Z方向上。这个选择有点随意,但是它的标准定义是这样的:
z
=
x
∗
y
z =x*y
z=x∗y
图2.19 向量a x b的外积是一个垂直于向量a和b的三维向量,它的长度等于所示的平行四边形的面积。这三个笛卡尔单位向量的所有可能的排列是
由于公式中存在sinϕ,我们还知道矢量叉乘本身是零矢量,所以,
x
∗
x
=
0
x*x= 0
x∗x=0,以此类推。注意,叉乘不是可交换的,即
x
∗
y
=
y
∗
x
x*y =y *x
x∗y=y∗x。细心的读者会注意到,上面的讨论并没有让我们描绘出笛卡尔轴如何关联的明确图像。更具体地说,如果我们把x和y放在平面上,x指向东方,y指向北方,那么z是指向天空还是地面?通常的惯例是让z轴指向天空。这被称为右手坐标系。这个名字来自于用你的右手手掌和手指“抓住”x并向y方向旋转的记忆方案。向量z应该与你的拇指对齐。如图2.20所示。
图2.20 叉乘的右手法则。想象一下,把你的右手掌的底部放在a和b的尾部连接处,把a的箭头推向b。你伸出的右手拇指应该指向a × b。
叉乘有下面这些性质:
a × ( b + c ) = a × b + a × c a × (b + c) = a × b + a × c a×(b+c)=a×b+a×c,
和
a × ( k b ) = k ( a × b ) a × (kb) = k(a × b) a×(kb)=k(a×b)
但是,右手法则的运算结果却又呈现出下面的等式:
a × b = − ( b × a ) a × b = -(b × a) a×b=−(b×a)
在笛卡尔坐标系中,我们可以如此展开叉乘的计算过程:
所以,写成坐标的形式的话,叉乘是这样的
2.4.5 标准正交基与坐标框架 Orthonormal Bases and Coordinate Frames
管理坐标几乎是任何图形程序的核心任务之一;关键是管理标准正交基。任何两个二维向量u和v的集合都形成一个标准正交基,前提是它们是正交的(在直角上)并且每个向量都是单位长度。因此,
∣
∣
u
∣
∣
=
∣
∣
v
∣
∣
=
1
||u|| = ||v|| = 1
∣∣u∣∣=∣∣v∣∣=1 , 且
u
⋅
v
=
0
u·v=0
u⋅v=0
在3D中,三个向量u v w形成一组标准正交基,如果
∣
∣
u
∣
∣
=
∣
∣
v
∣
∣
=
∣
∣
w
∣
∣
=
1
||u|| = ||v|| = ||w||=1
∣∣u∣∣=∣∣v∣∣=∣∣w∣∣=1,且
u
⋅
v
=
v
⋅
w
=
w
⋅
u
=
0
u·v=v·w=w·u=0
u⋅v=v⋅w=w⋅u=0
这个标准正交基是右手系的
w
=
u
⋅
v
w=u·v
w=u⋅v
否则,它就是左手系的。
请注意,笛卡尔标准正交基只是无限个可能的标准正交基中的一个。它的特殊之处在于它和它的隐式原点位置用于程序中的低级表示。因此,向量x、y和z从未显式地存储,规范原点位置o也不存储。全局模型通常存储在这个规范坐标系中,因此通常称为全局坐标系。然而,如果我们想要使用另一个原点p和标准正交基向量u v w的坐标系,那么我们就明确地存储这些向量。这样的系统称为参照系或坐标系。例如,在飞行模拟器中,我们可能想要维护一个原点在飞机前端的坐标系,标准正交基与飞机对齐。同时,我们将拥有主规范坐标系(图2.21)。与特定对象(如平面)相关联的坐标系通常称为局部坐标系。在较低的级别上,局部帧存储在规范坐标中。例如,如果u有坐标
(
x
u
,
y
u
,
z
u
)
(x_u,y_u,z_u)
(xu,yu,zu),
u
=
x
u
x
+
y
u
y
+
z
u
z
u=x_ux+y_uy +z_uz
u=xux+yuy+zuz
一个位置隐式地包含了一个标准原点的偏移量:
p
=
o
+
x
p
x
+
y
p
y
+
z
p
z
p=o+x_px+ y_py +z_pz
p=o+xpx+ypy+zpz,
其中
(
x
p
,
y
p
,
z
p
)
(x_p,y_p,z_p)
(xp,yp,zp)是p的坐标。注意,如果我们存储一个相对于u-v-w坐标系的向量A,我们存储的是一个三维度Ua,Va,wa),我们可以从几何上解释为:
a
=
u
a
u
+
u
a
v
+
w
a
w
a = u_au + u_av + w_aw
a=uau+uav+waw
要获得存储在u-v-w坐标系中的向量a的规范坐标,只需回忆一下u、v和w本身是以笛卡尔坐标存储的,因此如果显式求值,表达式
u
a
u
+
v
a
v
+
w
a
w
u_au + v_av + w_aw
uau+vav+waw已经是笛卡尔坐标了。为了得到存储在规范坐标系中的向量b的u-v-w坐标,我们可以使用点积:
u
b
=
u
⋅
b
;
v
b
=
v
⋅
b
;
w
b
=
w
⋅
b
;
u_b=u·b; v_b = v·b; w_b =w·b;
ub=u⋅b;vb=v⋅b;wb=w⋅b;
图2.21 总有一个原点为o、基为x、y和z的主坐标系或“规范”坐标系。这个坐标系通常被定义为与全局模型对齐,因此常被称为“全局”或“世界”坐标系。源向量和基向量从不显式地存储。所有其他向量和位置都存储在与全局框架相关的坐标中。与该平面关联的坐标系以全局坐标的形式显式存储。
这是可行的,因为我们知道对于一些
u
b
u_b
ub,
v
b
v_b
vb和
w
b
w_b
wb
u
b
u
+
u
b
v
+
w
b
w
=
b
u_bu +u_bv + w_bw = b
ubu+ubv+wbw=b,
并且点积分离了
u
b
u_b
ub坐标:
u
⋅
b
+
u
b
(
u
⋅
u
)
+
v
b
(
u
⋅
v
)
+
w
b
(
u
⋅
w
)
=
u
b
u·b +u_b(u·u) + v_b(u·v)+w_b(u·w) = u_b
u⋅b+ub(u⋅u)+vb(u⋅v)+wb(u⋅w)=ub,
这是因为u v w是标准正交的。
使用矩阵来管理坐标系的变化将在7.2.1节和7.5节中讨论。
2.4.6 从单个向量构造基 Constructing a Basis from a Single Vector
通常我们需要一个与给定向量对齐的标准正交基。也就是说,给定一个向量a,我们想要一个标准正交的u, v和w,使得w指向与a相同的方向(Hughes & Möller, 1999),但我们并不特别关心u和v是什么。一个向量不足以唯一地确定答案;我们只需要一个可靠的程序来找到任何一个可能的基。这可以通过下面的叉乘来实现。首先,令w为a方向上的单位向量:
w
=
a
∣
∣
a
∣
∣
w= \frac{a}{||a||}
w=∣∣a∣∣a
然后,选择任意与w不共线的向量t,利用叉乘得到垂直于w的单位向量u:
u
=
t
∗
w
∣
∣
t
∗
w
∣
∣
u= \frac{t*w}{||t*w||}
u=∣∣t∗w∣∣t∗w
当然,同样的过程可以用任意顺序来构造这三个向量;只要注意叉乘的顺序就可以确保基是右手系的。
如果t与w共线,分母会消失,如果它们几乎共线,结果的精度会很低。找到一个与w完全不同的向量的简单步骤是,从t等于w开始,并将t的最小大小分量改为1。例如,如果
w
=
(
1
/
2
,
1
/
2
,
0
)
w = (1 / \sqrt2, 1 /\sqrt2, 0)
w=(1/2,1/2,0),那么
t
=
(
1
/
2
,
−
1
/
2
,
1
)
t = (1 / \sqrt2, -1 / \sqrt2, 1)
t=(1/2,−1/2,1)。一旦w和u到手,完成基就很简单了:
v
=
w
×
u
v=w×u
v=w×u
使用这种结构的一个例子是表面阴影,其中需要一个与表面法线对齐的基底,但围绕法线的旋转通常不重要。对于严肃的生产代码,最近皮克斯的研究人员开发了一种相当出色的方法,用于从两个向量构建一个向量,其紧凑性和效率令人印象深刻(Duff et al., 2017)。他们提供了经过实战测试的代码,鼓励读者使用它,因为它在整个行业中使用时没有出现什么问题。