0

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>

1 Answer 1

1

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>

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! Could you explain why animate() has the now argument but is called as animate() regardless?
I stole that part from stackoverflow.com/a/45346664

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.