Three.js相机Camera控件知识梳理

JavaScript/前端
407
0
0
2023-05-26
目录
  • 1. 相机类型
  • 1.1 正交相机
  • 1.2 透视相机
  • 2. 相机属性
  • 2.1 视角(FOV)
  • 2.2 宽高比(Aspect)
  • 2.3 近裁剪面(Near)和远裁剪面(Far)
  • 3. 不同方向的投影视图
  • 3.1 x轴方向观察
  • 3.2 y轴方向观察
  • 3.3 z轴方向观察z轴方向观察
  • 4. 相机动画(.position和.lookAt())
  • 4.1 相机运动动画
  • 4.2 相机圆周运动相机圆周运动
  • 4.3 执行lookAt()计算相机视线方向
  • 5. 相机控件OrbitControls
  • 5.1 OrbitControls使用
  • 6. 相机控件MapControls
  • 6.1 MapControls使用
  • 7. 窗口变化的自适应渲染
  • 7.1 正投影相机OrthographicCamera自适应渲染
  • 7.2 透视投影相机PerspectiveCamera自适应渲染

1. 相机类型

Three.js 主要提供了两种类型的相机:正交相机(OrthographicCamera)和透视相机(PerspectiveCamera)。

1.1 正交相机

正交相机(OrthographicCamera)使用正交投影进行渲染。在正交投影中,物体的大小不会随着距离的增加而减小,这意味着所有物体在渲染时保持相同的尺寸,不受距离的影响。这种相机在制作 2D 游戏和 CAD 工具等应用中非常有用。

创建正交相机的代码如下:

const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
// 正投影相机案例
const width = window.innerWidth; //canvas画布宽度
const height = window.innerHeight; //canvas画布高度
const k = width / height; //canvas画布宽高比
const s = 600;//控制left, right, top, bottom范围大小
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 8000);

参数(属性)

含义

left

渲染空间的左边界

right

渲染空间的右边界

top

渲染空间的上边界

bottom

渲染空间的下边界

near

near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1

far

far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值2000

1.2 透视相机

透视相机(PerspectiveCamera)使用透视投影进行渲染。在透视投影中,物体的大小会随着距离的增加而减小,这使得远离相机的物体看起来更小,符合现实世界中的透视效果。这种相机在制作 3D 游戏和仿真应用中非常常见。

创建透视相机的代码如下:

const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
//透视相机案例
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = 800; //宽度
const height = 500; //高度
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);

参数

含义

fov

相机视锥体竖直方向视野角度

aspect

相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height

near

相机视锥体近裁截面相对相机距离

far

相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向

2. 相机属性

Three.js 中的相机具有一些基本属性,这些属性决定了相机的视角和视野。

2.1 视角(FOV)

仅透视相机具有视角属性(FOV)。视角表示相机的垂直视野范围,单位为度。较大的视角值会导致更大的视野,但可能会产生畸变。较小的视角值则会产生更窄的视野和更低的畸变。

2.2 宽高比(Aspect)

仅透视相机具有宽高比属性。宽高比表示相机水平视野范围与垂直视野范围的比值。通常,宽高比应该与渲染目标(如 Canvas 或 WebGLRenderTarget)的宽高比相同,以避免图像被拉伸或压缩。

2.3 近裁剪面(Near)和远裁剪面(Far)

近裁剪面和远裁剪面定义了相机的渲染范围。位于近裁剪面之前的物体和位于远裁剪面之后的物体都不会被渲染。为了提高渲染性能,通常应该尽量将近裁剪面和远裁剪面之间的距离设置得较小。

3. 不同方向的投影视图

3.1 x轴方向观察

// 通过UI按钮改变相机观察角度
document.getElementById('x').addEventListener('click', function () {
    camera.position.set(500, 0, 0); //x轴方向观察
    camera.lookAt(0, 0, 0); //重新计算相机视线方向
})

3.2 y轴方向观察

// 通过UI按钮改变相机观察角度
document.getElementById('y').addEventListener('click', function () {
    camera.position.set(0, 500, 0); //y轴方向观察
    camera.lookAt(0, 0, 0); //重新计算相机视线方向
})

3.3 z轴方向观察z轴方向观察

// 通过UI按钮改变相机观察角度
document.getElementById('z').addEventListener('click', function () {
    camera.position.set(0, 0, 500); //z轴方向观察
    camera.lookAt(0, 0, 0); //重新计算相机视线方向
})

4. 相机动画(.position和.lookAt())

通过相机对象Camera.position属性和.lookAt()方法,可实现一段相机动画。

4.1 相机运动动画

改变相机的位置.position,三维场景在canvas画布上呈现不同的效果,如果连续改变相机的位置.position,就可以获得一个动画效果。

课件案例源码是一个工厂模型,相机在空中俯视工厂,如果在渲染循环中不停地改变相机位置,这时候产生的视觉效果,就好比你在天上运动,看地面的效果。

// 渲染循环
function render() {
    camera.position.z -= 0.3;//相机直线运动动画
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

4.2 相机圆周运动相机圆周运动

在渲染循环中,改变相机位置,在XOZ平面上绕着y轴圆周运动。

// 渲染循环
let angle = 0; //用于圆周运动计算的角度值
const R = 100; //相机圆周运动的半径
function render() {
    angle += 0.01;
    // 相机y坐标不变,在XOZ平面上做圆周运动
    camera.position.x = R * Math.cos(angle);
    camera.position.z = R * Math.sin(angle);
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

4.3 执行lookAt()计算相机视线方向

改变.position属性后,如果不执行.lookAt()方法,相机的观察方向默认不变。

如果你希望相机圆周运动的同时,改变相机视线方向,保持相机镜头始终指向坐标原点或其它位置,需要每次改变.position属性后,重新执行一遍.lookAt()方法

function render() {
    angle += 0.01;
    camera.position.x = R * Math.cos(angle);
    camera.position.z = R * Math.sin(angle);
    // .position改变,重新执行lookAt(0,0,0)计算相机视线方向
    camera.lookAt(0,0,0);
    requestAnimationFrame(render);
}
render();

5. 相机控件OrbitControls

通常需要为用户提供一种直观的方式来浏览和操作场景。OrbitControls 是 Three.js 提供的一种常用的相机控制器,允许用户通过鼠标或触摸屏操作来旋转、平移和缩放场景。

5.1 OrbitControls使用

  • 旋转:拖动鼠标左键
  • 缩放:滚动鼠标中键
  • 平移:拖动鼠标右键

OrbitControls本质上就是改变相机的参数,比如相机的位置属性,改变相机位置也可以改变相机拍照场景中模型的角度,实现模型的360度旋转预览效果,改变透视投影相机距离模型的距离,就可以改变相机能看到的视野范围。

// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
    renderer.render(scene, camera); //执行渲染操作
    console.log('camera.position',camera.position);
});//监听鼠标、键盘事件
//相关限制方法:
controls.enablePan = false; //禁止平移
controls.enableZoom = false;//禁止缩放
controls.enableRotate = false; //禁止旋转
// 缩放范围
controls.minZoom = 0.5;
controls.maxZoom = 2;
// 上下旋转范围
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI/2;
// 左右旋转范围
controls.minAzimuthAngle = -Math.PI/2;
controls.maxAzimuthAngle = Math.PI/2;
//更新方法
function animate() {
  requestAnimationFrame(animate);
  // 更新控制器
  controls.update();
  // 渲染场景
  renderer.render(scene, camera);
}

6. 相机控件MapControls

在某些 Three.js 应用中,例如地图、地形或者 GIS 类型的项目,需要为用户提供一种直观且符合习惯的方式来浏览和操作场景。MapControls 是一个类似于 Google Maps 风格的相机控制器,允许用户通过鼠标和触摸屏操作来平移、缩放和旋转场景。

6.1 MapControls使用

  • 平移:鼠标左键拖动
  • 旋转:鼠标右键拖动
  • 缩放:鼠标中键滚动

MapControls本质上就是改变相机的参数,比如相机的位置属性、相机目标观察点。

// 引入相机控件`MapControls`
import { MapControls } from 'three/addons/controls/OrbitControls.js';
const controls = new MapControls(camera, renderer.domElement);
controls.addEventListener('change', function () {
    // 鼠标右键旋转时候,查看.position变化
    // 鼠标左键拖动的时候,查看.position、.target的位置会变化
    console.log('camera.position',camera.position);
    console.log('controls.target',controls.target);
});
//相关限制方法:
controls.enablePan = false; //禁止平移
controls.enableZoom = false;//禁止缩放
controls.enableRotate = false; //禁止旋转
//相机位置与观察目标点最小值
controls.minDistance = 200;
//相机位置与观察目标点最大值
controls.maxDistance = 500;
// 上下旋转范围
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI/2;
// 左右旋转范围
controls.minAzimuthAngle = -Math.PI/2;
controls.maxAzimuthAngle = Math.PI/2;
//更新方法
function animate() {
  requestAnimationFrame(animate);
  // 更新控制器
  controls.update();
  // 渲染场景
  renderer.render(scene, camera);
}

7. 窗口变化的自适应渲染

在开发 Three.js 项目时,我们需要考虑到不同的设备和屏幕尺寸。当用户调整浏览器窗口大小时,我们希望场景能够自适应地进行调整,以保持正确的比例和尺寸。

要实现自适应渲染,我们需要在浏览器窗口大小发生变化时更新相机和渲染器的设置。首先,我们需要为 window 对象添加一个 resize 事件监听器:

window.addEventListener('resize', onWindowResize);

接下来,我们定义 onWindowResize 函数。在这个函数中,我们需要完成以下任务:

  • 更新相机的宽高比(aspect)。
  • 更新相机的投影矩阵。
  • 更新渲染器的大小。

7.1 正投影相机OrthographicCamera自适应渲染

// onresize 事件会在窗口被调整大小时发生
function onWindowResize(){
  // 重置渲染器输出画布canvas尺寸
  renderer.setSize(window.innerWidth,window.innerHeight);
  // 重置相机投影的相关参数
  k = window.innerWidth/window.innerHeight;//窗口宽高比
  camera.left = -s*k;
  camera.right = s*k;
  camera.top = s;
  camera.bottom = -s;
  // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
  // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
  // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
  camera.updateProjectionMatrix ();
};

7.2 透视投影相机PerspectiveCamera自适应渲染

// onresize 事件会在窗口被调整大小时发生
function onWindowResize(){
  // 重置渲染器输出画布canvas尺寸
  renderer.setSize(window.innerWidth,window.innerHeight);
  // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
  camera.aspect = window.innerWidth/window.innerHeight;
  // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
  // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
  // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
  camera.updateProjectionMatrix ();
};