0

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:

Vertices Inspector

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?

Multiple X, Y and Z

Community
  • 1
  • 1
Summer Developer
  • 2,056
  • 7
  • 31
  • 68
  • Is it possible that there just isnt enough light to illuminate your object when you use THREE.MeshPhongMaterial( { } ); ? – BFG Feb 23 '17 at 17:26
  • @BFG Thanks, I will check that out once back at my station and report back. :) – Summer Developer Feb 23 '17 at 17:55
  • @SummerDeveloper For `THREE.Points()` there is `THREE.PointsMaterial()`. Read [this thread](https://github.com/mrdoob/three.js/issues/10491) to the end – prisoner849 Feb 23 '17 at 19:39
  • @prisoner849 I don't really follow the conversation, is the conclusion that a point-system shouldn't react to lights, so this is not an attainable task? – Summer Developer Feb 23 '17 at 23:14
  • @BFG I amped up the lighting effects, still a black canvas. – Summer Developer Feb 23 '17 at 23:14
  • 1
    `PointsMaterial() ` does not respond to lights. If you need that feature, you will have to write a custom `ShaderMaterial`. See [this answer](http://stackoverflow.com/questions/12337660/three-js-adjusting-opacity-of-individual-particles/12344288#12344288) to get you started. Also, use `three.js` (not `three.min.js`) for development -- and upgrade to the current `three.js` revision. – WestLangley Feb 25 '17 at 01:53
  • @WestLangley Can u comment on the positioning elements? Am I doing that part right? – Summer Developer Feb 25 '17 at 17:24
  • 1
    For one thing, it should be `geometry.vertices.push( vector )`. Make sure you understand why. – WestLangley Feb 25 '17 at 17:55
  • @WestLangley It ended up that following your example here: http://stackoverflow.com/questions/12659461/rendering-a-large-number-of-colored-particles-using-three-js-and-the-canvas-rend/12663852#12663852 made it work. – Summer Developer Feb 26 '17 at 13:20

0 Answers0