JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
precision highp float;
uniform float sineTime;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec3 offset;
attribute vec4 color;
attribute vec4 orientationStart;
attribute vec4 orientationEnd;
varying vec3 vPosition;
varying vec4 vColor;
void main(){
vPosition = offset * max( abs( sineTime * 2.0 + 1.0 ), 0.5 ) + position;
vec4 orientation = normalize( mix( orientationStart, orientationEnd, sineTime ) );
vec3 vcV = cross( orientation.xyz, vPosition );
vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
vColor = color;
gl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision highp float;
uniform float time;
varying vec3 vPosition;
varying vec4 vColor;
void main() {
vec4 color = vec4( vColor );
color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
gl_FragColor = color;
}
</script>
JavaScript
let camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10);
camera.position.z = 2;
scene = new THREE.Scene();
// geometry
const vector = new THREE.Vector4();
const instances = 50000;
const positions = [];
const offsets = [];
const colors = [];
const orientationsStart = [];
const orientationsEnd = [];
positions.push(0.025, -0.025, 0);
positions.push(-0.025, 0.025, 0);
positions.push(0, 0, 0.025);
// instanced attributes
for (let i = 0; i < instances; i++) {
// offsets
offsets.push(Math.floor( Math.random() * 255 ) , Math.floor( Math.random() * 255 ), Math.floor( Math.random() * 255 ) );
// colors
colors.push(Math.random(), Math.random(), Math.random(), Math.random());
// orientation start
vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
vector.normalize();
orientationsStart.push(vector.x, vector.y, vector.z, vector.w);
// orientation end
vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
vector.normalize();
orientationsEnd.push(vector.x, vector.y, vector.z, vector.w);
}
const geometry = new THREE.InstancedBufferGeometry();
geometry.instanceCount = instances; // set so its initalized for dat.GUI, will be set in first draw otherwise
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Uint8Array(offsets), 3, true));
geometry.setAttribute('color', new THREE.InstancedBufferAttribute(new Float32Array(colors), 4));
geometry.setAttribute('orientationStart', new THREE.InstancedBufferAttribute(new Float32Array(orientationsStart), 4));
geometry.setAttribute('orientationEnd', new THREE.InstancedBufferAttribute(new Float32Array(orientationsEnd), 4));
//...