简体   繁体   中英

WebGL blur shader in three JS

That's kind of a long shot, but I'm hopelessly lost. I've started learning Three.JS some time ago and wanted to migrate my project from P5.js (as an exercise, but with idea to use it in the future). In P5.js I simply made a canvas and applied a shader to it, but in Three.JS it doesn't seem to work. So I figured out - I'll try it in shaders.

The idea:

  1. to draw random circles (on transparent background)
  2. blur the circles
  3. use the outcome as a texture.

So far, I've managed to draw the circles (not random yet, but still working on it) and use it as a texture. JS 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
        });

Vert shader part:

        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 part:

        // 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 have an issue with the line vec4 spl = texture2D(iP, vTexCoord + dl * pc); . I don't know how I could use created circles on gl_FragColor. I've spent hours reading and looking for a solution, but I didn't find any help. I'd truly appreciate any directions or solutions! Thanks in advance!

You seem to be mixing a lot of variable names between P5.js and Three.js. You should just look at the way Three.js does it, and try to forget the P5.js terminology because they're not the same. Here's an official example of a simple shader setup, you could look at the source code there.

I've copied that demo below with a much more simplified shader so you can get a feel of how to pass a time uniform from JavaScript, and read 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/es-module-shims@1.3:6/dist/es-module-shims:js"></script> <script type="importmap"> { "imports": { "three". "https.//cdn.jsdelivr.net/npm/three@0.142.0/build/three,module:js". } } </script> <script type="module"> import * as THREE from "https.//cdn.jsdelivr.net/npm/three@0.142.0/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>

From here you should be able to extrapolate and add other types of uniforms, including float, vec2, sampler2D , etc. Finally, here's a bit more details on which uniforms, attributes, and naming conventions are used by default when you use ShaderMaterial .

Every effect (post-process), that you want to apply on your whole scene, you should pass into EffectComposer. Here is documentation: https://threejs.org/docs/#manual/en/introduction/How-to-use-post-processing .
Also sometimes you can apply some effects by setting css-rules like blur on the renderer element like this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM