Three.js学习6:透视相机和正交相机

一、相机

相机 camera,可以理解为摄像机。在拍影视剧的时候,最终用户看到的画面都是相机拍出来的内容。

Three.js 里,相机 camera 里的内容就是用户能看到的内容。从这个角度来看,相机其实就是用户的视野,就像用户的眼睛

Three.js 主要有四种不同的相机模式:

  • 透视相机 PerspectiveCamera:具有透视效果,近大远小,也是用的最多的相机。

  • 正交相机 OrthographicCamera:不具有透视效果,所有的元素的尺寸大小都是相同的,不管距离。

  • 立体相机 StereoCamera:主要做VR用的。就是让左右视觉有点点不一样的相机。

  • 立方体相机 CubeCamera:主要用作反射镜面纹理。

本文主要讨论透视相机和正交相机。

前面案例中应用到的轨道控制器,名为轨道,其实控制的就是相机的视角。

二、透视相机

透视相机(PerspectiveCamera)中的物体具有"近大远小"的特点,是3D场景的渲染中使用得最普遍的投影模式。

1. 参数解析

javascript 复制代码
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far );

其参数分别为:

  1. fov :摄像机视锥体垂直视野角度 ,以角度来表示。默认值是50

  2. aspect :摄像机视锥体宽高比 :默认值为 1 ,一般用渲染器到宽高比

  3. near:摄像机视锥体近截面 ,默认值是0.1

  4. 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 = "正交相机";
});
相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习