DEV Community

Cover image for Create your own post processing shader with React-Three-Fiber, useFBO and Drei's shaderMaterial with ease 👌
Erik Sachse
Erik Sachse

Posted on

Create your own post processing shader with React-Three-Fiber, useFBO and Drei's shaderMaterial with ease 👌

Look at this cool effect. It's just a fragment shader with the following function: color = texture2D(uScene, uv + vec2(sin(iTime + uv.x * 15.0) * iDistortion, sin(iTime + uv.y * 15.0) * iDistortion)).rgb;

It's just a basic image texture, but we'll display an entire scene as texture onto the shader material. Stick around, it's gonna be fun!

What is this?

Every WebGL scene, 3D or 2D, gets displayed on a screen. So everything that is a 3D scene gets converted into something 2D. Most of the time, this is done by the render engine. But we can draw the scene on to a mesh as a texture, and film it with a orthographic camera. That way we can play around with the texture via fragment shaders. This is super performant and adds a cool effect to a simple scene. It adds cool waves to your scene, and you can even change every single value. For example increase the distortion the faster the user scrolls, change easing functions… you get the idea.

The primitive way

In a plain three.js scene I usually use this file provided by Luruke:

With this file you can "redirect" your renderer into PostFX.js. I just threw the file inside some random three.js vanilla sandbox and that's it!

three.js bare example with PostFX - CodeSandbox

fork it and use as a template for your questions or experiments.

favicon codesandbox.io

The Pmndrs way

With React-Three-Fiber it becomes somewhat more difficult, but we'll go through everything. Pmndrs has a huge library of components that work very well out of the box.

Let's start with a basic setup.

devto 1 - CodeSandbox

devto 1 by eriksachse using @react-three/drei, @react-three/fiber, @types/three, lamina, react, react-dom, react-scripts, three

favicon codesandbox.io

Now we need useFBO to display a scene onto a texture. I used Drei's storybook to quickly mesh up the scene.

devto 2 - CodeSandbox

devto 2 by eriksachse using @react-three/drei, @react-three/fiber, @types/three, babel-plugin-glsl, lamina, react, react-dom, react-scripts, three

favicon codesandbox.io

Image description

useFBO alone doesn't work with shaders, or if, then I don't know how to manipulate that. So what we have to do is display the scene as texture onto a shaderMaterial. We can send the texture as uniform. You could also send videos and images to the shader the same way.

With this snippet we have two uniforms, time and texture. Time will be updated via useRef and useFrame, that way we only "re-render" the value as ref, which won't re-render the entire component.
The vertex shader is giving the fragment shader the correct coordination of the mesh, so we don't need resolution or anything. Keep in mind this is just basic C++ (Or was it C#?) and I hope this snippet isn't overwhelming you.

Replace the <meshBasicMaterial map={target.texture} /> with <waveShaderMaterial ref={shader} uTexture={target.texture} /> and add the shader ref. A complete setup is right here:

devto 3 - CodeSandbox

devto 3 by eriksachse using @react-three/drei, @react-three/fiber, babel-plugin-glsl, lamina, r3f-perf, react, react-dom, react-scripts, three

favicon codesandbox.io

If CSB is throwing errors, just download the Repo and install it locally. With the shader ref we can update the time value for further shading magic 🪄

Okayyyy

Let's add the magic into the fragment shader.
Create a new vec3:
vec3 color = vec3(vUv, 1.0);

Fragment shader to distort the scene:
color = texture2D(uTexture, vUv + vec2(sin(uTime + vUv.x * 15.0) * 0.2, sin(uTime + vUv.y * 15.0) * 0.02)).rgb;

Replace gl_FragColor = vec4(texture, 1.0); with
vec3 texture = texture2D(uTexture, vUv).rgb;
And it should display this:

Now all we have to do is get rid of the Controls and mesh the mesh up to the size of the screen.

If you are done, keep experimenting with different values. Replace sin with tan, add more dynamic values, etc.
Hope this cluster tutorial helped someone 👍

Top comments (0)