Orkun Yigit Cengiz
Orkun Yigit Cengiz

Reputation: 1

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?

Upvotes: 0

Views: 761

Answers (2)

Orkun Yigit Cengiz
Orkun Yigit Cengiz

Reputation: 1

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

Upvotes: 0

Orkun Yigit Cengiz
Orkun Yigit Cengiz

Reputation: 1

I went the easy way and created the effect I wanted using "useFBO" from react-three/drei

function BlurShader() {
const FBOTarget = useFBO(1024, 1024, { stencilBuffer: false, minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter });
const materialRef = useRef();
const uniforms = useMemo(() => ({
    time: { value: 0.0 },
    uKernel: { value: 13 },
    uSigma: { value: 6 },
    tDiffuse: { value: null },
    uStrength: { value: 2.5 },
    uDirection: { value: new THREE.Vector2(1, 1) },
    uDirectionalResolution: { value: 512 },
    resolution: { value: new THREE.Vector2(1024, 1024) },
}), [])
useFrame((state) => {
    state.gl.setRenderTarget(FBOTarget);
    state.gl.render(state.scene, state.camera);
    state.gl.setRenderTarget(null);
    materialRef.current.uniforms.tDiffuse.value = FBOTarget.texture;
})
return (
    <>
        <mesh position={[0, 0, 0.2]}>
            <planeGeometry args={[2, 1]} />
            <shaderMaterial
                ref={materialRef}
                fragmentShader={frag}
                vertexShader={vert}
                uniforms={uniforms}
            />
        </mesh>
        <Box args={[0.2, 0.2, 0.2]} position={[0.5, 0.2, 0]} >
            <meshBasicMaterial color={"green"} />
        </Box>
        <Box args={[0.2, 0.2, 0.2]} position={[0.1, 0, -0.1]} >
            <meshBasicMaterial color={"yellow"} />
        </Box>
        <Box args={[0.2, 0.2, 0.2]} position={[-0.7, 0.2, 0]} >
            <meshBasicMaterial color={"red"} />
        </Box>
        <Box args={[0.2, 0.2, 0.2]} position={[-0.4, -0.3, -0.3]} >
            <meshBasicMaterial color={"blue"} />
        </Box>
    </>
)}

Here is the result: Blur

But I still don't know how to apply it to the camera rather than a plane. Maybe render it offscreen and then render it to the camera again, or with effectComposer?

Upvotes: 0

Related Questions