【three.js教程】# Three.js 渲染目标(Render Targets):把场景画到一张“会动的贴图“上 Three.js 渲染目标Render Targets把场景画到一张会动的贴图上原文出处Three.js Manual – Render Targets本文基于官方手册 Render Targets 章节整理用最少的代码带你看懂怎么把三维场景先画进一张纹理再贴到别的地方。写在前面你可能会想three.js 不就是把场景画到 canvas 上吗这话说对了一半。其实渲染器每帧干的活是把 Scene Camera 画到某个目标上——这个目标默认就是屏幕canvas。但 three.js 允许你把这个目标换成一个离屏的纹理先把场景画进纹理里再拿这个纹理去当普通贴图用。这个可以渲染进去的纹理就叫Render Target渲染目标。它能干嘛你之前学的阴影Shadows、鼠标拾取Picking、各种后期处理Post Processing底层全靠它。游戏里车子的后视镜、监控屏幕里的实时画面也是它。这篇我们用最直白的方式把一个小场景画进 512×512 的渲染目标再把这张会动的图贴到一个立方体上。一、创建渲染目标和普通渲染一模一样只是第一步多创建一个WebGLRenderTargetconstrtWidth512;constrtHeight512;constrenderTargetnewTHREE.WebGLRenderTarget(rtWidth,rtHeight);就这么一行你手里就有了一张 512×512、可以往里画画的纹理。二、给渲染目标单独配一套场景和相机渲染目标是独立的小世界所以它要有自己的 Scene 和 CameraconstrtFov75;constrtAspectrtWidth/rtHeight;// 注意用渲染目标的宽高比不是 canvas 的constrtNear0.1;constrtFar5;constrtCameranewTHREE.PerspectiveCamera(rtFov,rtAspect,rtNear,rtFar);rtCamera.position.z2;constrtScenenewTHREE.Scene();rtScene.backgroundnewTHREE.Color(red);⚠️划重点相机的aspect要用渲染目标的宽高比。这里我们打算把渲染目标的纹理贴到立方体的侧面立方体的面是正方形的所以 aspect 设成 1.0正方形。用多大取决于你要拿它来干啥。然后往rtScene里塞点东西这里复用之前文章的灯光 三个彩色立方体{constcolor0xFFFFFF;constintensity1;constlightnewTHREE.DirectionalLight(color,intensity);light.position.set(-1,2,4);rtScene.add(light);}constboxWidth1,boxHeight1,boxDepth1;constgeometrynewTHREE.BoxGeometry(boxWidth,boxHeight,boxDepth);functionmakeInstance(geometry,color,x){constmaterialnewTHREE.MeshPhongMaterial({color});constcubenewTHREE.Mesh(geometry,material);rtScene.add(cube);cube.position.xx;returncube;}constrtCubes[makeInstance(geometry,0x44aa88,0),makeInstance(geometry,0x8844aa,-2),makeInstance(geometry,0xaa8844,2),];三、把渲染目标的纹理贴到物体上主场景scene和主相机camera都还在。我们往主场景加一个立方体让它用渲染目标的纹理constmaterialnewTHREE.MeshPhongMaterial({map:renderTarget.texture,});constcubenewTHREE.Mesh(geometry,material);scene.add(cube);注意renderTarget.texture就是那张画进去的图。它和普通TextureLoader加载出来的纹理用法完全一样。四、渲染循环里的关键两步这是整篇最核心的地方。每一帧你要先渲染到渲染目标再渲染到屏幕functionrender(time){time*0.001;// 1) 先让渲染目标场景里的立方体转起来rtCubes.forEach((cube,ndx){constspeed1ndx*0.1;constrottime*speed;cube.rotation.xrot;cube.rotation.yrot;});// 2) 把渲染目标场景画进渲染目标renderer.setRenderTarget(renderTarget);// 告诉渲染器接下来画到 renderTarget 里renderer.render(rtScene,rtCamera);renderer.setRenderTarget(null);// 画完切回屏幕// 3) 再让主场景里的立方体转起来并渲染到 canvascube.rotation.xtime;cube.rotation.ytime*1.1;renderer.render(scene,camera);}看明白了吗setRenderTarget(renderTarget)把渲染目标设成画布render(rtScene, rtCamera)把小世界画进去然后setRenderTarget(null)切回真正的屏幕再render(scene, camera)出最终画面。因为rtScene的背景是红色所以这张纹理底色是红的——你看到的立方体也就带着一片红底里转着的三个小方块。五、几个要知道的细节1渲染目标默认会创建两张纹理一张颜色纹理就是renderTarget.texture一张深度/模板纹理。如果你用不到深度或模板可以显式关掉省内存constrtnewTHREE.WebGLRenderTarget(width,height,{depthBuffer:false,stencilBuffer:false,});2大小要跟着变上面我们固定做了 512×512。但像后期处理这种场景渲染目标得和 canvas 一样大。所以窗口尺寸变化时要同时更新渲染目标大小和它的相机functionrender(time){time*0.001;if(resizeRendererToDisplaySize(renderer)){constcanvasrenderer.domElement;camera.aspectcanvas.clientWidth/canvas.clientHeight;camera.updateProjectionMatrix();renderTarget.setSize(canvas.width,canvas.height);// 渲染目标跟着变rtCamera.aspectcamera.aspect;// 它的相机也跟着变rtCamera.updateProjectionMatrix();}// ...后面照旧}总结概念一句话说明Render Target一张可以往里渲染的离屏纹理使用套路setRenderTarget(rt)→render(rtScene, rtCamera)→setRenderTarget(null)→render(scene, camera)相机 aspect用渲染目标的宽高比别用 canvas 的默认纹理数颜色 深度/模板不需要可关典型用途阴影、拾取、后期处理、后视镜、监控画面什么时候用只要你想要把一个三维画面当成贴图或者需要离屏多画一遍场景就该想到渲染目标。延伸阅读WebGLRenderTarget APIShadows阴影底层用渲染目标Post Processing后期处理