WebGL系列教程十(模型Model、视图View、投影Projection变换)

news/2024/9/19 19:36:26 标签: webgl, 模型变换, 视图变换, 投影变换, mvp变换

目录

  • 1 前言
  • 2 模型变换
  • 3 视图变换
    • 3.1 公式推导
      • 3.1.1 确定摄像机的参数
      • 3.1.2 构建摄像机坐标系
      • 3.1.3 构建视图变换矩阵
      • 3.1.4 组合视图矩阵
    • 3.2 方法调用
  • 4 投影变换
    • 4.1 正交投影推导
    • 4.2 正交投影调用
    • 4.3 透视投影推导
    • 4.3 透视投影调用
  • 5 总结

1 前言

  上一讲我们讲了动画,现在大家已经知道该如何让一个三维物体动起来了,那么接下来我们就要搞明白如何进行模型、视图、投影变换,即MVP变换,也即Model、View、Projection的变换。

2 模型变换

  什么是模型变换模型变换就是对模型做旋转、平移、缩放,即我们在WebGL系列教程七(二维及三维旋转、平移、缩放)中讲的就是模型变换模型变换,用大白话解释就是,把模型以某种姿态放在某个位置。那怎么样进行旋转平移缩放?自然是乘以相应的旋转、平移、缩放矩阵。这一块的代码本节我们就不赘述了,有兴趣的同学可以自行查看教程七。

3 视图变换

  什么是视图变换视图变换的意思就是我从哪里看向哪里。这里面就牵扯到了三个概念,视点、目标点和上方向。视点就是我们在哪个地方看,目标点就是我要看向哪里,上方向就是我看的角度。试想一下,我站着看一个物体,和躺着看一个物体,看到的东西或许是不一样的。比如我站着看看一个立方体,我能看到立方体的顶面,但是当我躺着看的时候,我就看不到顶面了,我看到的是正对着我的面。
  我们分别用三个坐标来表示视点、目标点和上方向。
  视点:eyeX,eyeY,eyeZ
  目标点:atX,atY,atZ
  上方向:upX,upY,upZ
  那我们怎么求一个视图变换的矩阵呢?我们的做法是:首先约定,相机始终位于原点,看向负Z轴方向,所以要将摄像机移到原点并调整方向,使得摄像机的视线对准坐标系的负Z轴。那么首先就是用齐次坐标去平移,然后旋转。由于从物体旋转到约定位置比较困难,因此改为从约定位置旋转到物体位置,得到旋转矩阵,进行得到逆矩阵,就是我们要的变换了。而又因为旋转矩阵是正交矩阵,它的逆矩阵就是它的转置。这样我们就很容易得到视图变换矩阵了。

3.1 公式推导

3.1.1 确定摄像机的参数

假设摄像机的位置为 E \mathbf{E} E(Eye),摄像机的朝向为 D \mathbf{D} D(Direction),摄像机的上向量为 U \mathbf{U} U(Up Vector)。

  • E = ( eyeX , eyeY , eyeZ ) \mathbf{E} = (\text{eyeX}, \text{eyeY}, \text{eyeZ}) E=(eyeX,eyeY,eyeZ) 是摄像机的位置。
  • D = ( atX − eyeX , atY − eyeY , atZ − eyeZ ) \mathbf{D} = (\text{atX} - \text{eyeX}, \text{atY} - \text{eyeY}, \text{atZ} - \text{eyeZ}) D=(atXeyeX,atYeyeY,atZeyeZ) 是摄像机的朝向。
  • U = ( upX , upY , upZ ) \mathbf{U} = (\text{upX}, \text{upY}, \text{upZ}) U=(upX,upY,upZ) 是摄像机的上向量,用于定义视图的顶部方向。

3.1.2 构建摄像机坐标系

首先,我们需要构建摄像机的坐标系,该坐标系由三个正交单位向量组成,我们通过归一化和向量差积获得:

  • f \mathbf{f} f 是摄像机的前向量,也称为视线向量:

f = D ∣ D ∣ \mathbf{f} = \frac{\mathbf{D}}{|\mathbf{D}|} f=DD

  • r \mathbf{r} r 是摄像机的右向量,定义为 U \mathbf{U} U f \mathbf{f} f 的叉积:

r = U × f ∣ U × f ∣ \mathbf{r} = \frac{\mathbf{U} \times \mathbf{f}}{|\mathbf{U} \times \mathbf{f}|} r=U×fU×f

  • u \mathbf{u} u 是新的上向量,定义为 f \mathbf{f} f r \mathbf{r} r 的叉积:

u = f × r \mathbf{u} = \mathbf{f} \times \mathbf{r} u=f×r

3.1.3 构建视图变换矩阵

视图变换矩阵由旋转矩阵 R R R 和平移矩阵 T T T 组合而成:

  1. 旋转矩阵 R R R:

R = [ r x r y r z 0 u x u y u z 0 − f x − f y − f z 0 0 0 0 1 ] R = \begin{bmatrix} r_x & r_y & r_z & 0 \\ u_x & u_y & u_z & 0 \\ -f_x & -f_y & -f_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} R= rxuxfx0ryuyfy0rzuzfz00001

  1. 平移矩阵 T T T:

T = [ 1 0 0 − eyeX 0 1 0 − eyeY 0 0 1 − eyeZ 0 0 0 1 ] T = \begin{bmatrix} 1 & 0 & 0 & -\text{eyeX} \\ 0 & 1 & 0 & -\text{eyeY} \\ 0 & 0 & 1 & -\text{eyeZ} \\ 0 & 0 & 0 & 1 \end{bmatrix} T= 100001000010eyeXeyeYeyeZ1

3.1.4 组合视图矩阵

视图变换矩阵 V V V 是旋转矩阵和平移矩阵的组合:

V = R × T V = R \times T V=R×T

将两者相乘得到:

V = [ r x r y r z − eyeX ⋅ r x − eyeY ⋅ r y − eyeZ ⋅ r z u x u y u z − eyeX ⋅ u x − eyeY ⋅ u y − eyeZ ⋅ u z − f x − f y − f z eyeX ⋅ f x + eyeY ⋅ f y + eyeZ ⋅ f z 0 0 0 1 ] V = \begin{bmatrix} r_x & r_y & r_z & -\text{eyeX} \cdot r_x - \text{eyeY} \cdot r_y - \text{eyeZ} \cdot r_z \\ u_x & u_y & u_z & -\text{eyeX} \cdot u_x - \text{eyeY} \cdot u_y - \text{eyeZ} \cdot u_z \\ -f_x & -f_y & -f_z & \text{eyeX} \cdot f_x + \text{eyeY} \cdot f_y + \text{eyeZ} \cdot f_z \\ 0 & 0 & 0 & 1 \end{bmatrix} V= rxuxfx0ryuyfy0rzuzfz0eyeXrxeyeYryeyeZrzeyeXuxeyeYuyeyeZuzeyeXfx+eyeYfy+eyeZfz1

这个矩阵 V V V 就是将世界坐标系中的点转换到摄像机视图坐标系的视图变换矩阵。

3.2 方法调用

公式推导可能有点复杂,但是方法调用很简单,各种框架都类似,WebGL编程指南中提供的方法如下:

let viewProjMatrix = new Matrix4();
//从0,2,7看向0,0,0,上方向是0,1,0
viewProjMatrix.lookAt(0,2,7,0,0,0,0,1,0);

4 投影变换

  什么是投影?投影就是将一个三维物体,映射到平面上,就叫投影。我们这里的投影共分为两种,正交投影和透视投影。投影的最终目标约定为,我们要将物体的任一x、y、z都变换到【-1,1】的范围内。三个值都需要变换到【-1,1】,所以也叫【-1,1】的三次方所形成的盒子。

4.1 正交投影推导

  正交投影(也叫正射投影)的核心思想是将三维空间中的点沿着平行于某一轴的方向投影到二维平面上。由于投影方向是平行的,投影后的图像不会产生透视缩小效果,保持物体的比例和尺寸不变。正交投影即不符合近大远小规律的投影,不论投影平面离物体多远,投影之后,大小始终是不变的。
在这里插入图片描述
在这里插入图片描述
  如图所示,可视空间中的红球和黄球,不论离近平面上多远,当投影到近平面之后,始终那么大。

我们先给定视体(View Volume)的边界参数:

  • 左边界(Left): l l l
  • 右边界(Right): r r r
  • 下边界(Bottom): b b b
  • 上边界(Top): t t t
  • 近剪裁面(Near Plane): n n n
  • 远剪裁面(Far Plane): f f f

将缩放矩阵 S S S 与平移矩阵 T T T 相乘,得到正交投影矩阵 O O O

O = S × T O = S \times T O=S×T

具体计算如下:

S × T = [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 − 2 f − n 0 0 0 0 1 ] × [ 1 0 0 − r + l 2 0 1 0 − t + b 2 0 0 1 − f + n 2 0 0 0 1 ] S \times T = \begin{bmatrix} \frac{2}{r - l} & 0 & 0 & 0 \\ 0 & \frac{2}{t - b} & 0 & 0 \\ 0 & 0 & -\frac{2}{f - n} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0 & -\frac{r + l}{2} \\ 0 & 1 & 0 & -\frac{t + b}{2} \\ 0 & 0 & 1 & -\frac{f + n}{2} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} S×T= rl20000tb20000fn200001 × 1000010000102r+l2t+b2f+n1

进行矩阵乘法,最终得到标准正交投影矩阵 O O O

O = [ 2 r − l 0 0 − r + l r − l 0 2 t − b 0 − t + b t − b 0 0 − 2 f − n − f + n f − n 0 0 0 1 ] O = \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{2}{f - n} & -\frac{f + n}{f - n} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} O= rl20000tb20000fn20rlr+ltbt+bfnf+n1

4.2 正交投影调用

var projMatrix = new Matrix4();
//projMatrix.setOrtho(left, right, bottom, top, near, far);
projMatrix.setOrtho(-1.0, 1.0, -1.0, 1.0, 0, 0.5);

4.3 透视投影推导

  与正交投影不同,透视投影符合近大远小的规律,因此在视觉上,保留了物体在空间中的视觉深度感。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
  如图所示,可视空间中的红球和黄球,离近平面上越近,投影到近平面之后越大,离近平面上越远,投影到近平面之后越小。根据侧视图我们可以看到如下规律:
在这里插入图片描述
  远平面上的xyz,和近平面的xyz是相似三角形的关系。也就是说
[ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ] ⋅ ( x y z 1 ) = ( x ′ y ′ ? 1 ) = ( n x / z n y / z ? 1 ) = ( n x n y ? z ) \begin{bmatrix} ? & ? & ? & ? \\ ? & ? & ? & ? \\ ? & ? & ?& ? \\ ? & ? & ?& ? \\ \end{bmatrix}\cdot\begin{pmatrix} x \\ y \\ z\\ 1 \end{pmatrix} =\begin{pmatrix} x' \\ y' \\ ? \\ 1 \end{pmatrix}= \begin{pmatrix} {nx}/{z} \\{ny}/{z} \\ ? \\ 1 \end{pmatrix}= \begin{pmatrix} nx \\ ny \\ ? \\ z \end{pmatrix} ???????????????? xyz1 = xy?1 = nx/zny/z?1 = nxny?z
可以推出
[ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ] = [ n 0 0 0 0 n 0 0 ? ? ? ? 0 0 1 0 ] \begin{bmatrix} ? & ? & ? & ? \\ ? & ? & ? & ? \\ ? & ? & ?& ? \\ ? & ? & ?& ? \\ \end{bmatrix}=\begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ?& ? \\ 0 & 0 & 1& 0 \\ \end{bmatrix} ???????????????? = n0?00n?000?100?0
就差第三行了,继续观察

在这里插入图片描述
通过观察我们发现远平面的在挤压到近平面的过程中,近平面上的点始终是没变化,说明
( x y n 1 ) = ( n x n y n 2 1 ) \begin{pmatrix} x \\ y \\ n \\ 1 \end{pmatrix}= \begin{pmatrix} nx \\ ny \\ n^2 \\ 1 \end{pmatrix} xyn1 = nxnyn21
上式中n表示到近平面的距离,n变为了n平方,那现在问题来了
( ? ? ? ? ) ( x y n 1 ) = n 2 (????)\begin{pmatrix} x \\ y \\ n \\ 1 \end{pmatrix}= n^2 ???? xyn1 =n2
推出
( ? ? ? ? ) = ( 00 A B ) (????)=(0 0 AB) ????=00AB

通过观察我们发现又远平面的在挤压到近平面的过程中,中心点也是始终没变化,说明
( 0 0 f 1 ) = ( 0 0 f 2 f ) \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \end{pmatrix}= \begin{pmatrix} 0 \\ 0 \\ f^2 \\ f \end{pmatrix} 00f1 = 00f2f
注意 f 变为了 f 平方
( 00 A B ) ( 0 0 f 1 ) = f 2 (0 0 AB) \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \end{pmatrix}= f^2 00AB 00f1 =f2
两个式子解方程
{ A n + B = n 2 A f + B = f 2  =>  { A = f + n B = − f n \begin{cases} An + B = n^2 \\ Af + B = f^2 \end{cases}\text{ => } \begin{cases} A = f+n \\ B = -fn \end{cases} {An+B=n2Af+B=f2 => {A=f+nB=fn
现在我们就得到了完整的透视除法矩阵
[ n 0 0 0 0 n 0 0 0 0 f + n − f n 0 0 1 0 ] \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & f+n& -fn \\ 0 & 0 & 1& 0 \\ \end{bmatrix} n0000n0000f+n100fn0
但是还没完,还要对它再做一次正交矩阵变换,就得到了透视投影矩阵,首先定义参数:

视野角(FOV):摄像机的垂直视角。
宽高比(aspect):近平面宽度与高度比。
近裁剪面(n):摄像机到近平面距离。
远裁剪面(f):摄像机到远平面距离。

然后进行换算,上边界、右边界和宽高比的关系如下:
t = n ⋅ tan ⁡ ( FOV 2 ) t = n \cdot \tan\left(\frac{\text{FOV}}{2}\right) t=ntan(2FOV)

r = t ⋅ aspect r = t \cdot \text{aspect} r=taspect
正交投影矩阵和透视除法矩阵相乘
[ 2 r − l 0 0 − r + l r − l 0 2 t − b 0 − t + b t − b 0 0 − 2 f − n − f + n f − n 0 0 0 1 ] [ n 0 0 0 0 n 0 0 0 0 f + n − f n 0 0 1 0 ] \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{2}{f - n} & -\frac{f + n}{f - n} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}\begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & f+n& -fn \\ 0 & 0 & 1& 0 \\ \end{bmatrix} rl20000tb20000fn20rlr+ltbt+bfnf+n1 n0000n0000f+n100fn0

简化后,得到最终的透视投影矩阵为
P = [ 1 aspect ⋅ tan ⁡ ( FOV 2 ) 0 0 0 0 1 tan ⁡ ( FOV 2 ) 0 0 0 0 − ( f + n ) f − n − 2 f n f − n 0 0 − 1 0 ] P = \begin{bmatrix} \frac{1}{\text{aspect} \cdot \tan(\frac{\text{FOV}}{2})} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan(\frac{\text{FOV}}{2})} & 0 & 0 \\ 0 & 0 & \frac{-(f + n)}{f - n} & \frac{-2fn}{f - n} \\ 0 & 0 & -1 & 0 \end{bmatrix} P= aspecttan(2FOV)10000tan(2FOV)10000fn(f+n)100fn2fn0

4.3 透视投影调用

let mvpMatrix = new Matrix4();
//mvpMatrix.setPerspective(fov,aspect,near,far);
mvpMatrix.setPerspective(30,1,1,100);

5 总结

  本文讲解了模型、视图、投影变换的原理,并具体分析了MVP变换过程中的推导过程及调用过程,在实际应用中,各个框架都提供了进行这三个变换的方法,因此不必拘泥于具体的调用,了解原理即可。本文在理解上是有一定难度的,希望读者仔细揣摩,回见~


http://www.niftyadmin.cn/n/5666046.html

相关文章

四、JVM原理-4.2、内存管理

4.2、内存管理 4.2.1、JVM是如何判断一个对象是否可以被回收的 答: 在Java中,JVM使用垃圾回收算法来判断一个对象是否可以被回收。JVM使用的垃圾回收算法主要有两种:引用计数法和可达性分析法。 引用计数法:每个对象都有一个计数…

rk3568开机LOGO旋转调试

1.分区表中增加独立的LOGO分区 2.用户根据需要以某种方式动态更新LOGO分区中的图片。更新时,用户直接把原始图片更新到 LOGO分区中即可,不需要任何打包。当LOGO分区的图片无效时,则仍旧使用resource文件中默认的图片。 LOGO分区支持2张图片:图片1用于替换logo.bmp,图片2用…

Amazon EC2:引领企业迈向云计算的未来

在数字化转型的浪潮中,企业需要一个强大、灵活且安全的计算平台来支持其不断变化的业务需求。Amazon Elastic Compute Cloud(EC2)正是这样一个解决方案,它为企业提供了一个可扩展的云计算环境,帮助企业实现高效、低成本…

3D GS 测试自己的数据

环境配置 win11 vs2019cuda11.8driver522.06python3.10pytorch 2.4.0colmap3.8(可选,用于将图像生成点云) 安装 1 minicodagit 略 2 vs2019 在装cuda前安装, 选择c桌面开发即可, 环境变量path中配置C:\Program…

C++—string类接口与用法大总结

目录 1.string类的本质 2.string类的构造 1.普通构造 2.功能型构造 1.拷贝构造功能型 2.带参构造功能型 3.其余构造 3.operator[] 4.迭代器(iterator) 1.概念 2.改变string对象本身 3.正向迭代器(iterator) 4.反向迭代…

c++程序接收参数供python批处理调用

背景 用c做批处理,真的是让人头大的一件事情。后来在网上了解到可以用python脚本结合c做批处理,这样的方式真的方便多了。 python和c混合编程的方法很多,但是我这里只介绍一个自己常用的方法python调用c生成的exe做批处理,这个方…

JAVA开源项目 校园美食分享平台 计算机毕业设计

本文项目编号 T 033 ,文末自助获取源码 \color{red}{T033,文末自助获取源码} T033,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

GlusterFS 分布式文件系统

一、GlusterFS 概述 1.1 什么是GlusterFS GlusterFS 是一个开源的分布式文件系统,它可以将多个存储服务器结合在一起,创建一个大的存储池,供客户端使用。它不需要单独的元数据服务器,这样可以提高系统的性能和可靠性。由于没有…