基于全屏 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 场景焕发新的视觉活力!

相关推荐
郝学胜-神的一滴3 天前
[简化版 GAMES 101] 计算机图形学 12:可见性与 Z‑Buffer 深度缓存
unity·godot·图形渲染·three.js·opengl·unreal
VcB之殇4 天前
[Three.js] 实现两个3D模型之间的粒子化切换
前端·javascript·three.js
郝学胜-神的一滴7 天前
中级OpenGL教程 008:精准控制高光光斑大小与强度
c++·unity·godot·three.js·图形学·opengl·unreal
xier12345610 天前
three-instance-batch 开发笔记
javascript·three.js
一根数据线12 天前
从几何压缩到KTX2纹理压缩:轻装3D的Three.js场景优化进阶
3d模型轻量化·three.js·3d模型·ktx2·轻装3d·纹理压缩
一根数据线13 天前
一键解决ThreeJS3D场景卡顿问题!轻装3D的几何体实例化与合并
3d模型轻量化·three.js·3d模型·轻装3d·实例化渲染·几何体合并
一根数据线14 天前
ThreeJS模型加载卡顿怎么办,用轻装3D来做模型压缩和LOD分级
3d模型轻量化·three.js·lod·3d模型优化·draco压缩·轻装3d
来自上海的这位朋友14 天前
用 Three.js 做一个 Web 3D 非对称追猎 Demo:从场景、角色到手感调试
后端·游戏开发·three.js
来自上海的这位朋友14 天前
Spring Boot + MySQL 搭一个多人游戏后端:登录、房间、匹配、对局和成长系统
前端·后端·three.js
郝学胜-神的一滴14 天前
中级OpenGL教程 007:解决背面光照异常高光问题
c++·unity·游戏引擎·three.js·opengl·unreal