Search code examples
three.jsglslreact-three-fiberreact-three-drei

React-three-fiber Gaussian blur


I am trying to add Gaussian blur to a scene. I am using this GLSL shader:

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

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

float parabola(float x,float k){
  return pow(4.*x*(1.-x),k);
}

void main(){
  float weightSum=gaussianPdf(0.,uSigma);
  vec4 t=texture2D(tDiffuse,v_Uv);
  vec3 diffuseSum=t.rgb*weightSum;
  float invSize=1./uDirectionalResolution;
  
  float focus=distance(vec2(.5),v_Uv);
  focus=smoothstep(0.,.7,focus);
  
  for(int i=1;i<uKernel;i++){
    float x=float(i);
    float w=gaussianPdf(x,uSigma);
    
    vec2 offset=uDirection*invSize*x*uStrength*focus;
    vec4 sample1=texture2D(tDiffuse,v_Uv+offset);
    vec4 sample2=texture2D(tDiffuse,v_Uv-offset);
    diffuseSum+=(sample1.rgb+sample2.rgb)*w;
    weightSum+=2.*w;
  }
  
  gl_FragColor=vec4(diffuseSum/weightSum,1.);
}

It works on images when you provide image as the tDiffuse texture but I am trying to add it as a postprocessing effect. I tried to get the renderTarget of the camera and pass that texture as the tDiffuse but that was computationally too expensive :(
Here is the react code:

import vert from "./vertex.glsl"
import frag from "./fragment.glsl"

function BlurShader() {
    const { gl, size, scene, camera } = useThree();
    const uniforms = useMemo(() => ({
        time: { value: 0.0 },
        uKernel: { value: 13 },
        uSigma: { value: 3 },
        tDiffuse: { value: null },
        uStrength: { value: 1 },
        uDirection: { value: new THREE.Vector2(1, 1) },
        uDirectionalResolution: { value: 512 },
    }), [])
    return (
        <>

        </>
    )
}


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 />
                <BlurShader />
            </Canvas>
        </div>
    )
}

Is there a way of achieving this without doing the camera renderTarget stuff? Or any other more efficient way?


Solution

  • To answer my own question, I created a huge plane in front of the camera :)