Three.js 相机:从数学原理到源码实现

王者杯·14天创作挑战营·第5期 10w+人浏览 1.1k人参与

Three.js 是一个强大的 WebGL 库,用于在浏览器中创建 3D 图形和交互式体验。相机是 Three.js 中的重要组成部分,它决定了如何将 3D 场景投影到 2D 屏幕上。本文将深入探讨 Three.js 相机的数学原理、源码实现、API 使用以及实际应用。


一、相机的数学原理

相机的核心是将 3D 世界中的物体投影到 2D 屏幕上。Three.js 中的相机主要分为两种类型:透视投影相机(Perspective Camera)正交投影相机(Orthographic Camera)

1.1 投影矩阵

投影矩阵是将 3D 点投影到 2D 屏幕的核心工具。Three.js 中的投影矩阵基于线性代数中的矩阵变换。

1.1.1 透视投影矩阵

透视投影矩阵模拟了人类视觉的特性,近处的物体看起来大,远处的物体看起来小。其公式如下:

P = ( 2 n r − l 0 r + l r − l 0 0 2 n t − b t + b t − b 0 0 0 − ( f + n ) f − n − 2 f n f − n 0 0 − 1 0 ) P = \begin{pmatrix} \frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\ 0 & 0 & \frac{-(f+n)}{f-n} & \frac{-2fn}{f-n} \\ 0 & 0 & -1 & 0 \end{pmatrix} P= rl2n0000tb2n00rlr+ltbt+bfn(f+n)100fn2fn0

其中:

  • n n n 是近裁剪面距离。
  • f f f 是远裁剪面距离。
  • l , r l, r l,r 是左右视界的范围。
  • b , t b, t b,t 是上下视界的范围。

1.1.2 正交投影矩阵

正交投影矩阵不考虑透视效果,适用于技术绘图等场景。其公式如下:

P = ( 2 r − l 0 0 − ( r + l ) r − l 0 2 t − b 0 − ( t + b ) t − b 0 0 2 n − f − ( n + f ) n − f 0 0 0 1 ) P = \begin{pmatrix} \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}{n-f} & \frac{-(n+f)}{n-f} \\ 0 & 0 & 0 & 1 \end{pmatrix} P= rl20000tb20000nf20rl(r+l)tb(t+b)nf(n+f)1

1.2 视图矩阵

视图矩阵(View Matrix)是将世界坐标系中的物体变换到视图坐标系(Camera Space)中的矩阵。视图矩阵的作用是将场景中的物体相对于相机的位置进行变换,使得相机的位置和方向成为新的原点和坐标轴。

1.2.1 视图矩阵的推导

视图矩阵的推导可以通过以下步骤完成:

  1. 计算相机的坐标系:根据相机的位置、目标点和上方向,计算出相机的局部坐标系。
  2. 构建变换矩阵:将世界坐标系中的点变换到相机的局部坐标系中。

具体来说,视图矩阵可以通过以下公式计算:

V = ( u x v x n x − p x u y v y n y − p y u z v z n z − p z 0 0 0 1 ) V = \begin{pmatrix} u_x & v_x & n_x & -p_x \\ u_y & v_y & n_y & -p_y \\ u_z & v_z & n_z & -p_z \\ 0 & 0 & 0 & 1 \end{pmatrix} V= uxuyuz0vxvyvz0nxnynz0pxpypz1

其中:

  • ( u , v , n ) (u, v, n) (u,v,n) 是相机的局部坐标系的三个单位向量。
  • ( p x , p y , p z ) (p_x, p_y, p_z) (px,py,pz) 是相机的位置。

1.2.2 LookAt 函数

在 Three.js 中,视图矩阵的计算可以通过 lookAt 方法实现。lookAt 方法会根据相机的位置、目标点和上方向,自动计算出视图矩阵。

camera.lookAt(targetX, targetY, targetZ);

在 OpenGL 或其他图形库中,LookAt 函数的实现通常基于以下公式:

V = ( u x ∥ u ∥ v x ∥ v ∥ n x ∥ n ∥ − p x u y ∥ u ∥ v y ∥ v ∥ n y ∥ n ∥ − p y u z ∥ u ∥ v z ∥ v ∥ n z ∥ n ∥ − p z 0 0 0 1 ) V = \begin{pmatrix} \frac{u_x}{\|u\|} & \frac{v_x}{\|v\|} & \frac{n_x}{\|n\|} & -p_x \\ \frac{u_y}{\|u\|} & \frac{v_y}{\|v\|} & \frac{n_y}{\|n\|} & -p_y \\ \frac{u_z}{\|u\|} & \frac{v_z}{\|v\|} & \frac{n_z}{\|n\|} & -p_z \\ 0 & 0 & 0 & 1 \end{pmatrix} V= uuxuuyuuz0vvxvvyvvz0nnxnnynnz0pxpypz1

其中:

  • u u u 是右方向向量。
  • v v v 是上方向向量。
  • n n n 是前方向向量。

1.2.3 视图矩阵的作用

视图矩阵的作用是将场景中的物体从世界坐标系变换到相机坐标系,使得相机的位置和方向成为新的原点和坐标轴。视图矩阵的计算是三维图形渲染中的关键步骤之一。


二、Three.js 相机源码分析

Three.js 的相机实现主要集中在 Camera 类及其子类中。

2.1 Camera 类

Camera 是所有相机的基类,定义了相机的基本功能,包括:

  • matrixWorld:相机的世界变换矩阵。
  • updateMatrixWorld:更新相机的变换矩阵。

2.2 PerspectiveCamera 类

PerspectiveCamera 实现了透视投影相机。其构造函数如下:

constructor(fov, aspect, near, far) {
    super();
    this.fov = fov;
    this.aspect = aspect;
    this.near = near;
    this.far = far;
    this.updateProjectionMatrix();
}

updateProjectionMatrix 方法根据 fov、aspect、near 和 far 计算投影矩阵。

2.3 OrthographicCamera 类

OrthographicCamera 实现了正交投影相机。其构造函数如下:

constructor(left, right, top, bottom, near, far) {
    super();
    this.left = left;
    this.right = right;
    this.top = top;
    this.bottom = bottom;
    this.near = near;
    this.far = far;
    this.updateProjectionMatrix();
}

updateProjectionMatrix 方法根据 left、right、top、bottom、near 和 far 计算投影矩阵。

2.4 CameraHelper 类

CameraHelper 是一个辅助类,用于可视化相机的视锥体。它可以帮助开发者调试相机的参数设置。

const cameraHelper = new CameraHelper(camera);
scene.add(cameraHelper);

2.5 Shader 中的视图矩阵

在 Shader 中,视图矩阵和投影矩阵通常用于将模型矩阵变换到屏幕空间。以下是 Three.js 中 Shader 的示例代码:

2.5.1 顶点着色器

uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectionMatrix;

void main() {
    // 将模型矩阵、视图矩阵和投影矩阵相乘
    gl_Position = u_ProjectionMatrix * u_ViewMatrix * modelViewMatrix * vec4(position, 1.0);
}

2.5.2 片元着色器

uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectionMatrix;

void main() {
    // 将模型矩阵、视图矩阵和投影矩阵相乘
    gl_Position = u_ProjectionMatrix * u_ViewMatrix * modelViewMatrix * vec4(position, 1.0);
}

2.5.3 逆矩阵的应用

在 Shader 中,逆矩阵通常用于将屏幕空间中的点变换回世界空间。以下是逆矩阵的应用示例:

uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectionMatrix;

void main() {
    // 将屏幕空间中的点变换回世界空间
    vec4 worldPos = inverse(u_ProjectionMatrix * u_ViewMatrix) * vec4(gl_FragCoord, 1.0);
}

2.5.4 视图矩阵的更新

在 Three.js 中,视图矩阵的更新可以通过以下代码实现:

camera.updateMatrixWorld();
camera.updateProjectionMatrix();

三、Three.js 相机 API 使用

Three.js 提供了丰富的 API 来控制相机的行为。

3.1 基本操作

3.1.1 设置相机位置和方向

camera.position.set(x, y, z); // 设置相机位置
camera.lookAt(x, y, z); // 设置相机目标点
camera.up.set(x, y, z); // 设置相机的上方向

3.1.2 更新投影矩阵

camera.updateProjectionMatrix(); // 更新投影矩阵

3.2 常用方法

3.2.1 计算屏幕坐标

const vector = new THREE.Vector3();
vector.setFromMatrixPosition(object.matrixWorld).applyProjection(camera.projectionMatrix);
const x = (vector.x + 1) * (window.innerWidth / 2);
const y = (-vector.y + 1) * (window.innerHeight / 2);

3.2.2 计算世界坐标

const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mousePosition, camera);
const intersects = raycaster.intersectObjects(scene.children);

3.3 相机控制库

Three.js 社区提供了许多相机控制库,例如 OrbitControlsTrackballControls,方便开发者快速实现相机的交互控制。

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
controls.enablePan = true;
controls.enableRotate = true;

3.4 常用 API 表格

以下是 Three.js 相机中常用 API 的汇总:

API描述参数示例代码应用场景
position.set(x, y, z)设置相机的位置。x, y, z:相机在世界坐标系中的位置。camera.position.set(0, 5, 10);初始化相机位置或动态调整相机位置。
lookAt(x, y, z)设置相机的目标点,使相机朝向该点。x, y, z:目标点的坐标。camera.lookAt(0, 0, 0);初始化相机视角或动态调整相机视角。
up.set(x, y, z)设置相机的上方向向量。x, y, z:上方向向量的坐标。camera.up.set(0, 1, 0);初始化相机的上方向或动态调整相机的上方向。
updateProjectionMatrix()更新投影矩阵。camera.updateProjectionMatrix();在修改相机参数(如 fov、aspect、near、far)后调用。
fov设置或获取相机的视场角(仅适用于透视投影相机)。fov:视场角,以度为单位。camera.fov = 75;初始化相机的视场角或动态调整相机的视场角。
aspect设置或获取相机的宽高比。aspect:宽高比。camera.aspect = window.innerWidth / window.innerHeight;初始化相机的宽高比或窗口大小变化时更新宽高比。
near设置或获取相机的近裁剪面距离。near:近裁剪面距离。camera.near = 0.1;初始化相机的近裁剪面或动态调整相机的近裁剪面。
far设置或获取相机的远裁剪面距离。far:远裁剪面距离。camera.far = 1000;初始化相机的远裁剪面或动态调整相机的远裁剪面。
getWorldDirection()获取相机的朝向向量。const direction = camera.getWorldDirection();计算相机的朝向向量,用于物理模拟或交互控制。
Viewport设置相机的视口区域。x, y, width, height:视口区域的坐标和尺寸。camera.viewport = new THREE.Vector4(x, y, width, height);在多相机渲染或分屏渲染时使用。

四、相机的实际应用

4.1 游戏开发

在游戏开发中,相机用于控制玩家的视角。例如,第一人称射击游戏通常使用透视投影相机,而第三人称游戏可能使用自由视角相机。

4.2 VR/AR 应用

在 VR/AR 应用中,相机用于模拟用户的视角。Three.js 结合 WebXR API 可以实现沉浸式的 VR 体验。

4.3 3D 建模工具

在 3D 建模工具中,相机用于观察和编辑 3D 模型。正交投影相机常用于技术绘图,而透视投影相机用于展示模型的效果。


五、总结

Three.js 相机是 3D 开发中的核心工具,理解其数学原理、源码实现和 API 使用对开发者来说至关重要。通过本文的介绍,希望读者能够更好地掌握 Three.js 相机的使用方法,并在实际项目中灵活应用。


参考文献

  1. Three.js 官方文档
  2. Three.js 相机源码
  3. Three.js 透视投影相机源码
  4. Three.js 正交投影相机源码
  5. Three.js 相机辅助类源码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值