✺ch3——数学基础

news2024/11/26 21:18:57

目录

    • 3D坐标系和点
    • 矩阵
      • 单位矩阵
      • 转置矩阵
      • 逆矩阵
      • 逆转置矩阵
      • 矩阵的运算
        • 矩阵加法(+)
        • 矩阵乘法(×)
      • 常用的变换矩阵
        • 平移矩阵
        • 缩放矩阵
        • 旋转矩阵
        • 透视矩阵
        • 正射投影矩阵
        • LookAt矩阵
    • 向量
      • 加法和减法
      • 点积
      • 叉积
    • 局部空间和世界空间——模型矩阵M
    • 视觉空间和合成相机——模型-视图矩阵MV
    • 用GLSL函数构建变换矩阵

3D坐标系和点

3D 空间通常用 3 个坐标轴即 x、 y 和 z 来表示。OpenGL 中的坐标系大都是右手坐标系。

3D 空间中的点可以通过使用形如(2, 8, −3)的符号列出 x、y、z 的值来表示。不过,如果用齐次坐标——一种在 19 世纪初首次描述的表示法来表示点会更有用。在每个点的齐次坐标有 4 个值,前 3 个值表示 x、y 和 z,第四个值 w 总是非零值,通常为 1。因此,我们会将之前的点表示为(2, 8, −3, 1)。

齐次坐标将会使我们的图形计算更高效。

用来存储齐次 3D 坐标的 GLSL 数据类型是 vec4(“ vec”代表向量, 同时也可以用来表示点)。GLM 库包含适合在 C++/OpenGL 应用程序中创建和存储含有 3 个和 4 个坐标的(齐次) 点的类,分别叫作 vec3vec4

矩阵

[ A 00 A 01 A 02 A 03 A 10 A 11 A 12 A 13 A 20 A 21 A 22 A 23 A 30 A 31 A 32 A 33 ] \begin{bmatrix} A_{00} & A_{01} & A_{02} & A_{03} \\ A_{10} & A_{11} & A_{12} & A_{13} \\ A_{20} & A_{21} & A_{22} & A_{23} \\ A_{30} & A_{31} & A_{32} & A_{33} \end{bmatrix} A00A10A20A30A01A11A21A31A02A12A22A32A03A13A23A33

GLSL语言中的mat4数据类型用来存储4×4矩阵。同样,GLM库中有mat4类用以实例化并存储4×4矩阵。

单位矩阵

一条对角线的值为1,其余值全为0。

[ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ] \begin{bmatrix} \mathbf{{\color {red}1}}& 0& 0& 0 \\ 0& \mathbf{{\color {red}1}}& 0& 0 \\ 0& 0& \mathbf{{\color {red}1}}& 0 \\ 0& 0& 0& \mathbf{{\color {red}1}} \end{bmatrix} 1000010000100001

任何值乘以单位矩阵都不会改变。

单位矩阵也可以简单地记为一个对角线矩阵:En = diag(1,1,…,1)
根据矩阵乘法的定义,单位矩阵的重要性质为:AEn = A;EnA = A

在GLM中,调用构造函数glm::mat4 m(1.0f);以在变量m中生成单位矩阵

转置矩阵

矩阵转置的计算是通过交换矩阵的行与列完成的。转置,就是旋转置换的意思;给定一个mxn的矩阵,则A的转置就是nxm的矩阵,用 AT 表示。例如:

[ A 00 A 01 A 02 A 03 A 10 A 11 A 12 A 13 A 20 A 21 A 22 A 23 A 30 A 31 A 32 A 33 ] = [ A 00 A 10 A 20 A 30 A 01 A 11 A 21 A 31 A 02 A 12 A 22 A 32 A 03 A 13 A 23 A 33 ] T \begin{bmatrix} A_{00} &A_{01} &A_{02} &A_{03} \\ A_{10} &A_{11} &A_{12} &A_{13} \\ A_{20} &A_{21} &A_{22} &A_{23} \\ A_{30} &A_{31} &A_{32} &A_{33} \end{bmatrix}= \begin{bmatrix} A_{00} &A_{10} &A_{20} &A_{30} \\ A_{01} &A_{11} &A_{21} &A_{31} \\ A_{02} &A_{12} &A_{22} &A_{32} \\ A_{03} &A_{13} &A_{23} &A_{33} \end{bmatrix}^{\mathbf{T}} A00A10A20A30A01A11A21A31A02A12A22A32A03A13A23A33 = A00A01A02A03A10A11A12A13A20A21A22A23A30A31A32A33 T

转置矩阵特性:
1、(A) = A
2、(A+B) = A + B
3、对任意数r,(rA) = rA
4、(AB) = BA

GLSL库和GLM库都有转置函数,分别是transpose(mat4)glm::transpose(mat4)

逆矩阵

一个4×4矩阵的逆矩阵是另一个4×4矩阵,用 M-1 表示。

在矩阵乘法中有如下性质:
M×M-1 = M-1×M = 单位矩阵 E
AB = BA = E,则 A 是可逆的,BA 的逆矩阵(B=A-1)。
若A可逆,则A-1亦可逆,且:(A-1)-1 = A
若A可逆,则AT亦可逆,且:(AT)-1=(A-1)T
若A、B为同阶方阵且均可逆,则AB亦可逆,且:(AB)-1=B-1A-1

GLSL和GLM都提供了mat4.inverse()函数完成矩阵的逆矩阵运算。

逆转置矩阵

要对向量 V 使用变换矩阵 M 进行与对点相同的变换,一般需要计算 M 的逆转置矩阵,记为 (M-1)T,并用所得矩阵乘 V。在某些情况下, M=(M-1)T,在这些情况下只要用 M 就可以了。

矩阵的运算

矩阵加法(+)

[ A + a B + b C + c D + d E + e F + f G + g H + h I + i J + j K + k L + l M + m N + n O + o P + p ] = [ A B C D E F G H I J K L M N O P ] + [ a b c d e f g h i j k l m n o p ] \begin{bmatrix} A+a &B+b &C+c &D+d \\ E+e &F+f &G+g &H+h \\ I+i &J+j &K+k &L+l \\ M+m &N+n &O+o &P+p \end{bmatrix}= \begin{bmatrix} A &B &C &D \\ E &F &G &H \\ I &J &K &L \\ M &N &O &P \end{bmatrix}+ \begin{bmatrix} a &b &c &d \\ e &f &g &h \\ i &j &k &l \\ m &n &o &p \end{bmatrix} A+aE+eI+iM+mB+bF+fJ+jN+nC+cG+gK+kO+oD+dH+hL+lP+p = AEIMBFJNCGKODHLP + aeimbfjncgkodhlp

GLSL 和 GLM 都支持使用重载后的加法“+”运算符,进行矩阵加法。

矩阵乘法(×)

[ A B C D E F G H I J K L M N O P ] × [ a b c d e f g h i j k l m n o p ] = \begin{bmatrix} A &B &C &D \\ E &F &G &H \\ I &J &K &L \\ M &N &O &P \end{bmatrix}\times \begin{bmatrix} a &b &c &d \\ e &f &g &h \\ i &j &k &l \\ m &n &o &p \end{bmatrix}= AEIMBFJNCGKODHLP × aeimbfjncgkodhlp =
[ A a + B e + C i + D m A b + B f + C j + D n A c + B g + C k + D o A d + B h + C l + D p E a + F e + G i + H m E b + F f + G j + H n E c + F g + G k + H o E d + F h + G l + H p I a + J e + K i + L m I b + J f + K j + L n I c + J g + K k + L o I d + J h + K l + L p M a + N e + O i + P m M b + N f + O j + P n M c + N g + O k + P o M d + N h + O l + P p ] \begin{bmatrix} Aa+Be+Ci+Dm &Ab+Bf+Cj+Dn &Ac+Bg+Ck+Do &Ad+Bh+Cl+Dp\\ Ea+Fe+Gi+Hm &Eb+Ff+Gj+Hn &Ec+Fg+Gk+Ho &Ed+Fh+Gl+Hp\\ Ia+Je+Ki+Lm &Ib+Jf+Kj+Ln &Ic+Jg+Kk+Lo &Id+Jh+Kl+Lp\\ Ma+Ne+Oi+Pm &Mb+Nf+Oj+Pn &Mc+Ng+Ok+Po &Md+Nh+Ol+Pp \end{bmatrix} Aa+Be+Ci+DmEa+Fe+Gi+HmIa+Je+Ki+LmMa+Ne+Oi+PmAb+Bf+Cj+DnEb+Ff+Gj+HnIb+Jf+Kj+LnMb+Nf+Oj+PnAc+Bg+Ck+DoEc+Fg+Gk+HoIc+Jg+Kk+LoMc+Ng+Ok+PoAd+Bh+Cl+DpEd+Fh+Gl+HpId+Jh+Kl+LpMd+Nh+Ol+Pp

矩阵相乘也经常叫作合并
我们需要经常将相同的一系列矩阵变换应用到场景中的每个点上。通过预先一次计算好这些矩阵的合并,就可以成倍减少总的矩阵运算量。

GLSL 和 GLM 都支持使用重载后的乘法“ * ”运算符,进行矩阵乘法。

矩阵相乘(点 左乘 矩阵),得到新点

[ A B C D E F G H I J K L M N O P ] 4 × 4 × ( x y z 1 ) 4 × 1 = ( A x + B y + C z + D E x + F y + G z + H I x + J y + K z + L M x + N y + O z + H ) 4 × 1 \begin{bmatrix} A &B &C &D \\ E &F &G &H \\ I &J &K &L \\ M &N &O &P \end{bmatrix}_{4\times4}\times \begin{pmatrix} {\color{Green} x} \\ {\color{Green} y} \\ {\color{Green} z} \\ 1 \end{pmatrix}_{4\times1}= \begin{pmatrix} A{\color{Green} x} +B{\color{Green} y} +C{\color{Green} z} +D \\ E{\color{Green} x} +F{\color{Green} y} +G{\color{Green} z} +H \\ I{\color{Green} x} +J{\color{Green} y} +K{\color{Green} z} +L \\ M{\color{Green} x} +N{\color{Green} y} +O{\color{Green} z} +H \end{pmatrix}_{4\times1} AEIMBFJNCGKODHLP 4×4× xyz1 4×1= Ax+By+Cz+DEx+Fy+Gz+HIx+Jy+Kz+LMx+Ny+Oz+H 4×1

我们用齐次坐标将点(X, Y, Z)表示为列数为1的矩阵。
注意,矩阵乘法不满足 交换律 。(很容易证明交换律不成立)

矩阵乘法满足 结合律:(很容易证明结合律成立)
Now Point = M1×(M2×(M3×Point)) == (M1×M2×M3)×Point( = M123合并×Point)

常用的变换矩阵

变换矩阵的重要特性之一就是它们都是 4× 4 矩阵。这是因为我们决定使用齐次坐标系。否则,各变换矩阵可能会有不同的维度并且无法相乘。正如我们所见,确保变换矩阵大小相同并不只是为了方便,同时让它们可以任意组合,进行预先计算变换矩阵以提升性能。

平移矩阵

平移矩阵用于将物体从一个位置移至另一位置。

下面展示了平移矩阵和它与齐次坐标点相乘的效果——平移矩阵变换:(若非齐次坐标,无法进行)
[ 1 0 0 T x 0 1 0 T y 0 0 1 T z 0 0 0 1 ] × ( x y z 1 ) = ( x + T x y + T y z + T z 1 ) \begin{bmatrix} { 1 } & { 0 } & { 0 } & { T_x } \\ { 0 } & { 1 } & { 0 } & { T_y } \\ { 0 } & { 0 } & { 1 } & { T_z } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix} \times \begin{pmatrix} { x } \\ { y } \\ { z } \\ { 1 } \end{pmatrix}= \begin{pmatrix} { x+T_x } \\ { y+T_y } \\ { z+T_z } \\ { 1 } \end{pmatrix} 100001000010TxTyTz1 × xyz1 = x+Txy+Tyz+Tz1

( )是代表单列矩阵,也能用向量vector代表,[ ]是代表矩阵matrix。

平移矩阵变换的结果:
点(x, y, z)平移到了位置(x+Tx, y+Ty, z+Tz)

例如,将一组点向上沿Y轴正方向移动5个单位,可以通过给一个单位矩阵的Ty位置放入5来构建平移矩阵。之后只需将想要移动的点与矩阵左乘就可以了。

GLM中有一些函数是用于构建与点相乘的平移矩阵的。相关操作有:

  • mat4 glm::translate(mat4 const& m, vec3 const& v) 通过3元素向量,构建4×4平移变换矩阵;
  • mat4 × vec4 // 4×4矩阵 × 4元素向量 => 新4元素向量。

一般是用单位矩阵来使3元素平移向量生成平移变换矩阵:
glm::mat4 m = glm::translate(glm::mat4(1.0f), glm::vec3(x,y,z));
结果如下:
[ 1 0 0 x 0 1 0 y 0 0 1 z 0 0 0 1 ] \begin{bmatrix} { 1 } & { 0 } & { 0 } & { x } \\ { 0 } & { 1 } & { 0 } & { y } \\ { 0 } & { 0 } & { 1 } & { z } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix} 100001000010xyz1
可见,刚好满足平移变换矩阵的格式。

注:对于vec3 类型,要用它可以与乘以的 4×4 矩阵兼容,需要将其转换为 vec4 类型,这个转换是用 vec4(vec3, 1.0)完成的。

从 3 元素的向量创建一个 4×4 的转换矩阵:
mat<4,4,T,Q> glm::translate(mat<4,4,T,Q> const& m, vec<3,T,Q> const& v)
m - 输入矩阵乘以这个平移矩阵。
v - 平移向量的坐标。
[ v x 0 0 0 0 v y 0 0 0 0 v z 0 0 0 0 1 ] = [ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ] ( v x v y v z 1 ) \begin{bmatrix} v_x& 0& 0&0 \\ 0& v_y& 0&0 \\ 0& 0& v_z&0 \\ 0& 0& 0&1 \end{bmatrix}= \begin{bmatrix} 1& 0& 0&0 \\ 0& 1& 0&0 \\ 0& 0& 1&0 \\ 0& 0& 0&1 \end{bmatrix} \begin{pmatrix} v_x\\ v_y\\ v_z\\ 1 \end{pmatrix} vx0000vy0000vz00001 = 1000010000100001 vxvyvz1
平移变换矩阵:glm::mat4 t = glm::translate(glm::mat4(1.0f), glm::vec3(vx, vy, vz));

缩放矩阵

缩放矩阵用于改变物体的大小或者将点向原点相反方向移动。虽然缩放点这个操作乍一看有点奇怪,不过 OpenGL 中的物体都是用一组或多组的点定义的。因此,缩放物体涉及缩放它的点的集合

下面展示了缩放矩阵的形式和当它与齐次坐标点相乘的效果——经过缩放值修改后的新点。
( x ∗ S x y ∗ S y z ∗ S z 1 ) = [ S x 0 0 0 0 S y 0 0 0 0 S z 0 0 0 0 1 ] × ( x y z 1 ) \begin{pmatrix} { x*S_x } \\ { y*S_y } \\ { z*S_z } \\ { 1 } \end{pmatrix}= \begin{bmatrix} { S_x } & { 0 } & { 0 } & { 0 } \\ { 0 } & { S_y } & { 0 } & { 0 } \\ { 0 } & { 0 } & { S_z } & { 0 } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix}\times \begin{pmatrix} { x } \\ { y } \\ { z } \\ { 1 } \end{pmatrix} xSxySyzSz1 = Sx0000Sy0000Sz00001 × xyz1
GLM中有一些函数是用于构建与点相乘的缩放矩阵的。相关操作有:
glm::scale(x, y, z) 构建缩放(x, y, z)的矩阵。以上式子的表示为:mat4 * vec4

缩放还可以用来 切换坐标系
例如,可以用缩放来在给定右手坐标系的情况下确定左手坐标。

从上图可以看到通过反转Z坐标就可以从右手坐标切换为左手坐标。因此,用来切换坐标系的缩放矩阵变换是:
[ 1 0 0 0 0 1 0 0 0 0 − 1 0 0 0 0 1 ] \begin{bmatrix} { 1 } & { 0 } & { 0 } & { 0 } \\ { 0 } & { 1 } & { 0 } & { 0 } \\ { 0 } & { 0 } & { -1 } & { 0 } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix} 1000010000100001

旋转矩阵

旋转会稍微复杂一些,因为在 3D 空间中旋转物体需要指定旋转轴和旋转的角度或弧度。

数学家莱昂哈德· 欧拉表明,围绕任何轴的旋转都可以表示为绕 X、Y、Z 轴旋转的组合。围绕这 3 个轴的旋转角度被称为欧拉角。 这个被称为欧拉定理的发现,对我们很有用,因为对于每个坐标轴的旋转可以用矩阵变换来表示。

ⓐ绕 X 轴旋转θ度:
( x ′ y ′ z ′ 1 ) = [ 1 0 0 0 0 c o s θ − s i n θ 0 0 s i n θ c o s θ 0 0 0 0 1 ] × ( x y z 1 ) \begin{pmatrix} { x' } \\ { y' } \\ { z' } \\ { 1 } \end{pmatrix} = \begin{bmatrix} { 1 } & { 0 } & { 0 } & { 0 } \\ { 0 } & { cosθ } & { -sinθ } & { 0 } \\ { 0 } & { sinθ } & { cosθ } & { 0 } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix} \times \begin{pmatrix} { x } \\ { y } \\ { z } \\ { 1 } \end{pmatrix} xyz1 = 10000cosθsinθ00sinθcosθ00001 × xyz1
ⓑ绕 Y 轴旋转θ度:
( x ′ y ′ z ′ 1 ) = [ c o s θ 0 s i n θ 0 0 1 0 0 − s i n θ 0 c o s θ 0 0 0 0 1 ] × ( x y z 1 ) \begin{pmatrix} { x' } \\ { y' } \\ { z' } \\ { 1 } \end{pmatrix} =\begin{bmatrix} { cosθ } & { 0 } & { sinθ } & { 0 } \\ { 0 } & { 1 } & { 0 } & { 0 } \\ { -sinθ } & { 0 } & { cosθ } & { 0 } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix} \times \begin{pmatrix} { x } \\ { y } \\ { z } \\ { 1 } \end{pmatrix} xyz1 = cosθ0sinθ00100sinθ0cosθ00001 × xyz1
ⓒ绕 Z 轴旋转θ度:
( x ′ y ′ z ′ 1 ) = [ c o s θ − s i n θ 0 0 s i n θ c o s θ 0 0 0 0 1 0 0 0 0 1 ] × ( x y z 1 ) \begin{pmatrix} { x' } \\ { y' } \\ { z' } \\ { 1 } \end{pmatrix} =\begin{bmatrix} { cosθ } & { -sinθ } & { 0 } & { 0 } \\ { sinθ } & { cosθ } & { 0 } & { 0 } \\ { 0 } & { 0 } & { 1 } & { 0 } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix} \times \begin{pmatrix} { x } \\ { y } \\ { z } \\ { 1 } \end{pmatrix} xyz1 = cosθsinθ00sinθcosθ0000100001 × xyz1
因sin2θ+cos2θ=1,cosθ=cos(-θ),sinθ=-sinθ,所以显然有:基础旋转矩阵与它们的逆转置矩阵相等。

粗略推导 [旋转矩阵]
我们这里只推导点绕Z轴旋转的旋转矩阵,绕其他轴旋转的推导过程类似。

空间中任意一点 p(x0,y0,z0) 绕 Z 轴旋转 β 角到点 p’(x’0,y’0,z’0),对应的在XOY平面上的垂直投影点分别为点 P(x,y,z) 和 点 P’(x’,y’,z’),有:
 
{ x = r ∗ c o s α y = r ∗ s i n α \begin{cases} x=r*cosα \\ y=r*sinα \end{cases} {x=rcosαy=rsinα
 
{ x ′ = r ∗ c o s ( α + β ) = r ∗ c o s α ∗ c o s β − r ∗ s i n α ∗ s i n β = x ∗ c o s β − y ∗ s i n β y ′ = r ∗ s i n ( α + β ) = r ∗ s i n α ∗ s i n β + r ∗ c o s α ∗ c o s β = x ∗ s i n β + y ∗ c o s β \begin{cases} x'=r*cos(α+β)=r*cosα*cosβ - r*sinα*sinβ=x*cosβ-y*sinβ \\ y'=r*sin(α+β)=r*sinα*sinβ + r*cosα*cosβ=x*sinβ+y*cosβ \end{cases} {x=rcos(α+β)=rcosαcosβrsinαsinβ=xcosβysinβy=rsin(α+β)=rsinαsinβ+rcosαcosβ=xsinβ+ycosβ
 
( x ′ y ′ ) = [ c o s β − s i n β s i n β c o s β ] ( x y ) \begin{pmatrix} x'\\ y' \end{pmatrix}= \begin{bmatrix} cosβ &-sinβ \\ sinβ &cosβ \end{bmatrix} \begin{pmatrix} x\\ y \end{pmatrix} (xy)=[cosβsinβsinβcosβ](xy)
 
绕 Z 轴旋转,z坐标是不变的,即:
z ′ = z \begin{matrix} z'=z \end{matrix} z=z
则有:
( x ′ y ′ z ′ ) = [ c o s β − s i n β 0 s i n β c o s β 0 0 0 1 ] ( x y z ) \begin{pmatrix} x'\\ y'\\ z' \end{pmatrix}= \begin{bmatrix} cosβ &-sinβ &0\\ sinβ &cosβ &0\\ 0 & 0 & 1 \end{bmatrix} \begin{pmatrix} x\\ y\\ z \end{pmatrix} xyz = cosβsinβ0sinβcosβ0001 xyz
用齐次坐标表示为:
( x ′ y ′ z ′ 1 ) = [ c o s β − s i n β 0 0 s i n β c o s β 0 0 0 0 1 0 0 0 0 1 ] ( x y z 1 ) \begin{pmatrix} x'\\ y'\\ z'\\ 1 \end{pmatrix}= \begin{bmatrix} cosβ &-sinβ &0 &0\\ sinβ &cosβ &0 &0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{pmatrix} x\\ y\\ z\\ 1 \end{pmatrix} xyz1 = cosβsinβ00sinβcosβ0000100001 xyz1

欧拉角在某些 3D 图形应用中会导致一些问题。因此,通常在计算旋转时推荐使用四元数。不过欧拉角足以满足我们的大部分需求。

透视矩阵


[ A 0 0 0 0 q 0 0 0 0 B C 0 0 0 0 ] \begin{bmatrix} A& 0& 0&0 \\ 0& q& 0&0 \\ 0& 0& B&C \\ 0& 0& 0&0 \end{bmatrix} A0000q0000B000C0
其中,
q = 1 tan ⁡ ( f i e l d O f V i e w 2 ) A = q a s p e c t R a t i o ▫ B = Z n e a r + Z f a r Z n e a r − Z f a r C = 2 ∗ ( Z n e a r ∗ Z f a r ) Z n e a r − Z f a r q = \frac { 1 } { \operatorname { tan } ( \frac { fieldOfView } { 2 } ) }\qquad A = \frac { q } { aspectRatio }\qquad \\ \\▫ \\ B = \frac {Z_{near} + Z_{far}} {Z_{near} - Z_{far}}\qquad C = \frac {2*(Z_{near}*Z_{far})} {Z_{near} - Z_{far}} q=tan(2fieldOfView)1A=aspectRatioqB=ZnearZfarZnear+ZfarC=ZnearZfar2(ZnearZfar)

glm::perspective(<fieldOfView>, <aspectRatio>, <nearPlane>, <farPlane>);
<fieldOfView>=FOV角,<aspectRatio>=W/H,
<nearPlane>=Znear,<farPlane>=Zfar

正射投影矩阵


[ 2 R − L 0 0 − R + L R − L 0 2 T − B 0 − T + B T − B 0 0 1 Z f a r − Z n e a r − Z n e a r Z f a r − Z n e a r 0 0 0 1 ] \begin{bmatrix} { \frac { 2 } { R-L } } & { 0 } & { 0 } & { -\frac { R+L } { R-L } } \\ \\ { 0 } & { \frac { 2 } { T-B } } & { 0 } & { -\frac { T+B } { T-B } } \\ \\ { 0 } & { 0 } & { \frac { 1 } { Z_{far}-Z_{near} } } & { -\frac { Z_{near} } { Z_{far}-Z_{near} } } \\ \\ { 0 } & { 0 } & { 0 } & { 1 } \end{bmatrix} RL20000TB20000ZfarZnear10RLR+LTBT+BZfarZnearZnear1

LookAt矩阵


f w d ⃗ = n o r m a l i z e ( e y e ⃗ − t a r g e t ⃗ ) s i d e ⃗ = n o r m a l i z e ( − f w d ⃗ × u p ⃗ ) u p ⃗ = n o r m a l i z e ( s i d e ⃗ × ( − f w d ) ⃗ ) ———————————————————— [ s i d e x ⃗ s i d e y ⃗ s i d e z ⃗ − ( s i d e ⋅ e y e ) ⃗ ⃗ u p x ⃗ u p y ⃗ u p z ⃗ − ( u p ⋅ e y e ) ⃗ ⃗ − f w d x ⃗ − f w d y ⃗ − f w d z ⃗ − ( − f w d ⋅ e y e ) ⃗ ⃗ 0 0 0 1 ] \vec{fwd} = normalize(\vec{eye} - \vec{target})\\ \vec{side} = normalize(\vec{-fwd} \times \vec{up})\\ \vec{up} = normalize(\vec{side} \times \vec{(-fwd)})\\ \\ ———————————————————— \\ \begin{bmatrix} \vec{side_x}& \vec{side_y}& \vec{side_z}& \vec{-(side\cdot \vec{eye)}} \\ \vec{up_x}& \vec{up_y}& \vec{up_z}& \vec{-(up\cdot \vec{eye)}} \\ \vec{-fwd_x}& \vec{-fwd_y}& \vec{-fwd_z}& \vec{-(-fwd\cdot \vec{eye)}} \\ 0& 0& 0& 1 \end{bmatrix} fwd =normalize(eye target )side =normalize(fwd ×up )up =normalize(side ×(fwd) )———————————————————— sidex upx fwdx 0sidey upy fwdy 0sidez upz fwdz 0(sideeye) (upeye) (fwdeye) 1
在GLM中已经有一个用来构建LookAt矩阵的函数glm::lookAt(eye, center, up)

template<typename T, qualifier Q>
GLM_FUNC_DECL mat<4,4,T,Q> lookAt(
	const vec<3,T,Q>& eye,    // eye position in worldspace
	const vec<3,T,Q>& center, // the point where we look at
	const vec<3,T,Q>& up      // the vector of upwords(your head is up)
)
——————————————————————————————————————
作用:基于默认的左右手法则构建一个LookAt矩阵。
参数:
   eye 相机位置
center 相机注视的位置(即目标位置)
    up 归一化的向上失量,指明相机的方位
模板参数:
T 一个浮点标量类型
Q 一个限定符,值是枚举常量值

理解参数——up向量:
把相机想象成自己的脑袋,那么:
eye - 脑袋的位置;
center - 脑袋看的方向;
up - 脑袋看物体的姿态(因为可以歪着头看同一个物体)。

该函数定义一个视图矩阵,其中:
eye - 相机在世界坐标的位置;
center - 相机镜头对准的物体在世界坐标的位置;
up - 指明世界坐标中相机向上的方位(并不是说此时相机的向上方位!

此向量基本上是一个定义世界“向上”方向的向量。
它不得与从视点到参考点的视线平行。
在几乎所有正常情况下,这将是向量(0, 1, 0),即朝向正Y

向量


假设有向量 A(u, v, w) 和 B(x, y, z)。
归一化(将长度为为1):
A ^ = A ⃗ ∣ A ∣ = A ⃗ u 2 + v 2 + w 2 \hat{A} = \frac{\vec{A}}{|A|} = \frac{\vec{A}}{\sqrt{u^2+v^2+w^2}} A^=AA =u2+v2+w2 A
glm/GLSL:normalize(vec3 or vec4)

加法和减法

A ⃗ ± B ⃗ = ( u ± x , v ± y , w ± z ) = B A ⃗ \vec{A}\pm\vec{B} = (u\pm{x}, v\pm{y}, w\pm{z}) =\vec{BA} A ±B =(u±x,v±y,w±z)=BA

点积

A ⃗ ⋅ B ⃗ = u x + v y + w z \vec{A}\cdot\vec{B}=ux+vy+wz A B =ux+vy+wz
glm/GLSL:dot(vec3/vec4, vec3/vec4)

点积最重要也最基本的应用就是求两向量夹角。高向量VW,计算其夹角θ:
V ⃗ ⋅ W ⃗ = ∣ V ⃗ ∣ ∣ W ⃗ ∣ c o s θ c o s θ = V ⃗ ⋅ W ⃗ ∣ V ⃗ ∣ ∣ W ⃗ ∣ \vec{V}\cdot\vec{W}=|\vec{V}||\vec{W}|cosθ\\ cosθ=\frac{\vec{V}\cdot\vec{W}}{|\vec{V}||\vec{W}|} V W =V ∣∣W cosθcosθ=V ∣∣W V W
因此,如果 V 和 W 是归一化向量(有着单位长度的向量,这里用“^”标记),则有:
c o s θ = V ^ ⋅ W ^ θ = arccos ⁡ ( V ^ ⋅ W ^ ) cosθ=\hat{V}\cdot\hat{W}\\ θ=\arccos(\hat{V}\cdot\hat{W}) cosθ=V^W^θ=arccos(V^W^)
点积同时还有许多其他用途:

  • 求向量的大小: V ⃗ ⋅ V ⃗ \sqrt{\vec{V}\cdot\vec{V}} V V
  • 判断两向量是否正交,若正交,则 V ⃗ ⋅ W ⃗ = 0 \vec{V}\cdot\vec{W}=0 V W =0
  • 判断两向量是否同向,若同向,则 V ⃗ ⋅ W ⃗ = ∣ V ⃗ ∣ ∣ W ⃗ ∣ \vec{V}\cdot\vec{W}=|\vec{V}||\vec{W}| V W =V ∣∣W ;若反向,则 V ⃗ ⋅ W ⃗ = − ∣ V ⃗ ∣ ∣ W ⃗ ∣ \vec{V}\cdot\vec{W}=-|\vec{V}||\vec{W}| V W =V ∣∣W
  • 判断两向量夹角是否在-90°~+90° 范围内(不包含边界),若是,则 V ⃗ ⋅ W ⃗ > 0 \vec{V}\cdot\vec{W}>0 V W >0
  • 求点到平面的最小有符号距离: n ^ ⋅ P ⃗ + D 0 \hat{n}\cdot{\vec{P}} + D_0 n^P +D0

一般平面的表示法为:a(x-x0) + b(y-y0) + c(z-z0) = 0

对于平面的一般方程 ax + by + cz + d = 0,可表示为平面S(a, b, c, d)。
平面S(a, b, c, d)的法向量公式:
n ⃗ = ( a , b , c ) \vec{n} = (a, b, c) n =(a,b,c)
点P(xp, yp, zp)到平面S(a, b, c, d)的距离公式:
D p = ∣ a x p + b y p + c z p + d ∣ a 2 + b 2 + c 2 D_p = \frac{|ax_p+by_p+cz_p+d|}{\sqrt{a^2+b^2+c^2}} Dp=a2+b2+c2 axp+byp+czp+d
由以上两公式可以推出,平面S单位法向量:
n ^ = ( a a 2 + b 2 + c 2 , b a 2 + b 2 + c 2 , c a 2 + b 2 + c 2 ) \hat{n} = (\frac{a}{\sqrt{a^2+b^2+c^2}}, \frac{b}{\sqrt{a^2+b^2+c^2}}, \frac{c}{\sqrt{a^2+b^2+c^2}}) n^=(a2+b2+c2 a,a2+b2+c2 b,a2+b2+c2 c)
原点到平面S的有符号距离:
D 0 = d a 2 + b 2 + c 2 D_0 = \frac{d}{\sqrt{a^2+b^2+c^2}} D0=a2+b2+c2 d
推出,点P到平面S的有符号距离,可以用点积(方便程序函数运算)表示为:
D p s = n ^ ⋅ P ⃗ + D 0 D_{ps} = \hat{n}\cdot{\vec{P}} + D_0 Dps=n^P +D0

叉积

A ⃗ × B ⃗ = ( v z − w y , w x − u z , u y − v x ) \vec{A}\times\vec{B}=(vz-wy,wx-uz,uy-vx) A ×B =(vzwy,wxuz,uyvx)
glm/GLSL:corss(vec3,vec3)

通过叉积来获得法向量的能力对我们后面要学习的光照部分非常重要。为了确定光照效果,我们需要知道所渲染模型的外向法向量。使用叉积计算来获得其中一面的外向法向量。其所得法向量的方向遵循右手定则。

P ⃗ = V 2 ⃗ − V 1 ⃗ Q ⃗ = V 3 ⃗ − V 1 ⃗ N ⃗ = P ⃗ × Q ⃗ \vec{P}=\vec{V_2}-\vec{V_1}\\ \vec{Q}=\vec{V_3}-\vec{V_1}\\ \vec{N}=\vec{P}\times\vec{Q} P =V2 V1 Q =V3 V1 N =P ×Q

局部空间和世界空间——模型矩阵M

当建立物体的 3D 模型时,我们通常以最方便的定位方式描述模型。如果模型是球形的,那么我们很可能将球心定位于原点(0,0,0)并赋予它一个易于处理的半径,比如 1。模型定义的空间叫作局部空间(local space)模型空间(model space)。OpenGL 官方文档使用的术语是物体空间(object space)

我们刚刚定义的球可能用作一个大模型的一部分,如机器人的头部。这个机器人当然也定义在它自身的局部空间。我们可以用上图所示的矩阵变换即通过缩放、旋转和平移,将球放在机器人模型的空间。通过这种方式,可以分层次地构建复杂模型。

使用同样的方式,通过设定物体在模拟世界中的朝向和大小,可以将物体放在模拟世界的空间中,这个空间叫作世界空间。在世界空间中为对象定位及定向的矩阵称为模型矩阵,通常记为 M

视觉空间和合成相机——模型-视图矩阵MV

观察 3D 世界需要:
(a)将相机放入世界的某个位置;
(b)调整相机的角度,通常需要一套它自己的直角坐标轴 u、 v、 n(由向量 U,V,N 构成);
(c)定义一个视体(view volume);
(d)将视体内的对象投影到投影平面(projection plane)上。

OpenGL 有一个固定在原点(0,0,0)并朝向 z 轴负方向的相机:

为了应用 OpenGL 相机,我们需要将它移动到适合的位置和方向。我们需要先找出在世界中的物体与我们期望的相机位置的相对位置(如物体应该在由下图所示相机 U、 V、 N 向量定义的“相机空间”中的位置)。给定世界空间中的点 Pw,我们需要通过变换将它转换成相应相机空间中的点,从而让它看起来好像是从我们期望的相机位置 Cw 看到的样子。我们通过计算它在相机空间中的位置 PC 实现。

已知 OpenGL 相机位置永远固定在点(0,0,0),那么我们如何变换来实现上述功能?

需要做的变换如下:
(1)将 PW 平移,其向量为负的期望相机位置。
(2)将 PW 旋转,其角度为负的期望相机欧拉角。

为了模拟以某种方式移动的相机的表现,我们需要向相反的方向移动物体本身。例如,将相机向右移动会导致场景中的物体看起来像是向左移动,对应地,针对 OpenGL 的固定相机,我们可以通过把对象向左移动,让相机看起来向右移动了。对于旋转,类似。

我们可以构建一个单一变换矩阵以完成旋转和平移,这个矩阵叫作视图变换(viewing transform)矩阵,记作 V。矩阵 V 合并了矩阵 T(包含负相机期望位置的平移矩阵)和 R(包含负相机期望欧拉角的旋转矩阵)。在本例中,我们从右向左计算,先平移世界空间中的点 PW,之后旋转:
P C = R ⃗ ( T ⃗ P W ) = ( R T ⃗ ) P W = V ⃗ P W P_C=\vec{R}(\vec{T}P_W)=(\vec{RT})P_W=\vec{V}P_W PC=R (T PW)=(RT )PW=V PW

通常,将 V 矩阵与模型矩阵 M 的积定义为模型-视图(Model-View, MV)矩阵,记作 MVMV = VM。之后,点 PM 通过如下步骤就可以从自己的模型空间直接转换至相机空间:PC = MVPM

在复杂场景中,当我们需要对每个顶点,而非一个点做这个变换的时候,这种方法的好处就很明显了。通过预先计算 MV,空间中每个点的变换都只需要进行一次矩阵乘法计算。之后,我们会看到,可以将这个过程延伸到更多合并矩阵的计算中,以大量减少点的计算量。

用GLSL函数构建变换矩阵

GLSL 中用于初始化 mat4 矩阵的语法以列为单位读入值。其前 4 个参数会放入第一列,接
下来 4 个参数放入下一列,直到第四列,如下所示:

mat4 translationMatrix =
	mat4(1.0, 0.0, 0.0, 0.0, // 注意,这是最左列(第一列),而非第一行
		 0.0, 1.0, 0.0, 0.0,
		 0.0, 0.0, 1.0, 0.0,
		 tx,  ty,  tz,  1.0);
// 构建并返回平移矩阵
mat4 buildTranslate(float x, float y, float z) { 
	mat4 trans = mat4(1.0, 0.0, 0.0, 0.0,
					  0.0, 1.0, 0.0, 0.0,
					  0.0, 0.0, 1.0, 0.0,
					  x, y, z, 1.0);
	return trans;
}

// 构建并返回绕 x 轴的旋转矩阵
mat4 buildRotateX(float rad) { 
	mat4 xrot = mat4(1.0, 0.0, 0.0, 0.0,
					 0.0, cos(rad), -sin(rad), 0.0,
					 0.0, sin(rad), cos(rad), 0.0,
					 0.0, 0.0, 0.0, 1.0);
	return xrot;
}

// 构建并返回绕 y 轴的旋转矩阵
mat4 buildRotateY(float rad) { 
	mat4 yrot = mat4(cos(rad), 0.0, sin(rad), 0.0,
					 0.0, 1.0, 0.0, 0.0,
					-sin(rad), 0.0, cos(rad), 0.0,
					 0.0, 0.0, 0.0, 1.0);
	return yrot;
}

// 构建并返回绕 z 轴的旋转矩阵
mat4 buildRotateZ(float rad) { 
	mat4 zrot = mat4(cos(rad), -sin(rad), 0.0, 0.0,
					 sin(rad), cos(rad), 0.0, 0.0,
					 0.0, 0.0, 1.0, 0.0,
					 0.0, 0.0, 0.0, 1.0 );
	return zrot;
}

// 构建并返回缩放矩阵
mat4 buildScale(float x, float y, float z) { 
	mat4 scale = mat4(x, 0.0, 0.0, 0.0,
					  0.0, y, 0.0, 0.0,
					  0.0, 0.0, z, 0.0,
					  0.0, 0.0, 0.0, 1.0 );
	return scale;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1319174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习算法---异常检测

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…

RTOS队列的写入与读出

我们在stm32f103c8t6单片机上验证RTOS队列的写入与读出&#xff0c;利用stm32cube进行RTOS的配置。在选择TIM2当做RTOS的时钟&#xff0c;裸机的时钟源默认是 SysTick&#xff0c;但是开启 FreeRTOS 后&#xff0c;FreeRTOS会占用 SysTick &#xff08;用来生成1ms 定时&#x…

flask简单应用-1

目标&#xff1a; 做一个搜索网页&#xff0c;搜索当前路径下是否含有指定关键字的文件&#xff0c;如果有就列出来&#xff0c;没有返回消息 第一步&#xff1a;我们需要先显示一个搜索页面&#xff0c;页面上需要有一个可以输入的对话框&#xff0c;一个按钮执行搜索 建立ht…

Vue3-05-计算属性使用详解

计算属性简介 计算属性的函数是 computed()。计算属性可以帮助我们处理有复杂逻辑的响应式数据的渲染&#xff0c; 从而代替 模板表达式 的写法。比如 &#xff1a; 一个数值类型的数组对象&#xff0c;我们希望页面展示的只有 偶数。 此时&#xff0c;就可以通过 计算属性 来…

02.Git常用基本操作

一、基本配置 &#xff08;1&#xff09;打开Git Bash &#xff08;2&#xff09;配置姓名和邮箱 git config --global user.name "Your Name" git config --global user.email "Your email" 因为Git是分布式版本控制工具&#xff0c;所以每个用户都需要…

手拉手EasyExcel极简实现web上传下载(全栈)

环境介绍 技术栈 springbootmybatis-plusmysqleasyexcel 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。 他能让你在不用考虑性…

【PostgreSQL】从零开始:(十三)PostgreSQL-SQL语句操作架构(模式) Schema

Schema概述 PostgreSQL 数据库集群包含一个或多个命名数据库。角色和一些其他对象类型在整个集群中共享。与服务器的客户端连接只能访问单个数据库中的数据&#xff0c;该数据库在连接请求中指定。 用户不一定有权访问集群中的每个数据库。共享角色名称意味着不能在同一集群中…

IDEA2023 + spring cloud 工程热部署设置方法

基于spring cloud 工程进行热部署 &#xff0c;实现每次修改工程源文件&#xff0c;后台自动启动&#xff0c;方便开发测试工作。具体分为5步骤即可&#xff1a; 1、修改工程的pom文件&#xff0c;增加adding devtools 工具包。 <dependency> <groupId>org.s…

1264. 动态求连续区间和(树状数组---某个位置加上一个数/求在线(动态)前缀和/蓝桥杯)

题目&#xff1a; 输入样例&#xff1a; 10 5 1 2 3 4 5 6 7 8 9 10 1 1 5 0 1 3 0 4 8 1 7 5 0 4 8输出样例&#xff1a; 11 30 35 树状数组&#xff1a; 代码&#xff1a; #include<cstdio> #include<iostream> using namespace std;const int N100010; int n,…

【elementui笔记:el-table表格的输入校验】

之前做得比较多的校验是在el-form表单里做的&#xff0c;但有时也遇到&#xff0c;需要在table内输入数据&#xff0c;然后校验输入的数据是否符合要求的情况。因此记录一下。 思路&#xff1a; 1.需要借助el-form的校验&#xff0c;el-table外层嵌套一层el-form&#xff0c;使…

数据分析为何要学统计学(4)——何为置信区间?它有什么作用?

置信区间是统计学中的一个重要工具&#xff0c;是用样本参数()估计出来的总体均值在某置信水平下的范围。通俗一点讲&#xff0c;如果置信度为95%&#xff08;等价于显著水平a0.05&#xff09;&#xff0c;置信区间为[a,b]&#xff0c;这就意味着总体均值落入该区间的概率为95%…

宏基因组学Metagenome-磷循环Pcycle功能基因分析-从分析过程到代码及结果演示-超详细保姆级流程

大背景介绍 生信分析,凡事先看论文,有了论文就有了参考,后续分析就有底了,直接上硬菜开干: PCycDB: a comprehensive and accurate database for fast analysis of phosphorus cycling genes - PubMed 数据库及部分分析代码github库: GitHub - ZengJiaxiong/Phospho…

7.实现任务的rebalance

1.设计 1.1 背景 系统启动后&#xff0c;所有任务都在被执行&#xff0c;如果这时某个节点宕机&#xff0c;那它负责的任务就不能执行了&#xff0c;这对有稳定性要求的任务是不能接受的&#xff0c;所以系统要实现rebalance的功能。 1.2 设计 下面是Job分配与执行的业务点…

深度学习中的潜在空间

1 潜在空间定义 Latent Space 潜在空间&#xff1a;Latent &#xff0c;这个词的语义是“隐藏”的意思。“Latent Space 潜在空间”也可以理解为“隐藏的空间”。Latent Space 这一概念是十分重要的&#xff0c;它在“深度学习”领域中处于核心地位&#xff0c;即它是用来学习…

【每日一题】寻找峰值

文章目录 Tag题目来源解题思路方法一&#xff1a;二分查找 写在最后 Tag 【二分查找】【数组】【2023-12-18】 题目来源 162. 寻找峰值 解题思路 方法一&#xff1a;二分查找 思路 进行二分查找&#xff0c;记当前的二分中点为 mid&#xff1a; 如果 nums[mid] < nums…

UE4 去除重复纹理

如果直接连的话&#xff0c;效果如下&#xff1a; 就存在很多重复的纹理&#xff0c;如何解决这个问题呢&#xff1f; 将同一个纹理&#xff0c;用不同的Tilling&#xff0c;将Noise进行Lerp两者之间&#xff0c;为什么要这么做呢&#xff1f;因为用一个做清晰纹理&#xff0c;…

linux驱动的学习 驱动开发初识

1 设备的概念 在学习驱动和其开发之前&#xff0c;首先要知道所谓驱动&#xff0c;其对象就是设备。 1.1 主设备号&次设备号&#xff1a; 在Linux中&#xff0c;各种设备都以文件的形式存在/dev目录下&#xff0c;称为设备文件。最上层的应用程序可以打开&#xff0c;关…

1852_bash中的find应用扩展

Grey 全部学习内容汇总&#xff1a; https://github.com/GreyZhang/toolbox 1852_bash中的find应用扩展 find这个工具我用了好多年了&#xff0c;但是是不是真的会用呢&#xff1f;其实不然&#xff0c;否则也不会出现这种总结式的笔记。其实&#xff0c;注意部分小细节之后…

[AutoSar]基础部分 RTE 介绍

目录 关键词平台说明一、什么是RTE二、RTE的主要功能 关键词 嵌入式、C语言、autosar、EcuM、wakeup、flex 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 一、什么是RTE RTE&#xff08;Run-Time Environment&…

Docker 的基本概念、优势、及在程序开发中的应用

Docker 是一种容器化平台,它通过使用容器化技术,将应用程序及其依赖性打包到一个独立的、可移植的容器中,从而实现应用程序的快速部署、可靠性和可扩展性。 下面是 Docker 的一些基本概念和优势: 容器:Docker 使用容器化技术,将应用程序及其依赖性打包到一个可移植的容器…