【three.js教程】相机:透视与正交,决定你“怎么看“这个世界
2026/9/12 2:40:17 网站建设 项目流程

Three.js 相机:透视与正交,决定你"怎么看"这个世界

原文出处:Three.js Manual – Cameras
本文基于官方手册 Cameras 章节整理,用通俗方式讲清 three.js 里两种相机(透视 / 正交)到底是什么、near/far 为什么不能乱设。

写在前面

前面我们学会了给场景打光,模型有立体感了。但还有一个绕不开的东西——相机。它决定了"观众的眼睛"在哪、能看到多大范围。

three.js 里相机我们其实一直在用(PerspectiveCamera),但之前的文章没细讲。这篇把两种相机掰开揉碎说清楚,并顺带解决一个新手必踩的坑:near 和 far 为什么不能设成 0 和无穷大

一、PerspectiveCamera:透视相机,越近越大

这是 three.js 最常用的相机,也是现实人眼的投影方式——远处的东西看起来小,近处的大。

它靠四个参数定义一个"平截头体"(frustum,一个被削掉尖的金字塔)。nearfar是前后两个截面的距离,fov(field of view,视场角)决定这个金字塔有多"宽",aspect是宽高比。

参数含义常见值
fov垂直视场角(度)45~75
aspect画面宽高比canvas 宽/高
near近裁剪面距离0.1
far远裁剪面距离100~1000
constfov=45;constaspect=2;constnear=0.1;constfar=100;constcamera=newTHREE.PerspectiveCamera(fov,aspect,near,far);

改了任何参数都要调updateProjectionMatrix(),否则画面不会更新。手册用一个小工具类MinMaxGUIHelper保证far永远大于near,很贴心:

functionupdateCamera(){camera.updateProjectionMatrix();}constgui=newGUI();gui.add(camera,'fov',1,180).onChange(updateCamera);constminMaxGUIHelper=newMinMaxGUIHelper(camera,'near','far',0.1);gui.add(minMaxGUIHelper,'min',0.1,50,0.1).name('near').onChange(updateCamera);gui.add(minMaxGUIHelper,'max',0.1,50,0.1).name('far').onChange(updateCamera);

二、直观看清"平截头体":双相机 + 剪刀测试

光看参数很难想象。手册给了一个超直观的 demo:用 three.js 的**剪刀测试(scissor)**把画布切成两半,左边是正常视角,右边是"从第二个相机看第一个相机的平截头体"。

核心是把renderer.setScissorTest(true),再分别给左右两块区域设置视口和剪刀框:

functionsetScissorForElement(elem){constcanvasRect=canvas.getBoundingClientRect();constelemRect=elem.getBoundingClientRect();constright=Math.min(elemRect.right,canvasRect.right)-canvasRect.left;constleft=Math.max(0,elemRect.left-canvasRect.left);constbottom=Math.min(elemRect.bottom,canvasRect.bottom)-canvasRect.top;consttop=Math.max(0,elemRect.top-canvasRect.top);constwidth=Math.min(canvasRect.width,right-left);constheight=Math.min(canvasRect.height,bottom-top);constpositiveYUpBottom=canvasRect.height-bottom;renderer.setScissor(left,positiveYUpBottom,width,height);renderer.setViewport(left,positiveYUpBottom,width,height);returnwidth/height;}

near调大到 20,你会看到物体"前半截"突然消失——因为它们跑出了平截头体;把far调小到 35 以下,地面开始消失。一下子就懂了 near/far 是干嘛的。

三、near/far 精度与 Z-fighting(深度冲突)

你可能会想:那我把near设成 0.0000001、far设成 100000000000 不就啥都能看了?

不行,这就是新手经典大坑。GPU 用来判断"谁在前谁在后"的精度(深度缓冲)是有限的,而且默认在相机附近精度高、远处精度低。这个精度要在nearfar之间分配。两者差距拉太大,中间物体的前后关系就判断不准,出现Z-fighting(深度闪烁)——两个物体叠在一起时边缘疯狂抖动、互相穿模。

手册用 20 个彩色球演示了这个现象。一个缓解办法是创建 renderer 时开logarithmicDepthBuffer

constrenderer=newTHREE.WebGLRenderer({antialias:true,canvas,logarithmicDepthBuffer:true,});

但别迷信它:第一,很多移动端 GPU 不支持;第二,它比默认方案慢。最根本的解法还是——near 尽量远、far 尽量近,刚好框住你要显示的内容就行

四、OrthographicCamera:正交相机,没有近大远小

第二种常用相机是OrthographicCamera(正交相机)。它不定义金字塔,而是定义一个长方体盒子,参数是left/right/top/bottom/near/far。因为是正交投影,没有透视变形——物体多大就是多大,不受距离影响。

constleft=-1;constright=1;consttop=1;constbottom=-1;constnear=5;constfar=50;constcamera=newTHREE.OrthographicCamera(left,right,top,bottom,near,far);camera.zoom=0.2;

它最常见的用途是画 2D。你想让"画布上 1 像素 = 相机里 1 单位"?直接把盒子设成画布尺寸:

camera.left=0;camera.right=canvas.width;camera.top=0;camera.bottom=canvas.height;camera.near=-1;camera.far=1;camera.zoom=1;

这样左上角就是(0,0),跟普通 2D canvas 的坐标系一模一样。手册还用正交相机做了个"图片在画布里像素级反弹"的小动画,跟写 2D 游戏一模一样。另一个经典用途是 3D 建模软件里的"顶/前/左/右"三视图。

gui.add(camera,'zoom',0.01,1,0.01).listen();// 加 .listen() 让滚轮缩放也同步到 GUI

总结

相机投影方式有透视吗典型用途
PerspectiveCamera平截头体绝大多数 3D 场景
OrthographicCamera长方体盒子2D 绘制、三视图

记住三条:① 改相机参数后调updateProjectionMatrix();② near/far 别拉太极端,小心 Z-fighting;③ 要做 2D 或工程三视图就用正交相机。

延伸阅读

  • PerspectiveCamera – docs
  • OrthographicCamera – docs
  • CameraHelper – docs
  • 下一篇可以看 Shadows(阴影)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询