Bigboss01
Bigboss01

Reputation: 628

WebGL Shader, modify position according to variable

I'm following the advice in this SO post. I want this blob to turn into other 3D models and back (like this). This set me on the path of Morphing, but I want to use shaders. The SO post (first link) has a basic run-through of how to do it. I'm currently adapting the example linked in that post to just go from start position to end position according to the variable time. However, either the sprites are not displayed, or they are fixed at the end position. Please take a look and let me know where my logic is off.

Here is a snippet:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - custom attributes [particles]</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
    </head>

    <body>
        <div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - custom attributes example - particles</div>
        <div id="container"></div>

        <script type="x-shader/x-vertex" id="vertexshader">

            attribute float size;
            attribute vec3 customColor;
      attribute vec4 endposition;

      uniform float time;

            varying vec3 vColor;

            void main() {

                vColor = customColor;

        vec4 offset = endposition;
        // offset.xyz *= time;

                vec4 mvPosition = modelViewMatrix * vec4( position, 1.0);

                gl_PointSize = size * ( 300.0 / -mvPosition.z );

                gl_Position = projectionMatrix * mvPosition;
        
        gl_Position += offset;
            }

        </script>

        <script type="x-shader/x-fragment" id="fragmentshader">

            uniform vec3 color;
            uniform sampler2D pointTexture;

            varying vec3 vColor;

            void main() {

                gl_FragColor = vec4( color * vColor, 1.0 );
                gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord );

            }

        </script>

        <script type="module">

      import * as THREE from 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.module.min.js';

      import Stats from 'https://cdnjs.cloudflare.com/ajax/libs/stats.js/r17/Stats.min.js';

            let renderer, scene, camera, stats;

            let sphere, material;

            const WIDTH = window.innerWidth;
            const HEIGHT = window.innerHeight;

            init();
            animate();

            function init() {

                camera = new THREE.PerspectiveCamera( 40, WIDTH / HEIGHT, 1, 10000 );
                camera.position.z = 300;

                scene = new THREE.Scene();

                const amount = 1000;
                const radius = 20;

                const positions = new Float32Array( amount * 3 );
                const colors = new Float32Array( amount * 3 );
                const sizes = new Float32Array( amount );

                const vertex = new THREE.Vector3();
                const color = new THREE.Color( 0xffffff );

        const endpositions = new Float32Array( amount * 3);
        const endvertex = new THREE.Vector3();
        const endradius = 100;

                for ( let i = 0; i < amount; i ++ ) {

                    vertex.x = ( Math.random() * 2 - 1 ) * radius;
                    vertex.y = ( Math.random() * 2 - 1 ) * radius;
                    vertex.z = ( Math.random() * 2 - 1 ) * radius;
                    vertex.toArray( positions, i * 3 );

          endvertex.x = ( Math.random() * 2 - 1 ) * endradius;
                    endvertex.y = ( Math.random() * 2 - 1 ) * endradius;
                    endvertex.z = ( Math.random() * 2 - 1 ) * endradius;
                    endvertex.toArray( endpositions, i * 3 );

                    if ( vertex.x < 0 ) {

                        color.setHSL( 0.5 + 0.1 * ( i / amount ), 0.7, 0.5 );

                    } else {

                        color.setHSL( 0.0 + 0.1 * ( i / amount ), 0.9, 0.5 );

                    }

                    color.toArray( colors, i * 3 );

                    sizes[ i ] = 10;

                }

                const geometry = new THREE.BufferGeometry();
                geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
                geometry.setAttribute( 'customColor', new THREE.BufferAttribute( colors, 3 ) );
                geometry.setAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
        geometry.setAttribute( 'endposition', new THREE.BufferAttribute( endpositions, 3 ) );

                //

                material = new THREE.ShaderMaterial( {

                    uniforms: {
                        color: { value: new THREE.Color( 0xffffff ) },
                        pointTexture: { value: new THREE.TextureLoader().load( "https://images-na.ssl-images-amazon.com/images/I/31-ijjGFI1L._SY291_BO1,204,203,200_QL40_FMwebp_.jpg" ) },
            time: { type: 'f', value: 0}
                    },
                    vertexShader: document.getElementById( 'vertexshader' ).textContent,
                    fragmentShader: document.getElementById( 'fragmentshader' ).textContent,

                    blending: THREE.AdditiveBlending,
                    depthTest: false,
                    transparent: true

                } );

                //

                sphere = new THREE.Points( geometry, material );
                scene.add( sphere );

                //

                renderer = new THREE.WebGLRenderer();
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( WIDTH, HEIGHT );

                const container = document.getElementById( 'container' );
                container.appendChild( renderer.domElement );

                stats = new Stats();
                container.appendChild( stats.dom );

                //

                window.addEventListener( 'resize', onWindowResize );

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

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

            }

            function animate() {

                requestAnimationFrame( animate );

                render();
                stats.update();

            }

            function render() {
                let time = 0

        for (let i = 0; i < 1; i+=0.1) {
          time=i
        }

        material.uniforms.time.value = time;

                renderer.render( scene, camera );

            }

        </script>

</body>

</html>

Upvotes: 0

Views: 432

Answers (1)

vtastek
vtastek

Reputation: 123

Your time variable is not updating, it does if it is in animate() function.

shader:

vec3 altposition = mix(position.xyz, endposition.xyz, time);

The box exploded like the Death Star here.

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - custom attributes [particles]</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
    </head>

    <body>
        <div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - custom attributes example - particles</div>
        <div id="container"></div>

        <script type="x-shader/x-vertex" id="vertexshader">

            attribute float size;
            attribute vec3 customColor;
      attribute vec4 endposition;

      uniform float time;

            varying vec3 vColor;

            void main() {

                vColor = customColor;

            vec3 altposition = mix(position.xyz, endposition.xyz, time);
        // offset.xyz *= time;

                vec4 mvPosition = modelViewMatrix * vec4( altposition, 1.0);

                gl_PointSize = size * ( 300.0 / -mvPosition.z );

                gl_Position = projectionMatrix * mvPosition;
        
        
            }

        </script>

        <script type="x-shader/x-fragment" id="fragmentshader">

            uniform vec3 color;
            uniform sampler2D pointTexture;

            varying vec3 vColor;

            void main() {

                gl_FragColor = vec4( color * vColor, 1.0 );
                gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord );

            }

        </script>

        <script type="module">

      import * as THREE from 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.module.min.js';

      import Stats from 'https://cdnjs.cloudflare.com/ajax/libs/stats.js/r17/Stats.min.js';

            let renderer, scene, camera, stats;

            let sphere, material;

            const WIDTH = window.innerWidth;
            const HEIGHT = window.innerHeight;

            init();
            animate();

            function init() {

                camera = new THREE.PerspectiveCamera( 40, WIDTH / HEIGHT, 1, 10000 );
                camera.position.z = 300;

                scene = new THREE.Scene();

                const amount = 1000;
                const radius = 20;

                const positions = new Float32Array( amount * 3 );
                const colors = new Float32Array( amount * 3 );
                const sizes = new Float32Array( amount );

                const vertex = new THREE.Vector3();
                const color = new THREE.Color( 0xffffff );

        const endpositions = new Float32Array( amount * 3);
        const endvertex = new THREE.Vector3();
        const endradius = 100;

                for ( let i = 0; i < amount; i ++ ) {

                    vertex.x = ( Math.random() * 2 - 1 ) * radius;
                    vertex.y = ( Math.random() * 2 - 1 ) * radius;
                    vertex.z = ( Math.random() * 2 - 1 ) * radius;
                    vertex.toArray( positions, i * 3 );

          endvertex.x = ( Math.random() * 2 - 1 ) * endradius;
                    endvertex.y = ( Math.random() * 2 - 1 ) * endradius;
                    endvertex.z = ( Math.random() * 2 - 1 ) * endradius;
                    endvertex.toArray( endpositions, i * 3 );

                    if ( vertex.x < 0 ) {

                        color.setHSL( 0.5 + 0.1 * ( i / amount ), 0.7, 0.5 );

                    } else {

                        color.setHSL( 0.0 + 0.1 * ( i / amount ), 0.9, 0.5 );

                    }

                    color.toArray( colors, i * 3 );

                    sizes[ i ] = 10;

                }

                const geometry = new THREE.BufferGeometry();
                geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
                geometry.setAttribute( 'customColor', new THREE.BufferAttribute( colors, 3 ) );
                geometry.setAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
        geometry.setAttribute( 'endposition', new THREE.BufferAttribute( endpositions, 3 ) );

                //

                material = new THREE.ShaderMaterial( {

                    uniforms: {
                        color: { value: new THREE.Color( 0xffffff ) },
                        pointTexture: { value: new THREE.TextureLoader().load( "https://images-na.ssl-images-amazon.com/images/I/31-ijjGFI1L._SY291_BO1,204,203,200_QL40_FMwebp_.jpg" ) },
            time: { type: 'f', value: 0}
                    },
                    vertexShader: document.getElementById( 'vertexshader' ).textContent,
                    fragmentShader: document.getElementById( 'fragmentshader' ).textContent,

                    blending: THREE.AdditiveBlending,
                    depthTest: false,
                    transparent: true

                } );

                //

                sphere = new THREE.Points( geometry, material );
                scene.add( sphere );

                //

                renderer = new THREE.WebGLRenderer();
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( WIDTH, HEIGHT );

                const container = document.getElementById( 'container' );
                container.appendChild( renderer.domElement );

                stats = new Stats();
                container.appendChild( stats.dom );

                //

                window.addEventListener( 'resize', onWindowResize );

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

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

            }
            
            var then = 0;
            var time = -1.633;
      var reverse = 1;
      function animate(now) {
                now *= 0.001;
                
                const delta = now - then;
                
                then = now;
                
                if(time > 15) {
          reverse *= -1;
        }

        if(time < -4) {
          reverse *= -1;
        } 
        
                time = time + delta * 1.0 * reverse;
                material.uniforms.time.value = Math.max(0.0, time);
                
                requestAnimationFrame( animate );
        
                render();
                stats.update();

            }

        
            function render() {
               

        
        

        renderer.render( scene, camera );

            }

        </script>

</body>

</html>

Upvotes: 1

Related Questions