<script type="x-shader/x-vertex" id="vertex">
attribute float aAlpha;
attribute vec3 aColor;
attribute float aTime;
attribute vec3 aTarget;
varying float vTime;
uniform float uTime;
varying float vAlpha;
varying vec3 vColor;
highp float random(vec2 co)
{
highp float a = 12.9898;
highp float b = 78.233;
highp float c = 43758.5453;
highp float dt= dot(co.xy ,vec2(a,b));
highp float sn= mod(dt,3.14);
return fract(sin(sn) * c);
}
void main(){
float curTime = mod(uTime + aTime, 3.);
float rate = curTime/2.;
vec3 pos;
if(rate < 1.0){
pos = mix(position, aTarget, rate);
}else{
float fract = rate - 1.0;
pos = mix(position, aTarget, 1.0 + fract * fract);
}
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
if(aAlpha < 0.7) vAlpha = clamp((1.0 - rate)/1.0, 0., 1.0);
else vAlpha = clamp((1.5 - rate)/0.5, 0., 1.0);
vColor = aColor;
gl_PointSize = 1.0; //(size * scale) * (1000.0 / length(mvPosition.xyz));
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragment">
varying float vAlpha;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, vAlpha );
}
</script>
html, body{
width: 100%;
height: 100%;
overflow: hidden;
}
var image, objLoader, bunnyGeo, particle, clock;
var camera, scene, renderer;
var imgSrc = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/13842/bunny.png"
var texture;
var datas = [];
var targetArr = [];
var MAX = 300000;
objLoader = new THREE.OBJLoader();
objLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/13842/bunny.obj", onLoadObj);
function range(min, max){
return min + (max - min) * Math.random();
}
class CustomParticle extends THREE.BufferGeometry {
constructor(){
super()
this.count = MAX;
var positionArray = new Float32Array(this.count * 3);
var endPositionArray = new Float32Array(this.count * 3);
var alphaArray = new Float32Array(this.count);
var timeArray = new Float32Array(this.count);
var colorArray = new Float32Array(this.count * 3);
for(var ii = 0; ii < this.count; ii++){
var data = datas[parseInt(datas.length * Math.random())];
var targetPos = targetArr[parseInt(targetArr.length * Math.random())];
positionArray[ii * 3 + 0] = data.x;
positionArray[ii * 3 + 1] = -10;
positionArray[ii * 3 + 2] = data.y;
endPositionArray[ii * 3 + 0] = targetPos.x + range(-1, 1);
endPositionArray[ii * 3 + 1] = targetPos.y + range(-1, 1);
endPositionArray[ii * 3 + 2] = targetPos.z + range(-1, 1);
timeArray[ii] = 3 * Math.random();
alphaArray[ii] = Math.random();
colorArray[ii * 3 + 0] = data.color.r;
colorArray[ii * 3 + 1] = data.color.g;
colorArray[ii * 3 + 2] = data.color.b;
}
this.addAttribute("position", new THREE.BufferAttribute(positionArray, 3));
this.addAttribute("aTarget", new THREE.BufferAttribute(endPositionArray, 3));
this.addAttribute("aTime", new THREE.BufferAttribute(timeArray, 1));
this.addAttribute("aAlpha", new THREE.BufferAttribute(alphaArray, 1));
this.addAttribute("aColor", new THREE.BufferAttribute(colorArray, 3));
}
}
class CustomMat extends THREE.ShaderMaterial {
constructor(){
var uniforms = {
uTime : {type : "f", value : 0}
};
super({
uniforms: uniforms,
vertexShader: document.getElementById("vertex").textContent,
fragmentShader: document.getElementById("fragment").textContent
});
this.transparent = true;
this.depthWrite = false;
this.blending = THREE.AdditiveBlending;
}
}
function onLoadObj(group){
var geometry = group.children[0].geometry;
var positionArr = geometry.attributes.position.array;
var positionCount = positionArr.length / 3;
var scale = 120;
var size = 0.2;
for(var ii = 0; ii < positionCount; ii++){
var pos = new THREE.Vector3(positionArr[3 * ii] * scale + 3, positionArr[3 * ii + 1] * scale - 15, positionArr[3 * ii + 2] * scale -15);
targetArr.push(pos);
}
image = new Image();
image.onload = onLoadimage;
image.crossOrigin = "Anonymous";
image.src = imgSrc;
}
function init(){
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.z = 30;
camera.position.y = 30;
camera.lookAt(new THREE.Vector3())
scene = new THREE.Scene();
particle = new THREE.Points(new CustomParticle(), new CustomMat());
scene.add(particle);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000);
clock = new THREE.Clock();
document.body.appendChild(renderer.domElement);
document.addEventListener('mousemove', onDocumentMouseMove, false);
TweenMax.ticker.addEventListener("tick", loop);
}
function onLoadimage(){
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
var data = ctx.getImageData(0, 0, image.width, image.height);
var imageData = data.data;
var scale = 10;
for(var yy = 0; yy < image.height; yy++){
for(var xx = 0; xx < image.width; xx++){
var num = 4 * (image.width * yy + xx ) + 3;
var alpha = imageData[num];
if(alpha != 0){
var colorRate = xx/image.width
var color = new THREE.Color();
color.setHSL(colorRate, 0.3 + 0.3 * Math.random(), 0.3 + 0.3 * Math.random())
var data = {x: (xx - image.width / 2) / scale, y: (yy - image.height / 2) / scale, alpha: alpha / 255, color: color }
datas.push(data);
}
}
}
init();
}
var theta = 0;
var time = 0;
var mouse = new THREE.Vector2();
function loop(){
var delta = clock.getDelta();
time += delta;
theta += (mouse.x /3 - theta)/10;
camera.position.z = 30 * Math.cos(theta);
camera.position.x = 30 * Math.sin(theta);
camera.lookAt(new THREE.Vector3())
particle.material.uniforms.uTime.value = time;
renderer.render(scene, camera);
}
function onDocumentMouseMove(event){
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
}
window.addEventListener("resize", function(ev){
if(camera){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
if(renderer)
renderer.setSize(window.innerWidth, window.innerHeight);
});
window.addEventListener('keydown', function(ev){
switch(ev.which){
case 27:
isLoop = !isLoop;
if(isLoop){
clock.stop();
TweenMax.ticker.addEventListener("tick", loop);
}else{
clock.start();
TweenMax.ticker.removeEventListener("tick", loop);
}
break;
}
});
View Compiled
This Pen doesn't use any external CSS resources.