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 ();
};
原文链接:https://juejin.cn/post/7231089453695238204 作者:士必弘毅