
Three.js 相机透视与正交决定你怎么看这个世界原文出处Three.js Manual – Cameras本文基于官方手册 Cameras 章节整理用通俗方式讲清 three.js 里两种相机透视 / 正交到底是什么、near/far 为什么不能乱设。写在前面前面我们学会了给场景打光模型有立体感了。但还有一个绕不开的东西——相机。它决定了观众的眼睛在哪、能看到多大范围。three.js 里相机我们其实一直在用PerspectiveCamera但之前的文章没细讲。这篇把两种相机掰开揉碎说清楚并顺带解决一个新手必踩的坑near 和 far 为什么不能设成 0 和无穷大。一、PerspectiveCamera透视相机越近越大这是 three.js 最常用的相机也是现实人眼的投影方式——远处的东西看起来小近处的大。它靠四个参数定义一个平截头体frustum一个被削掉尖的金字塔。near和far是前后两个截面的距离fovfield of view视场角决定这个金字塔有多宽aspect是宽高比。参数含义常见值fov垂直视场角度45~75aspect画面宽高比canvas 宽/高near近裁剪面距离0.1far远裁剪面距离100~1000constfov45;constaspect2;constnear0.1;constfar100;constcameranewTHREE.PerspectiveCamera(fov,aspect,near,far);改了任何参数都要调updateProjectionMatrix()否则画面不会更新。手册用一个小工具类MinMaxGUIHelper保证far永远大于near很贴心functionupdateCamera(){camera.updateProjectionMatrix();}constguinewGUI();gui.add(camera,fov,1,180).onChange(updateCamera);constminMaxGUIHelpernewMinMaxGUIHelper(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){constcanvasRectcanvas.getBoundingClientRect();constelemRectelem.getBoundingClientRect();constrightMath.min(elemRect.right,canvasRect.right)-canvasRect.left;constleftMath.max(0,elemRect.left-canvasRect.left);constbottomMath.min(elemRect.bottom,canvasRect.bottom)-canvasRect.top;consttopMath.max(0,elemRect.top-canvasRect.top);constwidthMath.min(canvasRect.width,right-left);constheightMath.min(canvasRect.height,bottom-top);constpositiveYUpBottomcanvasRect.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 用来判断谁在前谁在后的精度深度缓冲是有限的而且默认在相机附近精度高、远处精度低。这个精度要在near到far之间分配。两者差距拉太大中间物体的前后关系就判断不准出现Z-fighting深度闪烁——两个物体叠在一起时边缘疯狂抖动、互相穿模。手册用 20 个彩色球演示了这个现象。一个缓解办法是创建 renderer 时开logarithmicDepthBufferconstrenderernewTHREE.WebGLRenderer({antialias:true,canvas,logarithmicDepthBuffer:true,});但别迷信它第一很多移动端 GPU 不支持第二它比默认方案慢。最根本的解法还是——near 尽量远、far 尽量近刚好框住你要显示的内容就行。四、OrthographicCamera正交相机没有近大远小第二种常用相机是OrthographicCamera正交相机。它不定义金字塔而是定义一个长方体盒子参数是left/right/top/bottom/near/far。因为是正交投影没有透视变形——物体多大就是多大不受距离影响。constleft-1;constright1;consttop1;constbottom-1;constnear5;constfar50;constcameranewTHREE.OrthographicCamera(left,right,top,bottom,near,far);camera.zoom0.2;它最常见的用途是画 2D。你想让画布上 1 像素 相机里 1 单位直接把盒子设成画布尺寸camera.left0;camera.rightcanvas.width;camera.top0;camera.bottomcanvas.height;camera.near-1;camera.far1;camera.zoom1;这样左上角就是(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 – docsOrthographicCamera – docsCameraHelper – docs下一篇可以看 Shadows阴影