I am using three.js
to draw an image on the canvas, collect data from this image (i.e. pixel color) and redraw the image as a collection of particles using the data collected from the image, such as the colors.
I have zero error messages or warnings, just a blank, black canvas.
The code I am using is below:
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com/assets/css/sl.jpg");
xhr.responseType = "blob";
xhr.onload = function()
{
blob = xhr.response;
P.readAsDataURL(blob);
P.onload = function(){
image = document.createElement("img");
image.src = P.result;
setTimeout(function(){
// split the image
addParticles();
}, 100);
}
}
xhr.send();
addLights();
update();
setTimeout(function(){
holdAtOrigin = "next";
},1000)
function addParticles()
{
// draw in the image, and make sure it fits the canvas size :)
var ratio = 1 / Math.max(image.width/500, image.height/500);
var scaledWidth = image.width * ratio;
var scaledHeight = image.height * ratio;
context.drawImage(image,
0,0,image.width,image.height,
(500 - scaledWidth) * .5, (500 - scaledHeight) *.5, scaledWidth, scaledHeight);
// now set up the particle material
var material = new THREE.MeshPhongMaterial( { } );
var geometry = new THREE.Geometry();
var pixels = context.getImageData(0,0,WIDTH,HEIGHT);
var step = DENSITY * 4;
var x = 0, y = 0;
// go through the image pixels
for(x = 0; x < WIDTH * 4; x+= step)
{
for(y = HEIGHT; y >= 0 ; y -= DENSITY)
{
var p = ((y * WIDTH * 4) + x);
// grab the actual data from the
// pixel, ignoring any transparent ones
if(pixels.data[p+3] > 0)
{
var pixelCol = (pixels.data[p] << 16) + (pixels.data[p+1] << 8) + pixels.data[p+2];
var color = new THREE.Color(pixelCol);
var vector = new THREE.Vector3(-300 + x/4, 240 - y, 0);
// push on the particle
geometry.vertices.push(new THREE.Vector3(vector));
geometry.colors.push(color);
}
}
}
// now create a new system
particleSystem = new THREE.Points(geometry, material);
console.log(particleSystem);
particleSystem.sortParticles = true;
// grab a couple of cacheable vals
particles = particleSystem.geometry.vertices;
colors = particleSystem.geometry.colors;
// add some additional vars to the
// particles to ensure we can do physics
// and so on
var ps = particles.length;
while(ps--)
{
var particle = particles[ps];
particle.velocity = new THREE.Vector3();
particle.mass = 5;
particle.origPos = particle.x.clone();
}
// gc and add
pixels = null;
scene.add(particleSystem);
//test render
}
function addLights()
{
// point
pointLight = new THREE.PointLight( 0xFFFFFF );
pointLight.position.x = 300;
pointLight.position.y = 300;
pointLight.position.z = 600;
scene.add( pointLight );
// directional
directionalLight = new THREE.DirectionalLight( 0xFFFFFF );
directionalLight.position.x = -.5;
directionalLight.position.y = -1;
directionalLight.position.z = -.5;
directionalLight.position.normalize();
directionalLight.intensity = .6;
scene.add( directionalLight );
}
function update(){
var ps = particles.length;
while(ps--)
{
var particle = particles[ps];
// if we are holding at the origin
// values, tween the particles back
// to where they should be
if(holdAtOrigin == "start")
{
particle.velocity = new THREE.Vector3();
//particle.position.x += (particle.origPos.x - particle.position.x) * .2;
//particle.position.y += (particle.origPos.y - particle.position.y) * .2;
//particle.position.z += (particle.origPos.z - particle.position.z) * .2;
particle.x.x += (particle.origPos.x - .0000000000000000001) * 2;
particle.x.y += (particle.origPos.y - .0000000000000000001) * 2;
}
else if (holdAtOrigin == "next")
{
particle.velocity = new THREE.Vector3();
particle.x.x += (particle.origPos.x - particle.x.x) * .2;
particle.x.y += (particle.origPos.y - particle.x.y) * .2;
particle.x.z += (particle.origPos.z - particle.x.z) * .2;
}
else{
// get the particles colour and put
// it into an array
var col = colors[ps];
var colArray = [col.r, col.g, col.b];
// go through each component colour
for(var i = 0; i < colArray.length; i++)
{
// only analyse it if actually
// has some of this colour
if(colArray[i] > 0)
{
// get the target based on where it
// is in the array
var target = i == 0 ? redCentre :
i == 1 ? greenCentre :
blueCentre;
// get the distance of the particle to the centre in question
// and add on the resultant acceleration
var dist = particle.position.distanceToSquared(target.position),
force = ((particle.mass * target.mass) / dist) * colArray[i] * AGGRESSION,
acceleration = (new THREE.Vector3())
.sub(target.position,particle.position)
.normalize()
.multiplyScalar(force);
// if we are attracting we add
// the velocity
if(mode == ATTRACT)
{
// note we only need to check the
// squared radius for the collision :)
if(dist > target.boundRadiusSquared) {
particle.velocity.addSelf(acceleration);
}
else if (bounceParticles) {
// bounce, bounce, bounce
particle.velocity.negate();
}
else {
// stop dead
particle.velocity = new THREE.Vector3();
}
}
else {
// push it away
particle.velocity.subSelf(acceleration);
}
particle.position.addSelf(particle.velocity);
}
}
}
}
// set up a request for a render
requestAnimationFrame(update);
render();
}
function render()
{
// only render if we have
// an active image
if(image) {
if(holdAtOrigin=="start")
{
camera.position.z = 900;
}
if(camera.position.z < 200)
{
//do nothing
}
else{
camera.position.z -= 1.7;
};
renderer.render( scene, camera );
}
}
I checked the console log at various intervals and found that the pixel data is being collected appropriately, so I don't know what is wrong.
Is it the material? When I used a normal (light-independent) material the code worked as expected, I could see my particles.
But I wanted it to be affected by lights, so I changed it to var material = new THREE.MeshPhongMaterial( { } );
without any arguments.
Is this my problem or is it elsewhere in the code?
Thank you!
This may also be pertinent: How to get the absolute position of a vertex in three.js?
Because particle.x.x
or particle.x.y
doesn't look right to me, even though I wrote that code based on logged object contents.
EDIT: I changed the Phong
line to THREE.PointsMaterial
and amped up the potency of the light, but still a blank, black canvas.
EDIT 2: So I think it may be a problem with the particle coordinates being misconstrued? When I inspect using console.log(particleSystem);
I get the following:
Did I used to be that the x,y,z were wrapped in a position
property that newer versions of three.js
have removed?
For example I've found example code like:
particle.origPos = particle.position.clone();
But I don't see a position
property? How would I clone just the x,y and z bits or should I clone the whole vertex? Sorry if this is confusing or irrelevant just trying to chase down why I have a blank canvas.
EDIT 3: I've removed the update function's position alterations but I still get a weird console log for the particle-system even when all I am doing is cloning said particle using particle.origPos = particle.clone();
Basically I have and x,y and z property but the x property is an object with a subsequent x,y and z. Why is this and how do I fix?