课程表

three.js课程

工具箱
速查手册

Three 相机对象

当前位置:免费教程 » JS/JS库/框架 » three.js

Threejs是场景、渲染器、相机三大部分之一,在第一章新手上路部分已经进行了简单介绍。对于初学者而言,只要知道相机对象的作用是什么,可以不用深究,理解Threejs相机对象的最简单的方式就是参考生活中真实的相机拍照过程。

Threejs常用的相机对象就是正投影相机OrthographicCamera和透视投影相机PerspectiveCamera, 初学者对这两种相机对象建立一个基本的印象即可,知道什么样的场景要选择哪种相机。在我们之前的教程中,使用的都是正投影相机OrthographicCamera。

无论正投影相机还是透视投影相机本质上都是对视图矩阵、投影矩阵的封装。不同的投影相机就是封装的不同投影矩阵,如果你想深入学习,可以先学习WebGL或计算机图形学中的视图矩阵和投影矩阵。如果不想深入学习,只要会参考案例代码或查阅Threejs文档修改相机相关参数即可。


正投影相机OrthographicCamera和透视投影相机PerspectiveCamera

针对不同应用的三维场景需要使用不同的投影方式,比如机械、工业设计领域常常采用正投影(平行投影), 大型游戏场景往往采用透视投影(中心投影)。为了完成三维场景不同的投影方式,three.js封装WebGL API和相关算法,提供了正投影相机OrthographicCamera和透视投影相机PerspectiveCamera。

three.js中相机对象之间的关系如下图:

1.jpg

正投影和透视投影简单解释

下面对正投影相机和透视投影相机的投影算法进行简单介绍,对于初学者你有一个印象就可以,如果想深入了解,可以学习图形学或者阅读threejs官方源码src目录下文件OrthographicCamera.js和PerspectiveCamera.js。

生活中的物体都是三维的,但是人的眼睛只能看到正面,不能看到被遮挡的背面,三维几何体在人眼睛中的效果就像一张相机拍摄的二维照片,你看到的是一个2D的投影图。 空间几何体转化为一个二维图的过程就是投影,不同的投影方式意味着投影尺寸不同的算法。

1.jpg

对于正投影而言,一条直线放置的角度不同,投影在投影面上面的长短不同;对于透视投影而言,投影的结果除了与几何体的角度有关,还和距离相关, 人的眼睛观察世界就是透视投影,比如你观察一条铁路距离越远你会感到两条轨道之间的宽度越小。无论正投影还是透视投影,three.js都对相关的投影算法进行了封装, 大家只需要根据不同的应用场景自行选择不同的投影方式。使用OrthographicCamera相机对象的时候,three.js会按照正投影算法自动计算几何体的投影结果; 使用PerspectiveCamera相机对象的时候,three.js会按照透视投影算法自动计算几何体的投影结果。

正投影相机对象OrthographicCamera

/**
 * 正投影相机设置
 */
var width = window.innerWidth; //窗口宽度
var height = window.innerHeight; //窗口高度
var k = width / height; //窗口宽高比
var s = 150; //三维场景显示范围控制系数,系数越大,显示的范围越大
//创建相机对象
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
camera.position.set(200, 300, 200); //设置相机位置
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
// 构造函数格式
OrthographicCamera( left, right, top, bottom, near, far )

在线运行案例

                参数(属性)                含义
                left                渲染空间的左边界
                right                渲染空间的右边界
                top                渲染空间的上边界
                bottom                渲染空间的下边界
                near                near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1
                far                far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值1000


正投影相机原理图:

     1.png

三维场景中坐标值不在三维空间中的网格模型不会被渲染出来,会被剪裁掉,比如你把上面代码中far参数的值从1000更改为420,你会发现底部的圆柱体的一部分无法显示。

注意

左右边界的距离与上下边界的距离比值与画布的渲染窗口的宽高比例要一致,否则三维模型的显示效果会被单方向不等比例拉伸。

构造函数OrthographicCamera的参数( left,right,top,bottom,near,far)本质上是对WebGL投影矩阵的封装,宽度width、高度height越大,三维模型顶点的位置坐标就会越大,超出可视区域的网格模型就会被剪裁掉, 不会再显示在屏幕上,大家还可以看到参数left与right、参数top与bottom互为相反数,这样做的目的是lookAt指向的对象能够显示在canvas画布的中间位置。

透视投影相机PerspectiveCamera

基础代码:

/**
 * 透视投影相机设置
 */
var width = window.innerWidth; //窗口宽度
var height = window.innerHeight; //窗口高度
/**透视投影相机对象*/
var camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 300, 200); //设置相机位置
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)

在线运行案例

构造函数PerspectiveCamera格式:

PerspectiveCamera( fov, aspect, near, far )

参数解释:

  参数                含义                默认值
fovfov表示视场,所谓视场就是能够看到的角度范围,人的眼睛大约能够看到180度的视场,视角大小设置要根据具体应用,一般游戏会设置60~90度45
aspectaspect表示渲染窗口的长宽比,如果一个网页上只有一个全屏的canvas画布且画布上只有一个窗口,那么aspect的值就是网页窗口客户区的宽高比window.innerWidth/window.innerHeight
nearnear属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。0.1
farfar属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小,会有部分场景看不到1000

透视投影相机原理图:

1.png

相机位置.posiiotn和.lookAt(相机拍摄目标位置)

相机位置就好比现实中,相机镜头所在的位置。而lookAt自然就是指摄像头指向的位置。

1.png

相机Camera的基类是Object3D,相机对象Camera具有位置属性.posiiotn,通过位置属性.posiiotn可以设置相机的位置。

.lookAt()方法用来指定相机拍摄对象的坐标位置,.lookAt()方法的参数是表示位置坐标的三维向量对象Vector3,所以.lookAt()方法的参数可以通过代码new THREE.Vector3(x,y,z)设置。实际开发的时候,你希望相机对准哪个对象,就返回那个对象的位置属性.posiiotn,比如上面代码中的scene.position, 就表示返回场景scene的位置坐标,如果把scene.position换成某个网格模型对象的位置就是mesh.position,mesh.position表示网格模型mesh的本地位置坐标。通过相机观察点的位置和.lookAt()方法指向的位置就可以计算出相机的拍摄角度,本质上就是计算出相机对象的视图矩阵.matrixWorldInverse。

对于透视投影而言,相机位置与lookAt指向的观察目标位置间隔距离越小,场景中的三维模型放大倍数越大,准确地说是透视投影相机可以拍摄的范围更小,同时场景Scene中超出的相机参数约束范围的部分会被剪裁掉, 比如更改上面代码camera.position.set(100,200,200);为(20,20,20),测试结果你会发现立方体几何体放大显示,超出区域被剪裁。

相机位置放置

如果是观察一个产品外观效果,相机就位于几何体的外面,如果是室内漫游预览,就把相机放在房间三维模型的内部。


浏览器窗口尺寸变化(自适应渲染)

开发的过程中你可能会遇到这样一个问题,通过鼠标拖动使浏览器的窗口变大,因为Threejs渲染器的渲染尺寸范围没有跟着变化,出现局部空白区域。对于这种情况要做的就是重新获取浏览器窗口新的宽高尺寸,然后通过新的宽高尺寸更新相机Camera和渲染器WebGLRenderer的参数即可。

视图矩阵.matrixWorldInverse和投影矩阵.projectionMatrix

所谓相机对象Camera本质上就是视图矩阵.matrixWorldInverse和投影矩阵.projectionMatrix,Threejs渲染场景的时候调用相机对象的视图矩阵和投影矩阵值对顶点进行矩阵变换。

上节课讲解过正投影相机OrthographicCamera和透视投影相机PerspectiveCamera,其实对相机对象一些参数的设置本质上就是设置相机对象的视图矩阵.matrixWorldInverse和投影矩阵.projectionMatrix属性,因为Threejs渲染的时候会通过你设置的相机参数按照一定的算法计算出来投影和视图矩阵值,至于如何计算的,如果你想深入研究,你可以学习计算机图形学或者查看OrthographicCamera和PerspectiveCamera类封装的代码。如果你不想深入研究,只要知道相机参数会影响视图矩阵和投影矩阵就行,至于内部如何影响的可以当成黑箱。

影响视图矩阵.matrixWorldInverse计算的相关代码,无论透视投影相机还是正投影相机都一样。

camera.position.set(200, 300, 200); //设置相机位置
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)

影响投影矩阵.projectionMatrix计算相机参数的相关代码:

// 正投影相机对象
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
// 透视投影相机对象
var camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);

正投影相机OrthographicCamera自适应渲染

比如全屏渲染的时候,为了适应窗口的大小变化,要重新设置相机对象和渲染器对象的参数,不需要在渲染函数render中周期性更新,只需要通过窗口事件window.onresize来触发一个函数,然后在函数中更新相机Camera和渲染器WebGLRenderer的参数。

渲染区域变化了,要通过Three.js渲染器.setSize()方法重置渲染器渲染尺寸。

// onresize 事件会在窗口被调整大小时发生
window.onresize=function(){
  // 重置渲染器输出画布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 ();
};

在线运行案例

试着调整浏览器窗口大小,你会发现,渲染会随着窗口大小而改变,而在上一个例子中,这并不会发生。

渲染区域尺寸变化,相机的相关参数自然也需要变化,改变相机的参数后,注意需要执行相机对象.updateProjectionMatrix ()方法更新相机对象的投影矩阵.projectionMatrix,之所以需要手动更新,是因为Threejs为了提高渲染效率,Threejs系统每次执行渲染器WebGLRenderer渲染方法.rener()的时候不会读取相机相关的参数重新计算一次投影矩阵.projectionMatrix,Threejs系统只会首次渲染的时候计算一次投影矩阵,所以当你改变影响相机投影矩阵的属性,自然需要调用.updateProjectionMatrix ()更新相机对象的投影矩阵.projectionMatrix。

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

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

Three.js自适应渲染不一定就是窗口变化,本质上还是你要渲染的区域宽高尺寸变化了。

转载本站内容时,请务必注明来自W3xue,违者必究。
 友情链接: NPS