three.js 之 Camera相机
介绍
three.js 之 Camera 相机
# 相机Camera的作用
查看 Three.js 的文档,可以看到 Camera (opens new window) 是一个抽象类,一般不直接使用,其他类型的 Camera 实现了这个抽象类。有
ArrayCamera
包含着一组子摄像机,常用于多人同屏的渲染,更好地提升VR场景的渲染性能StereoCamera
双透视摄像机(立体相机),常用于创建 3D 立体影像,比如 3D 电影之类或 VRCubeCamera
有6个渲染,分别是立方体的6个面,常用于渲染环境、反光等OrthographicCamera
正交相机,在这种投影模式下,无论物体距离相机距离远或者近,在最终渲染的图片中物体的大小都保持不变。这对于渲染2D场景或者UI元素是非常有用的。
// 正投影相机对象
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000)
复制代码PerspectiveCamera
透视相机,这一投影模式被用来模拟人眼所看到的景象,它是3D场景的渲染中使用得最普遍的投影模式。
// 透视投影相机对象
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000)
复制代码透视相机 和 正交相机的区别图
其他相机
# PerspectiveCamera (opens new window)透视相机
PerspectiveCamera(fov : Number, aspect : Number, near : Number, far : Number)
复制代码fov | fov表示视场,所谓视场就是能够看到的角度范围,人的眼睛大约能够看到180度的视场,视角大小设置要根据具体应用,一般游戏会设置60~90度 | 45 |
---|---|---|
aspect | aspect表示渲染窗口的长宽比,如果一个网页上只有一个全屏的canvas画布且画布上只有一个窗口,那么aspect的值就是网页窗口客户区的宽高比 | window.innerWidth /window.innerHeight |
near | near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 | 0.1 |
far | far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小,会有部分场景看不到 | 1000 |
透视图中,灰色的部分是视锥体,是可能被渲染的物体所在的区域。
fov
是视锥体竖直方向上的张角(是角度制而非弧度制)aspect
等于 width / height,是照相机水平方向和竖直方向长度的比值,通常设为 Canvas 的横纵比例。near
和far
分别是照相机到视锥体最近、最远的距离,均为正值,且far
应大于near
。通常我们设置
.position.set
的时候X Y Z
的倍数大于正交角度的10倍注意: 不要将 near 和 far 设置为比较极端的数值,如 0.0001 和 99999,这可能引起 bug,让 threejs 无法分辨物体的前后,导致闪动
import * as THREE from 'three'
// Canvas
const canvas = document.querySelector('#mainCanvas') as HTMLCanvasElement
// Scene
const scene = new THREE.Scene()
// Object
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({
color: 0x607d8b,
}),
)
scene.add(cube)
// width和height用来设置Three.js输出Canvas画布尺寸,同时用来辅助设置相机渲染范围
const width = window.innerWidth; //窗口文档显示区的宽度
const height = window.innerHeight; //窗口文档显示区的高度
// 透视投影相机
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 3000); // (角度, 长宽比, 近裁截面, 远裁截面)
camera.position.set(318, 162, 204);//通过相机控件OrbitControls旋转相机,选择一个合适场景渲染角度
camera.lookAt(0, 0, 0);
// Renderer
const renderer = new THREE.WebGLRenderer({
canvas,
})
renderer.setSize(canvas.clientWidth, canvas.clientHeight)
renderer.render(scene, camera)
复制代码效果如下:
- 多个模型效果更加明显 离你最近的模型会比离你最远的模型大不少
# OrthographicCamera (opens new window) 正交相机
OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )
复制代码参数(属性) | 含义 |
---|---|
left | 渲染空间的左边界 |
right | 渲染空间的右边界 |
top | 渲染空间的上边界 |
bottom | 渲染空间的下边界 |
near | near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1 |
far | far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值1000 |
三维场景中坐标值不在三维空间中的网格模型不会被渲染出来,会被剪裁掉,比如你把上面代码中far参数的值从1000更改为420,你会发现长方体的一部分无法显示。
为了保持照相机的横竖比例,需要保证
(right - left)
复制代码与
(top - bottom)
复制代码的比例与 Canvas 宽度与高度的比例一致。
import * as THREE from 'three'
import stats from '../common/stats'
// Canvas
const canvas = document.querySelector('#mainCanvas') as HTMLCanvasElement
// Scene
const scene = new THREE.Scene()
// Object
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({
color: 0x607d8b,
}),
)
scene.add(cube)
// width和height用来设置Three.js输出Canvas画布尺寸,同时用来辅助设置相机渲染范围
const width = window.innerWidth; //窗口文档显示区的宽度
const height = window.innerHeight; //窗口文档显示区的高度
// 正投影相机
const k = width / height; //Three.js输出的Cnavas画布宽高比
// var s = 200; //控制相机渲染空间左右上下渲染范围,s越大,相机渲染范围越大
const s = 100;//根据你想要渲染的粮仓范围设置相机渲染范围大小
//THREE.OrthographicCamera()创建一个正投影相机对象
// -s * k, s * k, s, -s, 1, 1000定义了一个长方体渲染空间,渲染空间外的模型不会被渲染
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000); // (左侧面, 右侧面, 上侧面, 下侧面, 近裁截面, 远裁截面)
// camera.position.set(200, 300, 200); //相机在Three.js坐标系中的位置
camera.position.set(292, 223, 185);//通过相机控件OrbitControls旋转相机,选择一个合适场景渲染角度
camera.lookAt(0, 0, 0); //相机指向Three.js坐标系原点
// Renderer
const renderer = new THREE.WebGLRenderer({
canvas,
})
renderer.setSize(canvas.clientWidth, canvas.clientHeight)
// Clock
const clock = new THREE.Clock()
// Animations
const tick = () => {
stats.begin()
const delta = clock.getDelta()
cube.rotation.y += 1 * delta
// Render
renderer.render(scene, camera)
stats.end()
requestAnimationFrame(tick)
}
tick()
复制代码效果如下:
- 无论如何 离你近的和离你远的模型都一个大小
# ArrayCamera 阵列相机
ArrayCamera (opens new window) 可以帮我们方便的在一张画布上渲染多个相机视角,这有点像在监控室的大屏幕上看多个摄像头或者是在同一台电脑上玩多人游戏时的分屏显示。
# StereoCamera 立体相机
StereoCamera (opens new window)立体相机,这个相机很好玩,我们可以很简单的将场景渲染出需要带VR / 3D眼镜才可以观看到的立体效果。
# CubeCamera 立方体相机
CubeCamera (opens new window)必须配合WebGLCubeRenderTarget (opens new window)一起使用,这个相机主要用于将当前场景的画面实时渲染成一个六面图(全景图的一种),用于创建类似镜面反射的效果。
# Camera 相机参数设置
Camera
相机可以当成一双眼睛 通过Camera
可以设置相机的角度距离参数 这样页面刚进来的时候 就可以在合适的区域内进行观看- 在three.js中 没有单位的概念 只有数字 没有任何单位的概念
- 注意: 在大多数属性发生改变之后,你将需要调用.updateProjectionMatrix (opens new window)来使得这些改变生效。
# 相机位置 .position.set(x,y,z)
- 通过.position.set (opens new window) 我们可以设置相机的默认的角度(三维向量
Vector3
) 分别代表(x,y,z)
camera.position.set(292, 223, 185);//通过相机控件OrbitControls旋转相机,选择一个合适场景渲染角度
复制代码- 通常我们不会随便设置相机位置 我们可以搭配相机控件
OrbitControls
来获取合适的相机.position
角度- 该调试方式适用于
OrthographicCamera
正交相机 和PerspectiveCamera
透视相机
- 该调试方式适用于
// 渲染循环
function render() {
renderer.render(scene, camera); //执行渲染操作
requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
console.log(camera.position);//通过相机控件OrbitControls旋转相机,选择一个合适场景渲染角度
}
复制代码# 相机指向坐标系 .lookAt(x,y,z)
- 通过.lookAt (opens new window) 我们可以设置相机在Three.js坐标系中的渲染位置 也就通过
AxesHelper
的到的Three.js坐标轴为准的渲染的位置分量
camera.lookAt(0, 0, 0); //相机指向Three.js坐标系原点
复制代码- 通常我们我们很依赖 模型提供的坐标系 如果模型到导出的坐标系符合需求 那我们可以不用设置具体参数 设置0即可
- 我们可以告诉建模师(美术) 把坐标原点放到我们需要的区域
- 如果我们设置了轨道控制器
OrbitControls
那么.lookAt
参数不会生效 取默认值为0
# 相机自适应页面的尺寸
通过window.onresize
监听页面尺寸是否改变 重新给画布赋值 并更新摄像机投影矩阵
注意: 修改了相机参数 需要用到.updateProjectionMatrix (opens new window)方法 进行参数更新
position (opens new window) 位置属性不需要
# 正投影相机OrthographicCamera (opens new window)自适应渲染
- 渲染区域变化了,要通过Three.js渲染器
.setSize()
方法重置渲染器渲染尺寸。
// onresize 事件会在窗口被调整大小时发生
window.onresize = () = >{
// 重置渲染器输出画布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 ();
};
复制代码# 透视投影相机PerspectiveCamera (opens new window)自适应渲染
- 渲染区域变化了,要通过Three.js渲染器
.setSize()
方法重置渲染器渲染尺寸。
// onresize 事件会在窗口被调整大小时发生
window.onresize = () = >{
// 重置渲染器输出画布canvas尺寸
renderer.setSize(window.innerWidth,window.innerHeight);
// 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
camera.aspect = window.innerWidth/window.innerHeight;
// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
camera.updateProjectionMatrix ();
};
复制代码渲染区域尺寸变化,相机的相关参数自然也需要变化,改变相机的参数后,注意需要执行相机对象.updateProjectionMatrix ()
方法更新相机对象的投影矩阵.projectionMatrix
,之所以需要手动更新,是因为Threejs为了提高渲染效率,Threejs系统每次执行渲染器WebGLRenderer
渲染方法.rener()
的时候不会读取相机相关的参数重新计算一次投影矩阵.projectionMatrix
,Threejs系统只会首次渲染的时候计算一次投影矩阵,所以当你改变影响相机投影矩阵的属性,自然需要调用.updateProjectionMatrix ()
更新相机对象的投影矩阵.projectionMatrix
。
# 预置的相机控制器
下面为大家介绍一下具体有哪些内置的相机控制器,以及它们分别提供怎样的功能
# 陀螺仪控制器(被删除)
- DeviceOrientationControls 绝大多数当下主流的移动设备都支持,用倾斜旋转手机的方式来控制我们的相机镜头。利用这个控制器可以很容易的创造出类似VR眼镜那样的体验。
# 飞行控制器 和 第一人称控制器
- FlyControls (opens new window),FirstPersonControls (opens new window) 这两个控制器提供的功能极为相似,都可以很容易的实现类似人在场景中移动的效果。在开发一些比如VR看房,虚拟展厅,虚拟世界探索游戏等需求时很常用。
# 指针锁定控制器
- PointerLockControls (opens new window) 使用这个控制器会隐藏鼠标指针,相机镜头会跟随mousemove事件旋转看向不同的方位,就像真正的射击游戏那样。当我们需要创建一个第一人称的3D游戏时,这个控制器是最佳选择。
# 环轨控制器(轨道控制器)
- OrbitControls (opens new window) 我们刚才其实已经手写过了这个控制器的核心逻辑。当然,这个内置的控制器比我们刚才写的要完善很多,比如让我们可以通过拖拽旋转镜头,使用滚轮放大或缩小等等。这个控制器不允许世界颠倒,这是它和下面轨迹球控制器的区别。
# 轨迹球控制器
- TrackballControls (opens new window) 轨迹球控制器,和环轨控制器的效果极为相似,区别是取消了垂直镜头的区间限制,允许世界颠倒。
# 滚动页面
- 页面多个内容滚动的时候(高度为
100vh
) 可以通过相机的y
轴移动 来显示这些内容
// 移动相机 下滚动所以是-y 让相机沿着-y轴移动
this.camera.position.y = -(window.scrollY / window.innerHeight) * 30 // 当前滚动的距离 / 屏幕高度 * 物体间距 下滚动所以是-y
复制代码# 获取当前相机位于坐标原点的距离
.length (opens new window)() 可以获取当前相机位于坐标原点的距离, 可以通过设置相应的值来约束OrbitControls
轨道控制器
- 内部是通过距离计算公式: 欧式距离 (opens new window), 进行计算的
// 获取当前相机位于坐标原点的距离
console.log(camera.position.length())
// 设置控制器的最大距离
controls.maxDistance = 450
// 设置控制器的最小距离
controls.minDistance = 260
复制代码# 参考文献
Three.js 之相机 Camera (opens new window)
v1.4.14