🧠 三分视界:Three.js 离屏渲染与多重视角的艺术

作者:一名曾在帧缓存深渊里摸爬滚打的像素炼金术士


🍿 序章:主屏之外,另有洞天

在 Three.js 的世界里,渲染器 是那位负责把 3D 幻象灌注到你眼球里的魔术师。而默认的 WebGLRenderer 每次都是忠实地将图像渲染到屏幕的画布上,像一位守旧的戏剧演员,总演同一出好戏。

但如果我告诉你------我们可以偷天换日,偷偷把场景渲染到一块看不见的布上(也就是 离屏渲染),然后再像魔术贴画一样,把这块布拼接出更绚烂的视觉宇宙呢?

是的,这一切,都要从 WebGLRenderTarget 说起。


🎨 第一幕:RenderTarget 的神秘身世

在 WebGL 中,每一帧的绘制都会写入一个叫做 帧缓冲(Framebuffer) 的魔法盒。而 RenderTarget 就是我们在 Three.js 中对帧缓冲的一次"高雅封装"。

php 复制代码
const renderTarget = new THREE.WebGLRenderTarget(
  window.innerWidth,
  window.innerHeight,
  {
    minFilter: THREE.LinearFilter,
    magFilter: THREE.LinearFilter,
    format: THREE.RGBAFormat,
  }
);

这段代码创建了一个虚拟的画布,一块隐形的画卷,所有你渲染进去的图像都会被画在这里,而非屏幕上。我们可以:

  • 用它绘制阴影贴图
  • 做环境反射
  • 实现后处理(后期调色、模糊、辉光特效等)
  • 进行多 Pass 渲染(好戏还在后头)

🧭 第二幕:离屏渲染的三板斧

🪞1. 多重视角(多相机)

想象你在拍一部 3D 电影,一台相机拍主视角,另一台拍反光镜中的你。这该怎么实现?很简单,我们给每个相机准备一个自己的渲染目标。

ini 复制代码
const camera1 = new THREE.PerspectiveCamera();
const camera2 = new THREE.PerspectiveCamera();

const rt1 = new THREE.WebGLRenderTarget(w, h);
const rt2 = new THREE.WebGLRenderTarget(w, h);

renderer.setRenderTarget(rt1);
renderer.render(scene, camera1);

renderer.setRenderTarget(rt2);
renderer.render(scene, camera2);

// 回到主屏幕
renderer.setRenderTarget(null);

此时,你手里就拥有了两个不同角度的世界快照,可以把它们贴在场景的任何材质上,实现"上帝视角"、"镜中世界"、"后视镜"等视效。


🔮2. 多 Pass 渲染(层层递进的炼金术)

单次渲染太寡淡?那就分多个 Pass,让每一步都变得魔性:

第一步:渲染基础色(Albedo)

ini 复制代码
renderer.setRenderTarget(rtColor);
renderer.render(colorScene, camera);

第二步:计算光照或边缘高亮

ini 复制代码
renderer.setRenderTarget(rtLighting);
renderer.render(lightingScene, camera);

第三步:将它们合并输出

最终我们使用一个屏幕空间的正方形(THREE.PlaneGeometry)+ 自定义 Shader,将之前的结果混合输出:

ini 复制代码
fullScreenQuad.material = new THREE.ShaderMaterial({
  uniforms: {
    tColor: { value: rtColor.texture },
    tLighting: { value: rtLighting.texture },
  },
  vertexShader: `...`,
  fragmentShader: `
    uniform sampler2D tColor;
    uniform sampler2D tLighting;
    void main() {
      vec4 base = texture2D(tColor, gl_FragCoord.xy / resolution);
      vec4 light = texture2D(tLighting, gl_FragCoord.xy / resolution);
      gl_FragColor = base + light; // 简单叠加
    }
  `,
});
renderer.setRenderTarget(null);
renderer.render(screenScene, camera);

🎬 这样你就拥有了一个图层化的渲染流程,每一个 Pass 都是一次视觉炼金,可以做模糊、辉光、色彩分离、描边、GBuffer 等高级效果。


🧪3. 使用多个 RenderTarget 同时输出多张贴图(MRT)

需要 GBuffer 吗?需要一次性输出法线图、深度图和颜色图?启用 MRT(Multiple Render Targets) 模式!

⚠️ 前提是浏览器支持 WEBGL_draw_buffers 扩展(大多数支持)

ini 复制代码
const rt = new THREE.WebGLMultipleRenderTargets(w, h, 3);
rt.texture[0].name = 'Color';
rt.texture[1].name = 'Normal';
rt.texture[2].name = 'Position';

// 在 Shader 中使用 gl_FragData[i] 输出不同纹理

你就可以一次性绘制出三张纹理贴图,然后再做你想做的后处理。


📦 第三幕:自定义渲染器?还是说自定义管线?

虽然 WebGLRenderer 已经做得很不错,但如果你想拥有更细节的控制(比如完全手动控制渲染顺序),可以考虑:

  • 写一个渲染调度器(Render Graph)
  • 使用低阶 WebGL API 实现更底层的渲染逻辑
  • 或者......直接改写 Three.js 的 WebGLRenderer.prototype.render 方法(魔法慎用)

例如手动执行一个完整的多 Pass 渲染:

scss 复制代码
function render() {
  renderer.setRenderTarget(rt1);
  renderer.clear();
  renderer.render(scene1, camera1);

  renderer.setRenderTarget(rt2);
  renderer.clear();
  renderer.render(scene2, camera2);

  // 最后合成
  renderer.setRenderTarget(null);
  renderer.render(finalScene, finalCamera);
}

这就像你成为了整场电影的导演,不再只是观众。


🧠 附加彩蛋:RenderTarget 的常见用途清单

用途 描述
后期特效 景深、模糊、泛光、色差
多视角合成 安全监控、上帝视角
光照贴图 动态阴影、烘焙反射
GBuffer 延迟渲染准备
镜面反射 反射探头、玻璃球反射
缓存复杂渲染 节省 GPU 性能

🧙 尾声:像素炼金术士的嘱咐

每一次 renderTarget 的使用,都是对 GPU 空间与显存的挑战;每一次多 Pass 的渲染,都是性能与画质的取舍。因此请你:

  • 记得释放不再使用的 RenderTarget(.dispose()
  • 理性使用多 Pass,避免过度渲染
  • 为你的离屏宇宙起一个浪漫的名字 🌌

📚 推荐延伸阅读


愿你在虚拟画布中,绘出万千真实。

------像素的仆人,光的使者,Three.js 开发者 💡

相关推荐
paopaokaka_luck25 分钟前
基于SpringBoot+Uniapp的健身饮食小程序(协同过滤算法、地图组件)
前端·javascript·vue.js·spring boot·后端·小程序·uni-app
患得患失9491 小时前
【前端】【vscode】【.vscode/settings.json】为单个项目配置自动格式化和开发环境
前端·vscode·json
飛_1 小时前
解决VSCode无法加载Json架构问题
java·服务器·前端
YGY Webgis糕手之路4 小时前
OpenLayers 综合案例-轨迹回放
前端·经验分享·笔记·vue·web
90后的晨仔4 小时前
🚨XSS 攻击全解:什么是跨站脚本攻击?前端如何防御?
前端·vue.js
Ares-Wang4 小时前
JavaScript》》JS》 Var、Let、Const 大总结
开发语言·前端·javascript
90后的晨仔4 小时前
Vue 模板语法完全指南:从插值表达式到动态指令,彻底搞懂 Vue 模板语言
前端·vue.js
德育处主任4 小时前
p5.js 正方形square的基础用法
前端·数据可视化·canvas
烛阴4 小时前
Mix - Bilinear Interpolation
前端·webgl
90后的晨仔4 小时前
Vue 3 应用实例详解:从 createApp 到 mount,你真正掌握了吗?
前端·vue.js