Creating a blur effect in three JS using a WebGL shader

Feeling a bit lost here, but eager to learn. I recently switched from P5.js to Three.JS for a project and ran into some challenges with applying shaders. In P5.js, it was simple to create a canvas and add a shader, but in Three.JS, it's not as straightforward. So, I decided to experiment with shaders.

The concept:

  1. Create random circles on a transparent background
  2. Blur the circles
  3. Use the result as a texture

Currently, I've succeeded in drawing the circles (still working on making them random) and using them as a texture in the JavaScript part:

        const geometry = new THREE.PlaneGeometry( 1, 1, 1 );
        const material = new THREE.ShaderMaterial( { 
            uniforms: {
                iP: 0,
                dl: { value : new THREE.Vector2(.6, 0), },
                spots : {  value : 5.0 },
                offset : { value : new THREE.Vector2(0.5, 0.5) },
                radius : { value : .25 },

            },
            vertexShader: _VS,
            fragmentShader: _FS,
            transparent: true
        });

Vertex shader section:

        const _VS = `
            precision mediump float;  
            attribute vec3 aPosition;
            varying vec2 vTexCoord;

            varying vec2 vUv;
            
            void main() {
                vUv = uv;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
            }

        `;

Fragment shader section:

        // fragment shader
        const _FS = `
            precision mediump float;
            varying vec2 vTexCoord;
            varying vec2 vUv;

            uniform float spots;
            uniform vec2  offset;
            uniform float radius;
            
            // uniform sampler2D iP;           // canvas to be blurred
            uniform vec2      dl;

            const float Pi = 6.28318530718;
                
            float rnd(vec3 scale, float seed) {
                return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);
            }

            
            void main() {
                // CIRCLE;
                vec4 color1 = vec4(.1, .5, .3, 1.0);
                vec4 color2 = vec4(0., 0., 0., 0.01);
                vec2 shift = offset; 

                for (float t = 0.0; t <= spots; t++) {
                    float p = smoothstep(radius, radius + .0001, length(vUv - shift));
                    vec4 col = mix(color1, color2, vec4(p));
                    gl_FragColor += col;
                    shift.x += .05;
                    shift.y -= .01;
                }
              

                // BLUR
                
                vec4 col = vec4(0.0);
                float tt = 0.0;

                float off = rnd(vec3(12.9898, 78.233, 151.7182), 0.0);

                for (float t = -30.0; t <= 30.0; t++) {
                    float pc = (t + off - 0.5) / 30.0;
                    float w = 1.0 - abs(pc);
                    vec4 spl = texture2D(iP, vTexCoord + dl * pc);
                            
                    spl.rgb *= spl.a;

                    col += spl * w;
                    tt += w;
                }         
                gl_FragColor = col / tt;
                gl_FragColor.rgb /= gl_FragColor.a + 0.00001;

        }`;

I'm having trouble with the line

vec4 spl = texture2D(iP, vTexCoord + dl * pc);
. I need help integrating the created circles into gl_FragColor. I've spent hours searching for solutions without success. Any guidance or solutions would be greatly appreciated! Thank you in advance!

Answer №1

You may be blending variable names from P5.js and Three.js together. It might help to focus on how Three.js handles things, disregarding the terminology of P5.js since they are not interchangeable. Here's an official example demonstrating a basic shader setup that you can examine in the provided source code.

I've included a simplified version of that demo below with a straightforward shader to illustrate how to pass a time uniform from JavaScript and interpret it in GLSL:

body, html {
margin: 0;
}
<div id="container"></div>

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4a2f396727252e3f262f6739222327390a7b6479647c">[email protected]</a>/dist/es-module-shims.js"></script>

<script type="importmap">
{
    "imports": {
        "three": "https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="98ecf0eafdfdd8a8b6a9acaab6a8">[email protected]</a>/build/three.module.js",
    }
}
</script>
<script type="module">

import * as THREE from "https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9beff3e9fefedbabb5aaafa9b5ab">[email protected]</a>/build/three.module.js";

let camera, scene, renderer;

let uniforms;

init();
animate();

function init() {

    const container = document.getElementById( 'container' );

    camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );

    scene = new THREE.Scene();

    const geometry = new THREE.PlaneGeometry( 2, 2 );

    uniforms = {
        time: { value: 1.0 }
    };

  const _VS = `
    varying vec2 vUv;

    void main() {
      vUv = uv;
      gl_Position = vec4( position, 1.0 );
    }
  `;
  
  const _FS = `
  varying vec2 vUv;
  uniform float time;

  void main()   {
    float blue = sin(time * 5.0 + vUv.x * 10.0) * 0.5 + 0.5;
    gl_FragColor = vec4( vUv.x, vUv.y, blue, 1.0 );
  }
  `;

    const material = new THREE.ShaderMaterial( {

        uniforms: uniforms,
        vertexShader: _VS,
        fragmentShader: _FS

    } );

    const mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    container.appendChild( renderer.domElement );

    onWindowResize();

    window.addEventListener( 'resize', onWindowResize );

}

function onWindowResize() {

    renderer.setSize( window.innerWidth, window.innerHeight );

}

//

function animate() {

    requestAnimationFrame( animate );

    uniforms[ 'time' ].value = performance.now() / 1000;

    renderer.render( scene, camera );

}

</script>

This should provide a foundation for expanding and incorporating other types of uniforms like float, vec2, sampler2D, etc. Additionally, here is further information about which uniforms, attributes, and naming conventions are utilized by default when using ShaderMaterial.

Answer №2

To add effects (post-process) to your entire scene, you must pass them through the EffectComposer. For more information, check out this documentation: https://threejs.org/docs/#manual/en/introduction/How-to-use-post-processing.
Sometimes, you can also apply certain effects by using CSS rules like adding blur to the renderer element:

renderer.domElement.style.filter = `blur(10px)`;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Fields that have been loaded are not initialized within the supplementary function

Two functions are used to load components for a page and save them in two fields - representatives and allUsers. An additional function, subtractSets(), is then used to modify the loaded data slightly. The issue arises when these fields (representatives ...

Having issues with filterAll() and dc.redrawAll() not functioning properly with a dc.pieChart

I am currently experimenting with linking charts together using dc and crossfilter. In this instance, I have successfully linked a table and a pie chart so that clicking on a pie slice updates the table accordingly. However, there seems to be an issue wit ...

Storing data in the filesystem within a Nestjs application

Looking to save the URL of images received from clients using multipart/form-data in a MySQL database. Faced some challenges while following the documentation on how to store an image in the server's file system and provide the URL back to the client. ...

What is the process for printing with JQuery?

I have nested divs with dynamically generated images in my HTML code. My problem is that when I click the print button, I want the corresponding image to be printed. <div id="outputTemp" style="display:none"> <div id="rightoutputimgae"> <di ...

Building a local database using the MVC framework concept

Can a locally stored database be developed using MVC framework principles in javascript and html5? Thank you Ravindran ...

Tips for arranging cards in a stacked position: To create a visually

I'm currently developing a project using react typescript, and I need to showcase various projects in the form of cards. However, I would like these cards to be displayed in a stacked layout like this Here is the component for displaying the projects ...

CAUTION: The presence of numerous imports of Three.js

I encountered a warning when attempting to utilize both @react-three/fiber and @react-three/drei in my project. https://i.sstatic.net/SkM2K.png Steps to replicate npx create-react-app reproduced-warning cd reproduced-warning npm install three @react-thre ...

There is additional script showing up in the browser that was not originally in the source code

Upon loading my website from Oman, an additional script is being loaded that is not part of my original source code. Interestingly, this script does not appear when accessing the site from any other country. The presence of this extra script causes a delay ...

Implementing cookie settings upon button click in a next.js application route

I attempted to set cookies using the following code snippet. import Image from "next/image"; import styles from "./page.module.css"; import { cookies } from "next/headers"; export default function Home() { function setCooki ...

Retrieve the visibility and data type of an object's property in Javascript or Typescript

Issue at hand: I am currently facing a challenge in distinguishing between the private, public, and getter (get X()) properties within a TypeScript class. Current Project Scenario: Within my Angular project, I have implemented a model design pattern. Fo ...

Determine the scope of an element in Javascript in relation to its parent container

I have a function that returns an array with two elements, however it won't work in IE. The function returns the HTML code of what the user selects inside a div (with id=text). It also returns the range of the selection. If the user selects a simpl ...

What could be causing my HTML5 page to crash when accessed online, but not when viewed locally?

I am just getting started with HTML5 and decided to experiment with canvas. This is the first canvas page I have created. Everything works fine when I run the page locally (i.e. file:///), but once I upload the files to my webhost, the page gets stuck whi ...

Storing an updated file (including a revised array of subdocuments) within a Mongoose schema

Here is the current code snippet I am working with: User.findOne( { "subUsers.email" : userEmail }, { subUsers : { $elemMatch: { email : userEmail } } }, fu ...

Is there a way for me to identify when I am "traveling" along the same path?

I want to create a toggle effect where a view is hidden if the user tries to revisit it. This can be useful for showing/hiding modal boxes. Here's the code I attempted: /* Root Instance */ const app = new Vue({ router, watch: { '$route&a ...

Can we display the chosen form values before submitting?

Want to implement a confirmation message for users before submitting their form using onClick="return confirm('are you sure ?')". The basic form structure is as follows: <form> <Select name='val[]' class='select'> ...

What drawbacks come with developing an Express.js application using TypeScript?

Curious about the potential drawbacks of using TypeScript to write Express.js applications or APIs instead of JavaScript. ...

If there is only one remaining option after applying the angular filter, automatically choose that option

Currently, I have a scenario where there are two select boxes. The selection in one box acts as a filter for the other box. In some cases, this filter results in only one option left in the list. I am exploring options to automatically select that lone opt ...

Can Wireframe be applied to a 3D gltf model within an HTML document?

I am currently working on achieving a specific design concept, which can be viewed at the following link: To guide me through this process, I have been following a tutorial available here: While I have successfully implemented the model and created mater ...

Changing the name of a React Native sample application

I am currently working on a project based on this App example: https://github.com/aksonov/react-native-router-flux/tree/master/Example When I tried to change the following lines: index.ios.js import App from './App'; AppRegistry.regist ...

What is the best way to verify if a subscription is ready in a React component?

In order to effectively retrieve data from mongoDB in my meteor/react application, I have put together a simple example. One thing I would like to implement is a loading icon that will be displayed while the data is being fetched. To achieve this, I will ...