第七章 2D 和 3D 空间中的基础数学与几何
引言
- 实现原则
- 如果你对一个数学问题的理解足够透彻,则一定写一个程序实现它
记号
与传统的数学记号相同
集合
- 集合 B 和 集合 C 的笛卡尔乘积 定义为:
- B × C = { ( b , c ) : b ∈ B , c ∈ C } B × C = \{(b,c):b ∈ B, c ∈ C\} B×C={(b,c):b∈B,c∈C}
- 读作 B 叉乘 C,但称为 笛卡尔乘积,保留向量叉乘的术语
- 乘积 R×R 记作 R 2 R^2 R2,更高阶的是 R 3 R^3 R3 R 4 R^4 R4 等等…
函数
-
函数的表示:
- f : R → R : x → x 2 f:R → R : x → x^2 f:R→R:x→x2
- f 是函数的名字
- 第一个冒号后所示的两个集合,箭头左侧是 定义域 右侧是 陪域
- 第二个冒号后所示的是 定义域中某元素 x 到 陪域中对应元素的 映射规则
-
g : R → R 0 + : x → x 2 g:R → R_0^+ : x → x^2 g:R→R0+:x→x2
- 函数 f 与 函数 g 是有区别的。对于函数 g,函数值得集合 覆盖了整个陪域。而对于函数 f,函数值得集合只是陪域的一个真子集。
- 函数 g 称为 满射,而函数 f 不是
-
h : R 0 + → R 0 + : x → x 2 h : R_0^+ → R_0^+ : x → x^2 h:R0+→R0+:x→x2
- 函数 h 与 函数 g 又不同。函数 h 不仅仅满射,函数 h 定义域内任何两个元素都不会对应于陪域中同一个元素。换句话说有 h(a) = h(b) 则一定有 a = b
- 我们成这样的属性为 单射。即 函数 h 不仅满射 而且 单射
-
一个 既是 单射 又是 满射 的函数,具有 逆函数,以函数 h 举例
- h − 1 : R 0 + → R 0 + : x → x h^{-1} : R_0^+ → R_0^+ : x → \sqrt{x} h−1:R0+→R0+:x→x
- 更一般的说,如果
- f : C → D f : C → D f:C→D
- 既是 单射 又是 满射(或者说双射),那么它的逆函数:
- f − 1 : D → C f^{-1} : D → C f−1:D→C
- 是满足如下条件的唯一函数:
- f − 1 ( f ( x ) ) = x f^{-1}(f(x)) = x f−1(f(x))=x,对于所有 x ∈ D
- f ( f − 1 ( y ) ) = y f(f^{-1}(y)) = y f(f−1(y))=y,对于所有 y ∈ C
-
分段函数(略)
-
反正切函数
- 定义 从 R 到 开区间 ( − 2 π / 2 , 2 / p i / 2 ) (-2\pi/2, 2/pi/2) (−2π/2,2/pi/2) 的 arctan函数(反正切函数) 为 正切函数的 逆函数。
- 反正切函数大多用在角度值的计算。
- tan = cos / sin = y / x,arctan 同理
-
程序如何计算三角函数呢?
- 三角函数作为基本初等函数,我们似乎无法把它向下拆解,同时也无法很好的用程序表达。但是构造出其近似式,得出其近似值。
- 泰勒公式/麦克劳林公式:
- 泰勒公式是用多项式函数逼近一个给定函数的方法,也是机器学习中 梯度迭代的核心思想。
- 泰勒公式的定义:
- 如果函数
f
(
x
)
f(x)
f(x) 在含
x
0
x_0
x0 的某个开区间 (a, b) 内具有
(
n
+
1
)
(n + 1)
(n+1)阶导数,则对任意 x ∈ (a, b) 有
f ( x ) = f ( x 0 ) 0 ! + f ( x 0 ) ˙ 1 ! ∗ ( x − x 0 ) + f ( x 0 ) ¨ 2 ! ∗ ( x − x 0 ) 2 + f x 0 ¨ ˙ 3 ! ∗ ( x − x 0 ) 3 + . . . + f ( n ) ( x 0 ) n ! ∗ ( x − x 0 ) n + R n ( x ) f(x) = \frac{f(x_0)}{0!} + \frac{\dot{f(x_0)}}{1!}*(x-x_0) + \frac{\ddot{f(x_0)}}{2!}*(x-x_0)^2 + \frac{\dot{\ddot{f{x_0}}}}{3!}*(x-x_0)^3 + ... + \frac{f^{(n)}(x_0)}{n!}*(x-x_0)^n + R_n(x) f(x)=0!f(x0)+1!f(x0)˙∗(x−x0)+2!f(x0)¨∗(x−x0)2+3!fx0¨˙∗(x−x0)3+...+n!f(n)(x0)∗(x−x0)n+Rn(x)
-
其中 R_n(x) 为余项,即误差,拉格朗日余项表示为 f ( n + 1 ) ( x 0 ) ( n + 1 ) ! ∗ ( x − x 0 ) ( n + 1 ) \frac{f^{(n+1)}(x_0)}{(n+1)!} * (x-x_0)^{(n+1)} (n+1)!f(n+1)(x0)∗(x−x0)(n+1)
-
麦克劳林公式,是泰勒公式的拓展,其将泰勒公式中的 x 0 x_0 x0 限定为 0
f ( x ) = f ( 0 ) 0 ! + f ( 0 ) ˙ 1 ! ∗ x + f ( 0 ) ¨ 2 ! ∗ x 2 + . . . + f ( n ) ( 0 ) n ! ∗ x n + R n ( x ) f(x) = \frac{f(0)}{0!} + \frac{\dot{f(0)}}{1!}*x + \frac{\ddot{f(0)}}{2!}*x^2 + ... + \frac{f^{(n)}(0)}{n!}*x^n + R_n(x) f(x)=0!f(0)+1!f(0)˙∗x+2!f(0)¨∗x2+...+n!f(n)(0)∗xn+Rn(x) -
使用泰勒公式 或 麦克劳林公式可以对函数进行近似求解,但是它们是无穷项的呀?实际上,我们近似到第 n 项 和 近似到第 n + 1 项,它们的差 就是近似到第 n 项的余项,即误差,如果这个误差小于我们的 规定值,那么我们就可以输出近似结果了
实现代码:
public static double Sin(double x)
{
double res = 0;
double last = 0;
int n = 1;
double temp = x;
do
{
last = res;
res += temp;
n+=2;
temp = temp * x * x * (-1) / (n * (n - 1));
}
while (Math.Abs(res - last) > double.Epsilon);
return res;
}
- 这里使用的是麦克劳林的展开式,把 sin 函数代入即可得到 sin 的麦克劳林的展开式,比较简单,这里就不做推导了
- 有了 sin,cos 和 tan 就很简单了,一个是相位的改变,另一个是二者的比值。
- 那么我们是否可以用一个固定的多项式,来对其进行近似呢?
- 实际上是可以的,就像机器学习梯度迭代最后得到一个固定的多项式一样。但实际上我们上面的代码更为合适,因为对于一些输入,其只需要迭代 十几次 就可以得到准确答案,而有的则需要迭代二十多次,如果我们把多项式固定下来,则对于一些输入来说就浪费了一些计算量。
- 显而易见的,sin 的函数曲线与 幂函数曲线类似。我们可以通过构造 二次幂函数,然后将其进行分段构造成一个周期函数来近似 sin,这样做的话,我们的计算效率将会更高。至于这个幂函数该如何得到,这里就不作推导了,使用麦克劳林展开式计算三角函数是程序上更加常用的方法。
S i n x = x − x 3 3 ! + x 5 5 ! − x 7 7 ! + . . . + ( − 1 ) n + 1 ∗ x ( 2 n − 1 ) ( 2 n − 1 ) ! Sinx = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + ... + (-1)^{n + 1}*\frac{x^{(2n-1)}}{(2n - 1)!} Sinx=x−3!x3+5!x5−7!x7+...+(−1)n+1∗(2n−1)!x(2n−1)
坐标
略
坐标运算
- 中点计算公式:
- M = ( x 1 + x 2 2 , y 1 + y 2 2 ) M = (\frac{x_1+x_2}{2}, \frac{y_1+y_2}{2}) M=(2x1+x2,2y1+y2)
- 还可以写成
- M = 1 2 ( x 1 , y 1 ) + 1 2 ( x 2 , y 2 ) M = \frac{1}{2}(x_1,y_1) + \frac{1}{2}(x_2,y_2) M=21(x1,y1)+21(x2,y2)
- 像中的计算公式这样的,各项系数之和为 1 (1/2 + 1/2 = 1),我们称之为 仿射。该类组合结果在不同的坐标系下具有不变性。
- 我们设两点:P: ( x 1 , y 1 ) (x_1,y_1) (x1,y1) Q: ( x 2 , y 2 ) (x_2, y_2) (x2,y2)
- 那么中点计算公式得到的就是 P 到 Q 连线的中点
- 那么 1 3 P + 2 3 Q \frac{1}{3}P + \frac{2}{3}Q 31P+32Q 呢?
- 就是 P 到 Q 连线的三分点处
- 我们甚至可以取负数: − 1 3 P + 4 3 Q \frac{-1}{3}P + \frac{4}{3}Q 3−1P+34Q 这也仿射条件,且该点位于 P Q 连线所在直线上
- 于是根据 仿射我们可以得到:
- γ : R → R 2 : t → ( 1 − t ) P + t Q γ : R → R^2 : t → (1 - t)P + tQ γ:R→R2:t→(1−t)P+tQ
- 上式称为连接 P 和 Q的直线的参数化方程,变量 t 是参数。
向量、矩阵
- n维向量可以看作是一个 n×1 的矩阵
- m × n 的矩阵,表示为由 m行 n列 元素组成的矩阵
- 矩阵的转置操作:
- 转置可以把 m × n 的矩阵变为 n × m 的矩阵。只需要将矩阵 ij 位置上的元素置为 原来矩阵 ji 位置上的元素
- 矩阵A 的 转置矩阵记作 A T A^T AT
- 向量是矢量,即如果两个向量在坐标系下其位置不同,但是方向和长度相同,则这两个向量是同一个向量
- 向量的长度 又称为 向量的模,记为 ∣ ∣ v ∣ ∣ ||v|| ∣∣v∣∣,长度为 1 的向量称为单位向量,把一个向量变为单位向量,就是该向量除以自己的模,这一过程称为 归一化
- 向量运算 略,主要提一点,向量的叉乘遵循右手定则
- 记 × [ v x v y ] = [ − v y v x ] ×\left[ \begin{matrix} v_x \\ v_y \end{matrix} \right] = \left[ \begin{matrix} -v_y \\ v_x \end{matrix} \right] ×[vxvy]=[−vyvx] 为向量的"积",也记为 v ┴ v┴ v┴ 或 × v ×v ×v,其也是向量 v 的法向量
- 点 和 点 之间的差为一个向量
- 点 与 向量的和 为一个点
- 矩阵乘法:
-
如矩阵 A 是 m × k 的矩阵,矩阵 B 是 k × n 的矩阵,则二者可以进行乘法,得到的是 m × n 的矩阵
-
简单记为 矩阵 A 的 第 i 行,分别与矩阵 B 的 每列(即当前列为 第 j 列),对应元素相乘相加,即为 结果矩阵的 i,j 位置的元素
-
- 隐式直线
- 水平集:
- 如果 F : R 2 → R F : R^2 → R F:R2→R 是一个函数,那么对于任一 c,可定义一个集合:
- L c = { ( x , y ) : F ( x , y ) = c } L_c=\{(x,y) : F(x,y) = c\} Lc={(x,y):F(x,y)=c}
- 称 F 在 c 处的 水平集(类似于等高线图)
- 那么显然,不同的水平集之间是没有交集的,即不同的等高线是不会相交的
- 特别的,即 c = 0 的水平集为 函数 F 的 零水平集
- 找到一个函数 F,使其在经过 P、Q 两点所在直线上为 0,即该直线上的点坐标 代入结果都为 0
- 那么我们已经知道 P、Q 所在直线的法向量: n = × ( P − Q ) = ( P − Q ) ┴ n = ×(P - Q) = (P - Q)┴ n=×(P−Q)=(P−Q)┴
- 而又:直线上的向量与该直线法向的点乘为 0,即设 X 是直线上一点有 ( X − P ) • n = 0 (X - P) • n = 0 (X−P)•n=0
- 那么就有: F ( X ) = ( X − P ) • n F(X) = (X - P) • n F(X)=(X−P)•n
- 上面就是直线的标准隐式形式
- 标准隐式可以 转换为 直线方程
- 设 P 为 ( c 1 , c 2 ) (c_1, c_2) (c1,c2),n 为 [ a b ] \left[ \begin{matrix} a \\ b \end{matrix} \right] [ab]
- 那么标准隐式就是 ( ( x , y ) − ( c 1 , c 2 ) ) ⋅ [ a b ] ((x, y) - (c_1, c_2)) · \left[ \begin{matrix} a \\ b \end{matrix} \right] ((x,y)−(c1,c2))⋅[ab]
- [ ( x − c 1 ) ( y − c 2 ) ] ⋅ [ a b ] \left[ \begin{matrix} (x - c_1) \\ (y - c_2) \end{matrix} \right] · \left[ \begin{matrix} a \\ b \end{matrix} \right] [(x−c1)(y−c2)]⋅[ab]
- 即 a ( x − c 1 ) + b ( y − c 2 ) = 0 a(x - c_1) + b(y - c_2) = 0 a(x−c1)+b(y−c2)=0
- 即 a x + b y − c 1 − c 2 = 0 ax + by - c_1 - c_2 = 0 ax+by−c1−c2=0
- 水平集:
- 图形学一般避免使用 y = mx + b 的形式描述直线,即 斜率-截距 形式。其中 m 为斜率,b 为截距。因为斜截式无法表示垂直的直线(直线垂直于 x 轴,该直线无斜率和截距,因为斜截式是一种函数表示,垂直于x轴的图像,也不满足函数条件)
矩阵乘法的代码实现:
public static int[,] MatrixX(int[,] a, int[,] b)
{
if (a == null || b == null) return null;
int ac = a.GetLength(0);
int br = b.GetLength(1);
if (a.GetLength(0) != b.GetLength(1)) return null;
int[,] res = new int[ac, br];
for(int col = 0; col < ac; ++col)
{
for(int row = 0; row < br; ++br)
{
int item = 0;
for(int i = 0; i < ac; ++ac) item += a[col, i] * b[i, row];
}
}
return res;
}
直线求交
参数化-参数化直线求交
- 有两条直线的参数化表示:
- γ : R → R 2 : t → t A + ( 1 − t ) B γ : R → R^2 : t → tA + (1 - t)B γ:R→R2:t→tA+(1−t)B
- η : R → R 2 : s → s C + ( 1 − s ) D η : R → R^2 : s → sC + (1 - s)D η:R→R2:s→sC+(1−s)D
- 如果两个直线有交点,则一定有 t 0 t_0 t0 和 s 0 s_0 s0 使得
- t 0 A + ( 1 − t 0 ) B = s 0 C + ( 1 − s 0 ) D t_0A + (1-t_0)B = s_0C + (1-s_0)D t0A+(1−t0)B=s0C+(1−s0)D
- 令 v = A − D v = A - D v=A−D, u = C − D u = C - D u=C−D
- 得到 B − D = − t 0 v + s 0 u B - D = -t_0v + s_0u B−D=−t0v+s0u
- 令两边与 × v ×v ×v 进行点积 得
- ( B − D ) ⋅ ( × v ) = − t 0 v ⋅ ( × v ) + s 0 u ⋅ ( × v ) (B - D)·(×v) = -t_0v·(×v) + s_0u·(×v) (B−D)⋅(×v)=−t0v⋅(×v)+s0u⋅(×v)
- s 0 = ( B − D ) ⋅ ( × v ) u ⋅ ( × v ) s_0 = \frac{(B - D)·(×v)}{u·(×v)} s0=u⋅(×v)(B−D)⋅(×v)
- 如果要求 u 0 u_0 u0 那么两边都点乘 ×u 即可
参数化-隐式直线求交
- γ ( t ) = ( 1 − t ) P + t Q γ(t) = (1 - t)P + tQ γ(t)=(1−t)P+tQ
- l = X : ( X − S ) • n = 0 l = {X : (X - S)•n = 0} l=X:(X−S)•n=0
- 若两直线相交,则有 t 0 t_0 t0 使 γ ( t 0 ) γ(t_0) γ(t0) 在直线 l 上
- 那么就可以得到:
- ( γ ( t 0 ) − S ) ∗ n = 0 (γ(t_0) - S) * n = 0 (γ(t0)−S)∗n=0
- ( ( 1 − t 0 ) P + t 0 Q ) − S ) ∗ n = 0 ((1-t_0)P + t_0Q) - S) * n = 0 ((1−t0)P+t0Q)−S)∗n=0
- ( t 0 ( Q − P ) + ( P − S ) ) ∗ n = 0 (t_0(Q - P) + (P - S)) * n = 0 (t0(Q−P)+(P−S))∗n=0
- 设 u = ( Q − P ) u = (Q - P) u=(Q−P) v = ( P − S ) v = (P - S) v=(P−S) 得:
- t 0 u ∗ n + v ∗ n = 0 t_0u * n + v * n = 0 t0u∗n+v∗n=0
- t 0 = − v ∗ n u ∗ n t_0 = \frac{-v * n}{u * n} t0=u∗n−v∗n
直线求交代码实现:
public struct Vector2
{
public float x;
public float y;
public Vector2(float x, float y)
{
this.x = x;
this.y = y;
}
public static float operator * (Vector2 v1, Vector2 v2)
{
return v1.x * v2.x + v1.y * v2.y;
}
public static Vector2 operator *(Vector2 v1, float v2)
{
return new Vector2(v1.x * v2, v1.y * v2);
}
public static Vector2 operator *(float v1, Vector2 v2)
{
return new Vector2(v1 * v2.x, v1 * v2.y);
}
public static Vector2 operator + (Vector2 v1, Vector2 v2)
{
return new Vector2(v1.x + v2.x, v1.y + v2.y);
}
public static Vector2 operator -(Vector2 v1, Vector2 v2)
{
return new Vector2(v1.x - v2.x, v1.y - v2.y);
}
public static Vector2 operator -(Vector2 v)
{
return new Vector2(-v.x, -v.y);
}
public static Vector2 operator /(Vector2 v1, float v2)
{
return new Vector2(v1.x / v2, v1.y / v2);
}
}
public static Vector2 PPIntersect(Vector2 A, Vector2 B, Vector2 C, Vector2 D)
{
Vector2 v = A - B;
Vector2 u = C - D;
Vector2 xv = new Vector2(-v.y, v.x);
float s0 = (B - D) * xv / (u * xv);
return s0 * C + (1 - s0) * D;
}
public static Vector2 IPIntersect(Vector2 P, Vector2 Q, Vector2 S, Vector2 n)
{
Vector2 v = P - S;
Vector2 u = Q - P;
float t0 = (-v * n) / (u * n);
return (1 - t0) * P + t0 * Q;
}
光线-平面求交
- 给定一条起点为 P、方向向量为 d(故光线上的点为 P + td, t >= 0),已经经过点 Q、法向量为 n 的平面,计算它们的交点。平面上任意一点 X 具有如下属性:
- ( X − Q ) ⋅ n = 0 (X - Q) · n = 0 (X−Q)⋅n=0
- 这是由直线的隐式方程得到的 平面的方程
- 若 光线 与 平面相交,则有 t 0 t_0 t0 使得:
- ( P + t 0 d − Q ) ⋅ n = 0 (P + t_0d - Q) · n = 0 (P+t0d−Q)⋅n=0
- ( P − Q ) ⋅ n + t 0 d ⋅ n = 0 (P - Q) · n + t_0d · n = 0 (P−Q)⋅n+t0d⋅n=0
- t 0 = ( Q − P ) ⋅ n d ⋅ n t_0 = \frac{(Q - P) · n}{d · n} t0=d⋅n(Q−P)⋅n
- 但在求解过程中 d·n 可能为 0,此时 光线方向d 与 平面法向 n 垂直,即 光线与平面没有交点,光线平行与平面。或光线就在平面上,与平面有无数个交点。此时无法求解处 t 0 t_0 t0
- 平面也有其参数化方程,其由平面上一点 Q 和 两个线性无关的向量 u 和 v 定义:
- Q + α u + β v Q + αu + βv Q+αu+βv
- 若光线与平面有交点,则设 t 0 t_0 t0 有
- P + t 0 d = Q + α u + β v P + t_0d = Q + αu + βv P+t0d=Q+αu+βv
- P − Q = α u + β v − t 0 d P - Q = αu + βv - t_0d P−Q=αu+βv−t0d
- 现在问题转换为了 u、v 和 d 的线性组合。令 M 是一个矩阵,它的列向量为这三个向量,则有:
- P − Q = M [ α β − t ] P - Q = M\left[ \begin{matrix} α \\ β \\ -t \end{matrix} \right] P−Q=M⎣⎡αβ−t⎦⎤
- 若 M 不可逆,则光线平行于平面,若 光线在平面内,则有无数的解。
- 可以发现,对于参数化形式的平面求交,光线与隐式平面的求交计算要容易的多
代码:
public struct Ray
{
public Vector3 orgion { get; private set; }
public Vector3 dir { get; private set; }
public Ray(Vector3 orgion, Vector3 dir)
{
this.orgion = orgion;
this.dir = dir;
}
}
public struct Plane
{
public Vector3 Q { get; private set; }
public Vector3 nor { get; private set; }
public Vector3 u { get; private set; }
public Vector3 v { get; private set; }
public Plane(Vector3 Q, Vector3 nor)
{
this.Q = Q;
this.nor = nor;
this.u = Vector3.Zero;
this.v = Vector3.Zero;
}
public Plane(Vector3 Q, Vector3 u, Vector3 v)
{
this.Q = Q;
this.nor = Vector3.Cross(u, v);
this.u = u;
this.v = v;
}
public static bool IntersectWithRay(Ray ray, Plane plane, out Vector3 point)
{
point = Vector3.Zero;
float dn = Vector3.Dot(ray.dir, plane.nor);
if (dn == 0) return false;
float t = Vector3.Dot(plane.Q - ray.orgion, plane.nor) / dn;
point = ray.orgion + t * ray.dir;
return true;
}
}
光线-球求交
球的隐式描述:
(
X
−
Q
)
⋅
(
X
−
Q
=
r
2
(X - Q) · (X - Q = r^2
(X−Q)⋅(X−Q=r2
将射线表示
P
+
t
d
P + td
P+td 代入得到
(
d
⋅
d
)
t
2
+
(
2
d
⋅
v
)
t
+
(
v
⋅
v
−
r
2
)
=
0
(d·d)t^2 + (2d·v)t + (v·v - r^2) = 0
(d⋅d)t2+(2d⋅v)t+(v⋅v−r2)=0
是一个一元二次方程,求解 t 即可,可能有 无解、 1个解、2个解
代码:
public struct Sphere
{
public Vector3 origion;
public float radius;
public Sphere(Vector3 origion, float radius)
{
this.origion = origion;
this.radius = radius;
}
public static int IntersectWithRay(Ray ray, Sphere sphere, out Vector3[] points)
{
points = new Vector3[2];
Vector3 v = ray.orgion - sphere.origion;
float a = Vector3.Dot(ray.dir, ray.dir);
float b = 2 * Vector3.Dot(ray.dir, v);
float c = Vector3.Dot(v, v) - (sphere.radius * sphere.radius);
float t = b * b - 4 * a * c;
if (t < 0) return 0;
float aa = 2 * a;
float b2a = -b / aa;
if (t == 0)
{
points[0] = ray.orgion + ray.dir * b2a;
return 1;
}
float gt2a = Mathf.Sqrt(t) / aa;
points[0] = ray.orgion + ray.dir * (b2a + gt2a);
points[1] = ray.orgion + ray.dir * (b2a - gt2a);
return 2;
}
}
三角形
如图,我们设 A、B、C 为三角形的三个顶点,则可以得到
Q
=
(
1
−
t
)
A
+
t
B
Q = (1-t)A + tB
Q=(1−t)A+tB
R
=
(
1
−
s
)
Q
+
s
C
R = (1-s)Q + sC
R=(1−s)Q+sC
联立两式可以得到:
R
=
(
1
−
s
)
[
(
1
−
t
)
A
+
t
B
]
+
s
C
=
(
1
−
s
)
(
1
−
t
)
A
+
(
1
−
s
)
t
B
+
s
C
R = (1-s)[(1-t)A + tB] + sC = (1-s)(1-t)A + (1-s)tB + sC
R=(1−s)[(1−t)A+tB]+sC=(1−s)(1−t)A+(1−s)tB+sC
这样,令 s、t 的取值范围都为 0~1,我们就可以计算出三角形 ABC 内任意一点 R
这即为 三角形的参数式表达
重心坐标
我们再来看参数表达:
R
=
(
1
−
s
)
(
1
−
t
)
A
+
(
1
−
s
)
t
B
+
s
C
R = (1-s)(1-t)A + (1-s)tB + sC
R=(1−s)(1−t)A+(1−s)tB+sC
该参数方程有三个系数,分别为:
(
1
−
s
)
(
1
−
t
)
(1-s)(1-t)
(1−s)(1−t)、
(
1
−
s
)
t
(1-s)t
(1−s)t、
s
s
s
当 s、t 都取 0~1 范围时,这三个系数都为正数,且三个系数之和为 1:
(
1
−
s
)
(
1
−
t
)
+
(
1
−
s
)
t
+
s
=
(
1
−
s
)
(
1
−
t
+
t
)
+
s
=
1
−
s
+
s
=
1
(1-s)(1-t) + (1-s)t + s = (1-s)(1-t + t) + s = 1-s + s = 1
(1−s)(1−t)+(1−s)t+s=(1−s)(1−t+t)+s=1−s+s=1
因此,我们定义 三角形的重心坐标表示:
R
=
α
A
+
β
B
+
γ
C
R = αA + βB + γC
R=αA+βB+γC
其中:
α
+
β
+
γ
=
1
α + β + γ = 1
α+β+γ=1
练习:
在三角形 ABC 中,边 AB 的中点的重心坐标是什么?
P
=
0.5
A
+
0.5
B
P = 0.5A + 0.5B
P=0.5A+0.5B
三角形的中心的重心坐标:
P
=
0.5
A
+
0.5
B
+
0.5
C
P = 0.5A + 0.5B + 0.5C
P=0.5A+0.5B+0.5C
设
A
=
(
1
,
0
,
0
)
A = (1, 0, 0)
A=(1,0,0),
B
=
(
0
,
1
,
0
)
B = (0, 1, 0)
B=(0,1,0),
C
=
(
0
,
0
,
1
)
C = (0, 0, 1)
C=(0,0,1),点 P 的重心坐标为 α、β、γ,那么点 P 的 3D 坐标是什么?
P
=
α
(
1
,
0
,
0
)
+
β
(
0
,
1
,
0
)
+
γ
(
0
,
0
,
1
)
=
(
α
,
β
,
γ
)
P = α(1, 0, 0) + β(0, 1, 0) + γ(0, 0, 1) = (α, β, γ)
P=α(1,0,0)+β(0,1,0)+γ(0,0,1)=(α,β,γ)
重心坐标常用到的两种解释:
- 在一个非退化三角形中,点 P 的 α 坐标等于 点P 到 边BC 的垂直距离 乘以 一个比例因子。该比例因子使得 点A 的 α 坐标正好为 1. 该结论可以由两种方法来进行验证:
- 用顶点的坐标代入计算验证
- 点 P 到边的 垂直距离,和 α坐标,都是 平面仿射函数(线性变换 + 位移),因此若在 三个顶点处,它们的取值都一样,则就可以保证在三角形内部任意一点它们是一样的
- 根据上面的解释,我们可以得到 由点 P、B、C 构成的三角形,且 P 的 α坐标为:
α = S P B C S A B C α = \frac{S_{PBC}}{S_{ABC}} α=SABCSPBC
推导过程:
设 比例因子为 θ,点 A 到 边BC 的垂直距离为 h,点 P 到 边BC 的垂直距离为 h’ 得:
θ h = 1 θh = 1 θh=1
θ h ′ = α θh' = α θh′=α
S A B C = h ∗ B C ∗ 0.5 S_{ABC} = h*BC*0.5 SABC=h∗BC∗0.5
S P B C = h ′ ∗ B C ∗ 0.5 S_{PBC} = h'*BC*0.5 SPBC=h′∗BC∗0.5
S P B C S A B C = h ′ h = α / θ 1 / θ = α \frac{S_{PBC}}{S_{ABC}} = \frac{h'}{h} = \frac{α/θ}{1/θ} = α SABCSPBC=hh′=1/θα/θ=α
空间三角形 和 射线 与 三角形求交
上面的三角形参数化 和 重心坐标 与维度无关,因此在 3D空间下 仍然适用
接下来我们思考,射线 与 三角形 求交的解法
三角形所在的平面是唯一的,且我们知道三角形三个顶点的位置,那么就可以得到三角形所在平面的表示
我们的策略为,先计算 射线 与 三角形所在平面的交点 Q,然后 计算 交点Q 的重心坐标,确定其是否在三角形内。
为了加速求交计算,我们会让三角形存储一些预计算数据包括:
n
n
n 三角形法向量
A
B
┴
AB┴
AB┴ 在三角形所在平面内,且 垂直于 AB,且
(
C
−
A
)
⋅
A
B
┴
=
1
(C-A) · AB┴ = 1
(C−A)⋅AB┴=1
A
C
┴
AC┴
AC┴ 在三角形所在平面内,且 垂直于 AC,且
(
B
−
A
)
⋅
A
C
┴
=
1
(B-A) · AC┴ = 1
(B−A)⋅AC┴=1
我们先看 γ 的计算:
首先证明 AB┴ 的模,为 C 到 AB 的垂直距离 的 倒数:
根据之前的重心坐标的两种解释,我们可以知道 设我们要求 X 的重心坐标 则:
γ
=
S
X
A
B
S
A
B
C
=
h
′
h
γ = \frac{S_{XAB}}{S_{ABC}} = \frac{h'}{h}
γ=SABCSXAB=hh′
那么接下来我们就要找 h’ 即,X 到 AB 的垂直距离:
这样我们就得到了:
γ
=
(
X
−
B
)
⋅
A
B
⊥
γ = (X-B) \cdot AB^{\bot}
γ=(X−B)⋅AB⊥
或
γ
=
(
X
−
A
)
⋅
A
B
⊥
γ = (X-A) \cdot AB^{\bot}
γ=(X−A)⋅AB⊥
同理得:
β
=
(
X
−
A
)
⋅
A
C
⊥
β = (X-A) \cdot AC^{\bot}
β=(X−A)⋅AC⊥
α
=
1
−
γ
−
β
α = 1 - γ - β
α=1−γ−β
我们最后再看一下
A
B
⊥
AB^{\bot}
AB⊥ 和
A
C
⊥
AC^{\bot}
AC⊥ 是怎么计算得到的
对于
A
B
⊥
AB^{\bot}
AB⊥
我们先
A
B
⊥
=
n
×
(
B
−
A
)
AB^{\bot} = n \times (B - A)
AB⊥=n×(B−A) 得到正确方向的单位向量
然后
A
B
⊥
/
=
(
C
−
A
)
A
˙
B
⊥
AB^{\bot} /= (C-A) \dot AB^{\bot}
AB⊥/=(C−A)A˙B⊥ 得到 正确的模长
同理,对于
A
C
⊥
AC^{\bot}
AC⊥
我们先
A
C
⊥
=
n
×
(
C
−
A
)
AC^{\bot} = n \times (C - A)
AC⊥=n×(C−A) 得到正确方向的单位向量
然后
A
B
⊥
/
=
(
B
−
A
)
A
˙
C
⊥
AB^{\bot} /= (B-A) \dot AC^{\bot}
AB⊥/=(B−A)A˙C⊥ 得到 正确的模长
半平面 和 三角形
函数 F(x, y) = Ax + By + C 可以映射为
R
3
R^3
R3 即 三维空间中的一个平面
它是怎么映射的呢?我们看 xy平面:
将 Ax + By + C = 0 这条直线,在 z方向展开,即可得到 F(x, y) = Ax + By + C 在 三维空间映射的 平面
同时我们可以看到 原点 到 点(A, B) 的向量,与 Ax + By + C = 0 这条直线垂直,而 Ax + By + C = 0 这条直线 实际上就是 F(x, y) = Ax + By + C 在三维映射平面 与 xy平面 的交线
所以 F(x, y) = Ax + By + C 映射的平面,与 xy平面的交线(称为 l) 一定与 原点 到 (A, B) 这一向量垂直(称为 AB)
那么 在 F(x, y) 平面上,位于 l 往 AB 正方向一侧,F(x, y) > 0
在 位于 l 往 AB 负方向一侧, F(x, y) < 0
在 位于 l 上, F(x, y) = 0
我们称 F(X) >= 0 的点构成的平面为 半平面
一个在 xy平面上的三角形,可以由 三个这样的平面 在 xy平面上的交线构成,那么对于 三角形内一点 X 可以得到:
F
1
(
X
)
>
=
0
a
n
d
F
2
(
X
)
>
=
0
a
n
d
F
3
(
X
)
>
=
0
F_1(X) >= 0 and F_2(X) >= 0 and F_3(X) >= 0
F1(X)>=0andF2(X)>=0andF3(X)>=0
关于三角形法向的方向,我们将三角形的三个顶点按一定的顺序排列,然后根据右手定则来确定法向。
一般取逆时针顺序
多边形
多边形通常表示为一个有序的顶点表。不自交的多边形称为简单多边形
凸多边形:取其边界上任意两点,其连线完全位于多边形内 称为 凸多边形
多边形的内部 与 外部:
设
(
P
0
,
P
1
,
P
2
,
.
.
.
,
P
n
)
(P_0, P_1, P_2, ... , P_n)
(P0,P1,P2,...,Pn) 为一多边形,那么线段
P
0
P
1
,
P
1
P
2
,
.
.
.
P_0P_1, P_1P_2, ...
P0P1,P1P2,... 称为该多边形的 边
向量
v
0
=
P
1
−
P
0
v_0 = P_1 - P_0
v0=P1−P0,
v
1
=
P
2
−
P
1
v_1 = P_2 - P1
v1=P2−P1 … 称为 边向量
对于每条边
P
i
P
i
+
1
P_iP_{i+1}
PiPi+1 其朝内的边法向定义为
×
v
i
×v_i
×vi,朝外的边法向定义为
−
(
×
v
i
)
-(×v_i)
−(×vi)
还记得
×
v
i
×v_i
×vi 吗?它在本章开始时被定义,若
v
I
=
(
x
,
y
)
v_I = (x, y)
vI=(x,y) 则有
×
v
I
=
(
−
y
,
x
)
×v_I = (-y, x)
×vI=(−y,x),可以发现
v
i
v_i
vi 与
×
v
i
×v_i
×vi 是垂直的
这样的定义使得我们可以按照一定的顶点排列顺序来表示 有孔的多边形的内部与外部:
如上图,若要判断 平面上一点 是否在 多边形内部,只需要从该点朝任一方向发射一条射线,我们将该射线 与 该多边形的相交情况做下面分类:
- 如果在交点处射线与所交边的 朝内法向的内积(点积)为正,则称该点为 入点
- 如果在交点处射线与所交边的 朝内法向的内积为负,则称该点为出点
- 当然,如果一个交点都没有,该点一定位于该多边形外部
如果 出点数 大于 入点数,则说明该点位于该多边形内部
出点数与入点数之差称作多边形关于该点的绕数
绕数还有一种不同的定义:
对于由顶点
P
1
,
P
2
,
.
.
.
,
P
n
P_1, P_2, ... , P_n
P1,P2,...,Pn 构成的多边形,其关于点 Q 的绕数 与 多边形所有边对点 Q 所张夹角之和有关。若所有夹角之和为 2pi,则绕数为 1;如果为 4pi, 则绕数为 2,依次类推我们有:
n
=
1
2
π
∑
i
=
1
n
c
o
s
−
1
(
(
P
i
+
1
−
Q
)
⋅
(
P
i
−
Q
)
∣
P
i
+
1
−
Q
∣
∣
P
i
−
Q
∣
)
n = \frac{1}{2 \pi} \sum_{i = 1}^{n}cos^{-1}(\frac{(P_{i+1} - Q) \cdot (P_i - Q)}{|P_{i+1} - Q| |P_i - Q|})
n=2π1i=1∑ncos−1(∣Pi+1−Q∣∣Pi−Q∣(Pi+1−Q)⋅(Pi−Q))
非简单多边形的内部
非简单多边形的内部有三种判断规则:
- 正绕数规则:判定绕数取正值的点位于多边形内部
- 奇绕数规则:判定绕数为奇数的点位于内部
- 非零绕数规则:判定绕数不为零的点位于内部
每种规则都有其用途,绘图程序应该允许这 3 种规则并存。
平面多边形的符号面积: 分而治之
设
P
1
=
(
x
1
,
y
1
)
P_1 = (x_1, y_1)
P1=(x1,y1),
P
2
=
(
x
2
,
y
2
)
P_2 = (x_2, y_2)
P2=(x2,y2) 为 xy平面上的两点,Q = (0, 0) 为原点,那么三角形
Q
P
1
P
2
QP_1P_2
QP1P2 的符号面积由下式给出:
1
2
(
x
1
y
2
−
y
1
x
2
)
\frac{1}{2}(x_1y_2 - y_1x_2)
21(x1y2−y1x2)
推导过程:
如果三角形顶点 Q、P1、P2 为逆时针次序,则三角形符号面积为正
如果按顺时针次序则符号面积为负
关于这一点我们可以这样验证:
首先旋转三角形并不会改变三角形符号面积的绝对值,那么我们可以通过旋转三角形,使得 点 P1 在 x 轴上
那么根据 顺、逆 时针排序,会有下面这种情况:
我们再来推导更一般的情况:
给出由顶点
P
0
=
(
x
0
,
y
0
)
P_0 = (x_0, y_0)
P0=(x0,y0)、
P
1
=
(
x
1
,
y
1
)
P_1 = (x_1, y_1)
P1=(x1,y1)、
P
2
=
(
x
2
,
y
2
)
P_2 = (x_2, y_2)
P2=(x2,y2) 构成的三角形,求其符号面积
此时,我们可以设 P0 为坐标系原点,即将 P0 变为 (0, 0), 那么 P1 和 P2 的新坐标就为 (x1 - x0, y1 - y0)和 (x2 - x0, y2 - y0)
代入上面的公式,可得:
即:
S
=
1
2
∑
i
=
0
2
(
x
i
y
i
+
1
−
x
i
+
1
y
i
)
S = \frac{1}{2}\sum_{i=0}^{2}(x_{i}y_{i+1} - x_{i+1}y_{i})
S=21i=0∑2(xiyi+1−xi+1yi)
注意:下标要对 3 取余(模),即 x3 为 x0, y3 为 y0
若要确定一个多边形
P
0
P
1
.
.
.
P
n
P_0P_1 ... P_n
P0P1...Pn 的符号面积,可以采用相同的技术:
首先计算
Q
P
0
P
1
QP_0P_1
QP0P1,
Q
P
1
P
2
QP_1P_2
QP1P2,… ,
Q
P
n
P
0
QP_nP_0
QPnP0,然后去除位于 多边形外的那部分区域,雷焦位于内部的区域的面积,即可得到正确的总面积。最后形式为:
a
r
e
a
=
1
2
∑
i
=
0
n
(
x
i
y
i
+
1
−
y
i
x
i
+
1
)
area = \frac{1}{2}\sum_{i=0}{n}(x_iy_{i+1} - y_ix_{i+1})
area=21i=0∑n(xiyi+1−yixi+1)
这里下标同样对 n+1 进行取余(模)
空间多边形的法向量
前面已经给出 对于三角形
P
0
P
1
P
2
P_0P_1P_2
P0P1P2 的法向量 n,
n
=
(
P
1
−
P
0
)
×
(
P
2
−
P
1
)
n = (P_1 - P_0) \times (P_2 - P_1)
n=(P1−P0)×(P2−P1)
特别的,如果这些顶点都恰好共线,则 n = 0
法向量还有一种表示:
n
=
[
A
y
z
A
z
x
A
x
y
]
n = \begin{bmatrix} A_{yz} \\ A_{zx} \\ A_{xy} \end{bmatrix}
n=⎣⎡AyzAzxAxy⎦⎤
A
y
z
A_{yz}
Ayz 为三角形在 yz平面上的投影的面积
A
z
x
A_{zx}
Azx 为三角形在 zx平面上的投影的面积
A
x
y
A_{xy}
Axy 为三角形在 xy平面上的投影的面积
该法向量并不是一个单位向量,实际上其模长为该三角形的面积
这里可以通过
1
2
(
x
0
y
1
−
y
1
x
0
)
\frac{1}{2}(x_0y_1 - y_1x_0)
21(x0y1−y1x0) 来计算在 xy 平面上投影的面积,同理可以计算处 yz、zx 平面上投影的面积
可以发现,在对应分量上,其是 叉乘结果的二分之一
更一般多边形的符号面积
设有位于平面 S 上的一多边形
P
0
P
1
.
.
.
P
n
P_0P_1 ... P_n
P0P1...Pn,其法向量为单位向量 n,则可以在 S 上确定两个正交单位i向量(相互垂直的两个单位向量) x 和 y,使得 x、y 和 n 满足
n
=
x
×
y
n = x \times y
n=x×y。
我们取平面上某点为原点,可以构建 xyn 坐标系,那么就可以在该坐标系下确定每个
P
i
P_i
Pi 的坐标,显然 每个坐标的 z 分量 或者说 第三维度(3D坐标值) 为 0,因为我们的 z轴 就是该平面 法向 n
那么
P
i
P_i
Pi 可以表示为
(
x
i
,
y
i
,
0
)
(x_i, y_i, 0)
(xi,yi,0),这样就可以根据这些坐标进行符号面积的计算。
如果该多边形是三角形,且 符号面积为正,则称为 正向三角形,如果 面积为负 则称为 负向三角形
那么,如果我们的 法向 n 取反向,即 法向取 -n,那么求得的面积 也会取反(因为 x 和 y 中有一轴会取反,这样才能满足
n
=
x
×
y
n = x \times y
n=x×y)。因此,符号面积 和 三角形的取向是依据 带法向的平面 定义的。
以 zx平面为例,如果三角形顶点按逆时针顺序排列,则该三角形为正向三角形
倾斜原理
n’ 为 A平面的法向,n 为 B平面的法向
反过来,B平面上的三角形 T,其沿其法向 n 向 A平面做投影得到的三角形 T’ ,T’ 与 T 的面积二者之间是一个 余弦关系:
T
=
T
′
∗
n
′
⋅
n
T = T' * n' \cdot n
T=T′∗n′⋅n
T’ 沿着 n 向 B平面做投影,同样会在 B平面 得到 T,公式不变
重心坐标的仿射变换不变的性质
仿射变换:线性变换 + 平移变换
线性变换:旋转、缩放、错切、退化/奇异
重心坐标的仿射变换不变的性质即:
对三角形的顶点 和 三角形内任一点 Q 进行 旋转、平移、比例缩放这些变换的组合后,Q 的重心坐标不变。