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>