一、相机
相机 camera,可以理解为摄像机。在拍影视剧的时候,最终用户看到的画面都是相机拍出来的内容。
Three.js 里,相机 camera 里的内容就是用户能看到的内容。从这个角度来看,相机其实就是用户的视野,就像用户的眼睛。
Three.js 主要有四种不同的相机模式:
-
透视相机 PerspectiveCamera:具有透视效果,近大远小,也是用的最多的相机。
-
正交相机 OrthographicCamera:不具有透视效果,所有的元素的尺寸大小都是相同的,不管距离。
-
立体相机 StereoCamera:主要做VR用的。就是让左右视觉有点点不一样的相机。
-
立方体相机 CubeCamera:主要用作反射镜面纹理。
本文主要讨论透视相机和正交相机。
前面案例中应用到的轨道控制器,名为轨道,其实控制的就是相机的视角。
二、透视相机
透视相机(PerspectiveCamera)中的物体具有"近大远小"的特点,是3D场景的渲染中使用得最普遍的投影模式。
1. 参数解析
javascript
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far );
其参数分别为:
-
fov :摄像机视锥体垂直视野角度 ,以角度来表示。默认值是50。
-
aspect :摄像机视锥体宽高比 :默认值为 1 ,一般用渲染器到宽高比。
-
near:摄像机视锥体近截面 ,默认值是0.1。
-
far:摄像机视锥体远截面 ,默认值是2000。
javascript
const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
scene.add( camera );
只有在视锥体范围内,近截面和远截面之间的物体才会被渲染出来。
Three.js 编辑器里视锥体的样子:
可以通过点语法的形式修改相机视锥体的参数,但是必须调用camera.updateProjectionMatrix()
方法,让修改生效。
javascript
const camera = new THREE.PerspectiveCamera(50, winW/winH,1, 10);
camera.near = 5; // 修改参数
camera.far = 37;
camera.updateProjectionMatrix(); // 让参数修改生效
2. 修改相机坐标
因为,相机默认在原点上,物体也默认在原点上。所以,要看到物品,要把相机往后挪动位置,也既修改 z 轴位置。
javascript
// 调整相机位置 x,y,z
camera.position.z = 0;
camera.position.x = 0;
camera.position.y = 10;
// 或者
camera.position.set(0,0,10);
3. 镜头对准物体
不过有时候,移动相机的时候,相机必须要盯着目标物体,避免物体跑出视野之外。
javascript
camera.lookAt( Xmesh.position ); // 相机镜头盯着x物体
也可以对准某个具体的位置:
javascript
camera.lookAt(new THREE.Vector3(x,y,z));
4. 缩放镜头
javascript
camera.zoom = 4;
camera.updateProjectionMatrix(); // 让参数修改生效
获取或者设置摄像机的缩放倍数,其默认值为1。
-
>1
:镜头里的物体会被放大 -
<1
:物体会被缩小 -
=1
:物体正常大小
必须调用camera.updateProjectionMatrix()
方法,让修改生效。
三、正交相机
正交相机(OrthographicCamera),无论物体距离相机距离远或者近,在最终渲染物体的大小都保持不变。
主要用于渲染 2D 场景 或者UI元素。如下图所示:
1. 参数解析
javascript
const camera = new THREE.OrthographicCamera(OrthographicCamera( left, right, top, bottom, near, far);
scene.add( camera );
其参数依次分别为:
-
left : 摄像机视锥体左侧面。
-
right : 摄像机视锥体右侧面。left 与 right 互为反数。
-
top:摄像机视锥体上侧面。
-
bottom: 摄像机视锥体下侧面。top 与 bottom 互为反数。
-
near : 摄像机视锥体近截面 。其默认值为0.1。
-
far: 摄像机视锥体远截面 。其默认值为2000。
这几个参数刚好组成一个立方体。
例如:
javascript
const k = winW / winH; //canvas画布宽高比
const s = 2; // 显示控制系数。
const camera = new THREE.OrthographicCamera( -s*k, k*s, s, -s, 0.1, 2000 );
camera.position.set(8,8,8);
scene.add( camera );
为了保持照相机的横竖比例,需要保证 (right - left)
与 (top - bottom)
的比例与 Canvas(也就是渲染器)宽度与高度的比例一致。所以才有了两个变量 k、s。
-
变量 k:render 渲染的宽高比。因为,正交相机默认渲染的是一个正方形,但是我们渲染的范围(canvas)不一定是一个正方形。正交相机区域将被拉伸以适合我们的矩形画布,因此我们需要使用画布的宽高比。
-
变量 S 是正交相机显示控制系数。值越小,画面越大。反之,画面越小。如果为1,画面会很大。所以,这里用"单位"的2倍。当然,也可以根据需要自行调整数据。
Three.js 编辑器里正交相机视锥体的样子:
跟透视相机一样,可以通过点语法的形式修改相机视锥体的参数,但是必须调用camera.updateProjectionMatrix()
方法,让修改生效。
2. 正交相机例子
javascript
let winW = window.innerWidth;
let winH = window.innerHeight;
// 场景
const scene = new THREE.Scene();
scene.background = new THREE.Color("#cccccc");
// 网格辅助器
const grid = new THREE.GridHelper(1000,10);
scene.add(grid);
// 物体
const geometry = new THREE.BoxGeometry(100,100,100);
const material = new THREE.MeshBasicMaterial({
color:"#f00"
});
const mesh = new THREE.Mesh( geometry, material );
mesh.position.set(0,0,0);
scene.add( mesh );
// 正投影相机案例
const k = winW / winH; //canvas画布宽高比
const s = 2; // 显示控制系数。网格单位*2
const camera = new THREE.OrthographicCamera( -s*k, k*s, s, -s, 0.1, 2000 );
camera.position.set(8,8,8);
scene.add( camera );
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize( winW, winH );
document.body.appendChild( renderer.domElement );
renderer.render(scene, camera);
// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
controls.addEventListener(function(){
console.info( camera.position );
});
// 动画
function aniFun(){
renderer.render( scene, camera );
controls.update();
requestAnimationFrame( aniFun );
}
aniFun();
可以看到,正交相机的图像是没有近大远小的透视感的。
四、相机切换示例
html:
html
<div class="btns">
<button type="button" id="tsBtn">透视相机</button>
<button type="button" id="zjBtn">正交相机</button>
<h1 id="tit">透视相机</h1>
</div>
<script type="importmap">
{
"imports":{
"three":"./js/three.module.min.js",
"three/addons/":"./js/jsm/"
}
}
</script>
<script type="module" src="./js/myjs5-3.js"></script>
JS:
javascript
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
let scene,
camera,
controls, grid,
renderer;
let winW = window.innerWidth,
winH = window.innerHeight;
let tsBtn = document.getElementById("tsBtn"),
zjBtn = document.getElementById("zjBtn"),
tit = document.getElementById("tit");
// 场景
scene = new THREE.Scene();
scene.background = new THREE.Color("#cccccc");
// 透视相机
function pCamera(){
camera = new THREE.PerspectiveCamera(50, winW/winH,1, 1000);
camera.position.set( 8,8,8 );
camera.lookAt( scene.position );
scene.add( camera );
}
// 正交相机
function oCamera(){
const k = winW / winH; //canvas画布宽高比
const s = 8; // 显示控制系数。
camera = new THREE.OrthographicCamera( -s*k, k*s, s, -s, 0.1, 2000 );
camera.position.set(8,8,8);
camera.lookAt( scene.position );
scene.add( camera );
}
// 渲染器
function renderFun(){
renderer = new THREE.WebGLRenderer();
renderer.setSize( winW, winH );
document.body.appendChild( renderer.domElement );
}
// 网格辅助
function gridHelperFun(){
grid = new THREE.GridHelper(10,10);
scene.add(grid);
}
// 立方体
function cubeFun(){
let geometry = new THREE.BoxGeometry(1,1,1);
let metiral = new THREE.MeshBasicMaterial({
color:"#ff3300"
});
let mesh = new THREE.Mesh( geometry, metiral);
scene.add( mesh );
}
// 函数调用
pCamera();
renderFun();
gridHelperFun();
cubeFun();
renderer.render(scene, camera);
// 动画渲染
function animateFun(){
// 渲染
renderer.render( scene, camera);
requestAnimationFrame(animateFun);
}
animateFun();
// 按钮事件
tsBtn.addEventListener("click",function(){
pCamera();
renderer.render(scene, camera);
tit.innerHTML = "透视相机";
});
zjBtn.addEventListener("click",function(){
oCamera();
renderer.render(scene, camera);
tit.innerHTML = "正交相机";
});