You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-three-fiber场景高斯模糊后处理高效实现方案咨询

高效实现React-three-fiber高斯模糊后处理的方案

问题根源

你当前的实现性能瓶颈主要来自两点:

  • 单pass高斯模糊需要同时处理水平+垂直方向的采样,采样次数随核大小呈线性增长(比如13核需要25次纹理采样)
  • 直接对全分辨率的相机renderTarget进行模糊计算,像素处理量巨大

最优方案:使用成熟的后处理库

直接用@react-three/postprocessing库,它内置了优化好的高斯模糊效果,已经处理了双pass、降采样、纹理复用等性能优化,无需自己编写复杂的shader和renderTarget逻辑。

代码示例

import { Canvas } from '@react-three/fiber'
import { EffectComposer, GaussianBlur } from '@react-three/postprocessing'
import { OrbitControls } from '@react-three/drei'

export default function Test() {
  return (
    <div className='h-full w-full absolute'>
      <Canvas camera={{ fov: 75, near: 0.01, far: 50, position: [0, 0, 1] }}>
        <color attach="background" args={["black"]} />
        <OrbitControls />
        <Particles />
        {/* 后处理 composer,自动处理渲染管线 */}
        <EffectComposer>
          {/* 高斯模糊效果,可配置模糊半径、强度等参数 */}
          <GaussianBlur radius={3} strength={1.5} />
        </EffectComposer>
      </Canvas>
    </div>
  )
}

自定义优化实现(如果不想用库)

如果一定要自己实现shader,可通过以下优化点降低计算成本:

1. 改用双pass高斯模糊

将模糊拆分为水平方向和垂直方向两个独立的pass,每个pass只处理单一方向的采样,总采样次数减少一半。

水平模糊shader(fragment.glsl)

uniform sampler2D tDiffuse;
uniform int uKernel;
uniform float uSigma;
uniform float uStrength;
uniform float uResolution;
varying vec2 v_Uv;

float gaussianPdf(in float x, in float sigma) {
  return (.39894 / sigma) * exp(-.5 * x * x / (sigma * sigma));
}

void main() {
  float weightSum = gaussianPdf(0., uSigma);
  vec3 diffuseSum = texture2D(tDiffuse, v_Uv).rgb * weightSum;
  float invSize = 1. / uResolution;

  for(int i = 1; i < uKernel; i++) {
    float x = float(i);
    float w = gaussianPdf(x, uSigma);
    
    vec2 offset = vec2(1., 0.) * invSize * x * uStrength;
    diffuseSum += (texture2D(tDiffuse, v_Uv + offset).rgb + texture2D(tDiffuse, v_Uv - offset).rgb) * w;
    weightSum += 2. * w;
  }
  
  gl_FragColor = vec4(diffuseSum / weightSum, 1.);
}

垂直模糊shader(fragment-vertical.glsl)

仅需将offset的方向改为vec2(0., 1.)即可,其余逻辑与水平模糊一致。

2. 加入降采样

先将原渲染结果缩放到低分辨率(比如原分辨率的1/2),模糊后再放大回原分辨率,大幅减少需要处理的像素数量。

3. 使用React-three-fiber的EffectComposer管理后处理

避免手动操作renderTarget,用EffectComposer的Pass系统来组织双pass流程:

import { useThree, useEffect, useRef } from '@react-three/fiber'
import { EffectComposer, Pass, FullScreenQuad } from 'three/addons/postprocessing/EffectComposer.js'
import { ShaderMaterial } from 'three'
import horizontalFrag from './horizontal-frag.glsl'
import verticalFrag from './vertical-frag.glsl'
// 通用全屏顶点shader:vertex.glsl
// varying vec2 v_Uv;
// void main() {
//   v_Uv = uv;
//   gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);
// }
import vert from './vertex.glsl'

function BlurPass() {
  const { gl, size } = useThree()
  const composer = useRef(null)
  const horizontalPass = useRef(null)
  const verticalPass = useRef(null)

  useEffect(() => {
    // 创建后处理 composer
    composer.current = new EffectComposer(gl)
    
    // 创建水平模糊材质
    const horizontalMat = new ShaderMaterial({
      uniforms: {
        tDiffuse: { value: null },
        uKernel: { value: 13 },
        uSigma: { value: 3 },
        uStrength: { value: 1 },
        uResolution: { value: size.width / 2 } // 降采样到原宽度的1/2
      },
      vertexShader: vert,
      fragmentShader: horizontalFrag
    })
    
    // 创建垂直模糊材质
    const verticalMat = new ShaderMaterial({
      uniforms: {
        tDiffuse: { value: null },
        uKernel: { value: 13 },
        uSigma: { value: 3 },
        uStrength: { value: 1 },
        uResolution: { value: size.height / 2 } // 降采样到原高度的1/2
      },
      vertexShader: vert,
      fragmentShader: verticalFrag
    })
    
    // 创建自定义水平pass
    horizontalPass.current = new Pass()
    horizontalPass.current.render = (renderer, writeBuffer, readBuffer) => {
      horizontalMat.uniforms.tDiffuse.value = readBuffer.texture
      horizontalPass.current.fsquad = new FullScreenQuad(horizontalMat)
      horizontalPass.current.fsquad.render(renderer, writeBuffer)
    }
    
    // 创建自定义垂直pass
    verticalPass.current = new Pass()
    verticalPass.current.render = (renderer, writeBuffer, readBuffer) => {
      verticalMat.uniforms.tDiffuse.value = readBuffer.texture
      verticalPass.current.fsquad = new FullScreenQuad(verticalMat)
      verticalPass.current.fsquad.render(renderer, writeBuffer)
    }
    
    // 将pass添加到composer
    composer.current.addPass(horizontalPass.current)
    composer.current.addPass(verticalPass.current)
    
    // 替换默认渲染流程
    const originalRender = gl.render
    gl.render = () => {
      composer.current.render()
    }
    
    return () => {
      gl.render = originalRender
      composer.current.dispose()
    }
  }, [gl, size])

  return null
}

// 在Test组件中使用BlurPass
export default function Test() {
  return (
    <div className='h-full w-full absolute'>
      <Canvas camera={{ fov: 75, near: 0.01, far: 50, position: [0, 0, 1] }}>
        <color attach="background" args={["black"]} />
        <OrbitControls />
        <Particles />
        <BlurPass />
      </Canvas>
    </div>
  )
}

额外优化建议

  • 降低uKernel的值:13核已经属于较大的模糊核,若视觉效果允许,可降到7或9核,采样次数直接减少。
  • 动态调整分辨率:根据设备性能(比如检测移动端)自动降低模糊处理的分辨率。

内容的提问来源于stack exchange,提问作者Orkun Yigit Cengiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:37:32