基于全屏 Quad 的 Three.js 后处理全解析

一、问题背景:

最近在使用three渲染点云的时候遇到一个问题,当我缩放相机到离得很远的时候,这时调整不透明度,就会变得很白很亮,开始我以为可以通过调整设置来解决,就测试深度写入、测试、混合模式之间的各种组合,但是效果都不理想。后面借鉴已有的edl渲染,终于解决这个问题了。

二、后处理核心原理:全屏 Quad 的妙用

在 Three.js 中,后处理的本质是对渲染结果进行二次加工。而 "全屏 Quad"(全屏平面)是实现这一过程的核心载体,其原理可概括为:

  1. 第一步:渲染场景到缓冲区
    先将 3D 场景渲染到一个离屏缓冲区(WebGLRenderTarget),得到包含颜色、深度信息的纹理。

  2. 第二步:用全屏 Quad 加工纹理
    创建一个铺满屏幕的平面(2x2 大小,刚好覆盖标准化设备坐标 NDC),通过自定义着色器材质对第一步得到的纹理进行处理,最终将结果渲染到屏幕。

  3. 核心工具:Utils.screenPass
    具体来说就是封装一个全屏 Quad、场景和相机,在得到纹理图之后,通过把纹理的深度、颜色等信息发送给自定义着色器来二次加工并输出至屏幕。其核心逻辑是通过render方法动态替换材质,实现不同效果的快速切换。

    实现步骤:

  • 定义着色器材质

    javascript

    ini 复制代码
    const invertMaterial = new THREE.ShaderMaterial({
      uniforms: {
        uSceneTexture: { value: null } // 接收场景纹理
      },
      vertexShader: `
        varying vec2 vUv;
        void main() {
          vUv = uv; // 传递纹理坐标
          gl_Position = vec4(position, 1.0); // 全屏Quad坐标
        }
      `,
      fragmentShader: `
        uniform sampler2D uSceneTexture;
        varying vec2 vUv;
        void main() {
          vec4 color = texture2D(uSceneTexture, vUv);
          gl_FragColor = vec4(1.0 - color.rgb, color.a); // 反色计算
        }
      `
    });
  • 使用screenPass渲染

javascript

ini 复制代码
// 1. 创建渲染目标,用于存储场景原始渲染结果
const renderTarget = new THREE.WebGLRenderTarget(
  window.innerWidth,
  window.innerHeight
);

// 2. 先将场景渲染到目标缓冲区
renderer.render(scene, camera, renderTarget);

// 3. 传入反色材质,用screenPass渲染到屏幕
invertMaterial.uniforms.uSceneTexture.value = renderTarget.texture;
Utils.screenPass.render(renderer, invertMaterial);
  • screenPass函数实现

javascript

kotlin 复制代码
const screenPass = new function () {
	// @ts-expect-error
	this.screenScene = new THREE.Scene();
	// @ts-expect-error
	this.screenQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2, 1));
	// @ts-expect-error
	this.screenQuad.material.depthTest = true;
	// @ts-expect-error
	this.screenQuad.material.depthWrite = true;
	// @ts-expect-error
	this.screenQuad.material.transparent = true;
	// @ts-expect-error
	this.screenScene.add(this.screenQuad);
	// @ts-expect-error
	this.camera = new THREE.Camera();
	// @ts-expect-error
	this.render = function (renderer, material, target) {
		this.screenQuad.material = material;

		if (typeof target === 'undefined') {
			renderer.render(this.screenScene, this.camera);
		} else {
			renderer.render(this.screenScene, this.camera, target);
		}
	};
}();

三、总结:

上面就是基于全屏 Quad 的全流程了,也算是后处理框架中最灵活、最易扩展的方案。通过Quad自定义着色器我们可以快速实现从简单颜色调整到复杂辉光、边缘检测的各种效果。核心要点在于:

  • 理解 "渲染→加工→再渲染" 的后处理流水线;

  • 掌握着色器中纹理采样和像素操作的技巧;

  • 学会组合多个后处理步骤实现复杂效果。

希望本文能帮助你打开后处理的大门,让你的 3D 场景焕发新的视觉活力!

相关推荐
XiaoYu20023 天前
第9章 Three.js载入模型GLTF
前端·javascript·three.js
XiaoYu20024 天前
第8章 Three.js入门
前端·javascript·three.js
AlanHou13 天前
Three.js:Web 最重要的 3D 渲染引擎的技术综述
前端·webgl·three.js
一颗烂土豆16 天前
🚴‍♂️ Vue3 + Three.js 实战:如何写一个“不晕车”的沉浸式骑行播放器 🎥
vue.js·游戏·three.js
Elaine33617 天前
Gemini生成的3D交互圣诞树(娱乐版)
3d·交互·three.js·前端可视化
Awu122718 天前
Vue3自定义渲染器:原理剖析与实践指南
前端·vue.js·three.js
龙猫不热18 天前
THREE.js 关于Material基类下的depthTest 和 depthWrite的理解
前端·three.js
阿里巴啦22 天前
用React+Three.js 做 3D Web版搭建三维交互场景:模型的可视化摆放与轻量交互
前端·react·three.js·模型可视化·web三维·web三维交互场景
阿里巴啦23 天前
React + Three.js + R3F + Vite 实战:可交互的三维粒子化展厅
react.js·three.js·粒子化·drei·postprocessing·三维粒子化
叫我詹躲躲24 天前
基于 Three.js 的 3D 地图可视化:核心原理与实现步骤
前端·three.js