three.js 之 Camera相机

2022-05-30 28 three.js

介绍

three.js 之 Camera 相机

# 相机Camera的作用

查看 Three.js 的文档,可以看到 Camera (opens new window) 是一个抽象类,一般不直接使用,其他类型的 Camera 实现了这个抽象类。有

  • ArrayCamera 包含着一组子摄像机,常用于多人同屏的渲染,更好地提升VR场景的渲染性能
  • StereoCamera 双透视摄像机(立体相机),常用于创建 3D 立体影像,比如 3D 电影之类或 VR
  • CubeCamera 有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)

复制代码

透视相机 和 正交相机的区别图

在这里插入图片描述

其他相机

image-20220606145127687

# 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

img

  • 透视图中,灰色的部分是视锥体,是可能被渲染的物体所在的区域。fov 是视锥体竖直方向上的张角(是角度制而非弧度制)

  • aspect等于 width / height,是照相机水平方向和竖直方向长度的比值,通常设为 Canvas 的横纵比例。

  • nearfar 分别是照相机到视锥体最近、最远的距离,均为正值,且 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)
复制代码

效果如下:

  • 多个模型效果更加明显 离你最近的模型会比离你最远的模型大不少

img

# 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,你会发现长方体的一部分无法显示。

img

为了保持照相机的横竖比例,需要保证

(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()
复制代码

效果如下:

  • 无论如何 离你近的和离你远的模型都一个大小

v2-658ad7429ff462798328098b7966dcc6_b

# ArrayCamera 阵列相机

ArrayCamera (opens new window) 可以帮我们方便的在一张画布上渲染多个相机视角,这有点像在监控室的大屏幕上看多个摄像头或者是在同一台电脑上玩多人游戏时的分屏显示。

# StereoCamera 立体相机

StereoCamera (opens new window)立体相机,这个相机很好玩,我们可以很简单的将场景渲染出需要带VR / 3D眼镜才可以观看到的立体效果。

image-20220606145331665

# CubeCamera 立方体相机

CubeCamera (opens new window)必须配合WebGLCubeRenderTarget (opens new window)一起使用,这个相机主要用于将当前场景的画面实时渲染成一个六面图(全景图的一种),用于创建类似镜面反射的效果。

# Camera 相机参数设置

  • Camera相机可以当成一双眼睛 通过Camera可以设置相机的角度距离参数 这样页面刚进来的时候 就可以在合适的区域内进行观看
  • 在three.js中 没有单位的概念 只有数字 没有任何单位的概念
  • 注意: 在大多数属性发生改变之后,你将需要调用.updateProjectionMatrix (opens new window)来使得这些改变生效。

img

# 相机位置 .position.set(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

image-20220529201212577

# 相机自适应页面的尺寸

通过window.onresize监听页面尺寸是否改变 重新给画布赋值 并更新摄像机投影矩阵

# 正投影相机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眼镜那样的体验。

# 飞行控制器 和 第一人称控制器

# 指针锁定控制器

  • PointerLockControls (opens new window) 使用这个控制器会隐藏鼠标指针,相机镜头会跟随mousemove事件旋转看向不同的方位,就像真正的射击游戏那样。当我们需要创建一个第一人称的3D游戏时,这个控制器是最佳选择。

# 环轨控制器(轨道控制器)

  • OrbitControls (opens new window) 我们刚才其实已经手写过了这个控制器的核心逻辑。当然,这个内置的控制器比我们刚才写的要完善很多,比如让我们可以通过拖拽旋转镜头,使用滚轮放大或缩小等等。这个控制器不允许世界颠倒,这是它和下面轨迹球控制器的区别。

# 轨迹球控制器

# 滚动页面

  • 页面多个内容滚动的时候(高度为100vh) 可以通过相机的y轴移动 来显示这些内容
// 移动相机 下滚动所以是-y 让相机沿着-y轴移动
this.camera.position.y = -(window.scrollY / window.innerHeight) * 30 // 当前滚动的距离 / 屏幕高度 * 物体间距 下滚动所以是-y

复制代码

# 获取当前相机位于坐标原点的距离

.length (opens new window)() 可以获取当前相机位于坐标原点的距离, 可以通过设置相应的值来约束OrbitControls轨道控制器

// 获取当前相机位于坐标原点的距离
console.log(camera.position.length())

// 设置控制器的最大距离
controls.maxDistance = 450
// 设置控制器的最小距离
controls.minDistance = 260

复制代码

# 参考文献

Three.js 之相机 Camera (opens new window)

Three.js里的相机和镜头控制 | 《Three.js零基础直通06》 (opens new window)

Three.js零基础入门教程(郭隆邦) (opens new window)

Last Updated: 2023/3/23 08:47:22
来发评论吧~
Powered By Valine
v1.4.14
未加载音频 - (ಗ ‸ ಗ )
00:00 / 00:00