1

Currently in the page we have a mesh loaded with TreeJS and displayed in a canvas: enter image description here

How could we get the color of the point where we click on?

I have tried as it has been suggested here: Getting the color value of a pixel on click of a mesh with three.js , to create a canvas with a 2d context on top of the one using webgl context.

The problem is that when we convert the model to PNG, the image is white:

Our img 2d src:

enter image description here

If we click on it:

enter image description here

So then the console logs that the color is: 0 0 0 0

Also I will show the code where we generate the webgl canvas and the 2d canvas:

// this class handles the load and the canva for a nrrd
// Using programming based on prototype: https://javascript.info/class
// This class should be improved:
//   - Canvas Width and height

InitCanvas = function (IdDiv, Filename) {


    this.IdDiv = IdDiv;
    this.Filename = Filename
}

InitCanvas.prototype = {

    constructor: InitCanvas,

    init: function () {

        this.container = document.getElementById(this.IdDiv);

        // this should be changed.
        this.container.innerHeight = 600;
        this.container.innerWidth = 800;

        //These statements should be changed to improve the image position
        this.camera = new THREE.PerspectiveCamera(60, this.container.innerWidth / this.container.innerHeight, 0.01, 1e10);
        this.camera.position.z = 300;

        let scene = new THREE.Scene();
        scene.add(this.camera);

        // light

        let dirLight = new THREE.DirectionalLight(0xffffff);
        dirLight.position.set(200, 200, 1000).normalize();

        this.camera.add(dirLight);
        this.camera.add(dirLight.target);


        // read file

        let loader = new THREE.NRRDLoader();
        loader.load(this.Filename, function (volume) {

            //z plane
            let sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4));

            this.container.innerWidth = sliceZ.iLength;
            this.container.innerHeight = sliceZ.jLength;

            sliceZ.mesh.material.color.setRGB(0,1,1);


            console.log('Our slice is: ', sliceZ);

            scene.add(sliceZ.mesh);
        }.bind(this));


        this.scene = scene;



        var canvas2d = document.createElement('canvas');

        canvas2d.id = "canvas2D";
        canvas2d.style.width = `${this.container.innerWidth}px`;
        canvas2d.style.height = `${this.container.innerHeight}px`;
        canvas2d.style.zIndex = 8;




        this.container.appendChild(canvas2d);





        // renderer
        this.renderer = new THREE.WebGLRenderer({alpha: true});
        this.renderer.setPixelRatio(this.container.devicePixelRatio);
        this.renderer.setSize(this.container.innerWidth, this.container.innerHeight);

        // add canvas in container
        this.container.appendChild(this.renderer.domElement);





    },

    animate: function () {

        this.renderer.render(this.scene, this.camera);
    }

}

Also we use it in the logic.js

if (!Detector.webgl) Detector.addGetWebGLMessage();

// global variables for this scripts
let OriginalImg,
    SegmentImg,
    myFileReader;

var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
var mousePressed = false;
var clickCount = 0;
var allText;


init();
animate();


// initilize the page
function init() {


    readTextFile("columna01-es-latin1.txt");

    let originalImgPath = getParameterByName('originalImgPath');
    let filename = originalImgPath || "models/nrrd/columna02.nrrd"; // change your nrrd file
    let idDiv = 'original';
    OriginalImg = new InitCanvas(idDiv, filename);
    OriginalImg.init();
    console.log(OriginalImg);

    let segmentedImgPath = getParameterByName('segmentedImgPath');

    filename = segmentedImgPath || "models/nrrd/columnasegmentado02.nrrd"; // change your nrrd file
    idDiv = 'segment';
    SegmentImg = new InitCanvas(idDiv, filename);
    SegmentImg.init();
    

    let canvas2D = document.getElementById('canvas2D');
    let ctx2D = canvas2D.getContext('2d');

    var img2D = new Image();
    img2D.src = OriginalImg.renderer.domElement.toDataURL("img/png");
    console.log('Our img 2d source is:::: ',img2D.src);
    img2D.addEventListener("load", function () {
        ctx2D.clearRect(0, 0, canvas2D.width, canvas2D.height);
        ctx2D.drawImage(img2D, 0, 0);
        // from here, get your pixel data
        var imgData = ctx2D.getImageData(10, 10, 1, 1);
        red = imgData.data[0];
        green = imgData.data[1];
        blue = imgData.data[2];
        alpha = imgData.data[3];
        console.log(red + " " + green + " " + blue + " " + alpha);
    });
}

function getParameterByName(name, url) {
    if (!url) url = window.location.href;
    name = name.replace(/[\[\]]/g, "\\$&");
    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, " "));
}

let originalCanvas = document.getElementById('original');
originalCanvas.addEventListener('mousedown', onDocumentMouseDown, false);
originalCanvas.addEventListener('mouseup', onDocumentMouseUp, false);


function onDocumentMouseDown(event) {
    mousePressed = true;

    clickCount++;

    let realClickedCanvasX = event.offsetX;
    let realClickedCanvasY = event.offsetY;

    mouse.x = ( ( event.clientX - OriginalImg.renderer.domElement.offsetLeft ) / OriginalImg.renderer.domElement.clientWidth ) * 2 - 1;
    mouse.y = -( ( event.clientY - OriginalImg.renderer.domElement.offsetTop ) / OriginalImg.renderer.domElement.clientHeight ) * 2 + 1


    console.log('Mouse x position is: ', realClickedCanvasX, 'the click number was: ', clickCount);
    console.log('Mouse x position is: ', realClickedCanvasY, 'the click number was: ', clickCount);

    //console.log('Mouse x position is: ', mouse.x, 'the click number was: ', clickCount);
    //console.log('Mouse Y position is: ', mouse.y);

    raycaster.setFromCamera(mouse.clone(), OriginalImg.camera);
    var objects = raycaster.intersectObjects(OriginalImg.scene.children);

    console.log(objects);
}

function onDocumentMouseUp(event) {
    mousePressed = false
}


function animate() {


    requestAnimationFrame(animate);
    OriginalImg.animate();
    SegmentImg.animate();


}

I have also read:

Thank you for your help, in advance.

EDIT:

Thank you @TheJim01 for your help.

I have tried your suggestion, and with the following code:

Into OnDocumentMouseDown function:

var target = OriginalImg.renderer.getRenderTarget();
    console.log('Our target which should be a WebGLRenderTarget is: ');
    console.log(target);

    var outputBuffer = new Uint8Array( OriginalImg.renderer.width * OriginalImg.renderer.height * 4 );

    OriginalImg.renderer.readRenderTargetPixels ( target, 0, 0, OriginalImg.renderer.width, OriginalImg.renderer.height, outputBuffer );

    var pixelIndex = ((realClickedCanvasX * OriginalImg.renderer.width) + realClickedCanvasY) * 4;

    var color = {
        r: outputBuffer[pixelIndex + 0],
        g: outputBuffer[pixelIndex + 1],
        b: outputBuffer[pixelIndex + 2],
        a: outputBuffer[pixelIndex + 3]
    };

    console.log('Color of clicked pixel is: ');
    console.log(color);

We see in the web console:

Our target which should be a WebGLRenderTarget is: 

null

THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.

Color of clicked pixel is: 

Object { r: undefined, g: undefined, b: undefined, a: undefined }

I tried to understand what happened and to solve this I read:

Three.js render to texture

And I added a WebGlRenderTarget as:

... more code...

    this.renderTarget = new THREE.WebGLRenderTarget(this.renderer.domElement.clientWidth, this.renderer.domElement.clientHeight);
    
        this.renderer.render(this.scene, this.camera, this.renderTarget);

... more code...

And now we see in our web console:

    Error: WebGL warning: clear: Framebuffer not complete. (status: 0x8cd6) COLOR_ATTACHMENT0 has no width or height
    three.js:21370:4
    Error: WebGL warning: clear: Framebuffer must be complete.
    three.js:21370:4
    Error: WebGL warning: clear: Framebuffer not complete. (status: 0x8cd6) COLOR_ATTACHMENT0 has no width or height
    three.js:21370:4
    Error: WebGL warning: clear: Framebuffer must be complete.
    three.js:21370:4
  ...

I have tried to look for it and I have seen this issue in GitHub:

https://github.com/pixijs/pixi.js/issues/3767

However I do not see it could be related.

EDIT2:

Thank you @TheJim01 for your help, I appreciate it!! With your help and code the Render Target issue looks like it is solved. In fact I see a strange warning in the web console:

Error: WebGL warning: Exceeded 16 live WebGL contexts for this principal, losing the least recently used one.

After searching for this warning, I saw this great topic: Error: WebGL: Exceeded 16 live WebGL contexts for this principal, losing the least recently used one

And after closing the tab, and reloading the project it has disappeared.

In addition, the colors we obtain are undefined, I will show a console log:

Our OriginalImg where we want to get the color is: 

Object { IdDiv: "original", Filename: "models/nrrd/columna02.nrrd", container: div#original.column, camera: {…}, scene: {…}, renderer: {…} }

Error: WebGL warning: Exceeded 16 live WebGL contexts for this principal, losing the least recently used one.

Error: WebGL warning: Exceeded 16 live WebGL contexts for this principal, losing the least recently used one.

The target being used in this click is: 

Object { uuid: "8EAEFD8F-5C55-4973-84BC-471D43C006F8", width: undefined, height: undefined, scissor: {…}, scissorTest: false, viewport: {…}, texture: {…}, depthBuffer: true, stencilBuffer: true, depthTexture: null }

Our target which should be a WebGLRenderTarget is: 
onMouseDownLogic.js:23:5
Object { uuid: "8EAEFD8F-5C55-4973-84BC-471D43C006F8", width: 800, height: 600, scissor: {…}, scissorTest: false, viewport: {…}, texture: {…}, depthBuffer: true, stencilBuffer: true, depthTexture: null, … }
onMouseDownLogic.js:24:5
Color of clicked pixel is: 
onMouseDownLogic.js:39:5
{…}
​
a: undefined
​
b: undefined
​
g: undefined
​
r: undefined
​
__proto__: Object { … }

To help better understand what I am writing about I refactored the code to better explain and write it.

We have logic.js which aims is to initialize canvas and animate them:

if (!Detector.webgl) Detector.addGetWebGLMessage();

// global variables for this scripts
let OriginalImg,
    SegmentImg;

var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
var allText;

init();
animate();

// initilize the page
function init() {

    readTextFile("columna01-es-latin1.txt");
    OriginalImg = initCanvasOfOriginalImg();
    initCanvasOfSegmentedImg();

}

let originalCanvas = document.getElementById('original');
originalCanvas.addEventListener('mousedown', onDocumentMouseDown, false);

// re-usable objects defined outside your mouse event
console.log('Our OriginalImg where we want to get the color is: ');
console.log(OriginalImg);

var size = OriginalImg.renderer.getSize();
var reusableTarget = new THREE.WebGLRenderTarget(OriginalImg.renderer.width, OriginalImg.renderer.height);

function onDocumentMouseDown(event) {

    let {realClickedCanvasX, realClickedCanvasY} = calculateClickedPointWindowCoordinates(event);
    let OriginalImgRenderer = calculateNormalizedClickedPointCoordinates(event);


    var target = OriginalImgRenderer.getRenderTarget() || reusableTarget;
    console.log('The target being used in this click is: ');
    console.log(target);

    size = OriginalImgRenderer.getSize();
    target.setSize(size.width, size.height);

    OriginalImgRenderer.render( OriginalImg.scene, OriginalImg.camera, target );

    calculateClickedPointColor(OriginalImgRenderer, realClickedCanvasX, realClickedCanvasY);
    projectRayAndGetIntersectedObject();
}

function animate() {


    requestAnimationFrame(animate);
    OriginalImg.animate();
    SegmentImg.animate();


}

Then we have InitCanvas.js which is the Prototype which joins the div, with the canvas and the model being loaded:

// this class handles the load and the canva for a nrrd
// Using programming based on prototype: https://javascript.info/class
// This class should be improved:
//   - Canvas Width and height

InitCanvas = function (IdDiv, Filename) {


    this.IdDiv = IdDiv;
    this.Filename = Filename
}

InitCanvas.prototype = {

    constructor: InitCanvas,

    init: function () {

        this.container = document.getElementById(this.IdDiv);

        // this should be changed.
        this.container.innerHeight = 600;
        this.container.innerWidth = 800;

        //These statenments should be changed to improve the image position
        this.camera = new THREE.PerspectiveCamera(60, this.container.innerWidth / this.container.innerHeight, 0.01, 1e10);
        this.camera.position.z = 300;

        let scene = new THREE.Scene();
        scene.add(this.camera);

        // light

        let dirLight = new THREE.DirectionalLight(0xffffff);
        dirLight.position.set(200, 200, 1000).normalize();

        this.camera.add(dirLight);
        this.camera.add(dirLight.target);


        // read file

        let loader = new THREE.NRRDLoader();
        loader.load(this.Filename, function (volume) {

            //z plane
            let sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4));

            this.container.innerWidth = sliceZ.iLength;
            this.container.innerHeight = sliceZ.jLength;

            sliceZ.mesh.material.color.setRGB(0, 1, 1);


           // console.log('Our slice is: ', sliceZ);

            scene.add(sliceZ.mesh);
        }.bind(this));


        this.scene = scene;


        // renderer
        this.renderer = new THREE.WebGLRenderer({alpha: true});
        this.renderer.setPixelRatio(this.container.devicePixelRatio);
        this.renderer.setSize(this.container.innerWidth, this.container.innerHeight);

        // add canvas in container
        this.container.appendChild(this.renderer.domElement);


    },

    animate: function () {

        this.renderer.render(this.scene, this.camera);
    }

}

Plus we have the functions being used when we click the Mouse, into OnDocumentMouseDown():

let projectRayAndGetIntersectedObject = function () {
    raycaster.setFromCamera(mouse.clone(), OriginalImg.camera);
    var objects = raycaster.intersectObjects(OriginalImg.scene.children);

    console.log(objects);
};

let calculateClickedPointWindowCoordinates = function (event) {
    let realClickedCanvasX = event.offsetX;
    let realClickedCanvasY = event.offsetY;
    return {realClickedCanvasX, realClickedCanvasY};
};

let calculateNormalizedClickedPointCoordinates = function (event) {
    let OriginalImgRenderer = OriginalImg.renderer;
    mouse.x = ( ( event.clientX - OriginalImgRenderer.domElement.offsetLeft ) / OriginalImgRenderer.domElement.clientWidth ) * 2 - 1;
    mouse.y = -( ( event.clientY - OriginalImgRenderer.domElement.offsetTop ) / OriginalImgRenderer.domElement.clientHeight ) * 2 + 1
    return OriginalImgRenderer;
};

let calculateClickedPointColor = function (OriginalImgRenderer, realClickedCanvasX, realClickedCanvasY) {
    var target = OriginalImgRenderer.getRenderTarget();
    console.log('Our target which should be a WebGLRenderTarget is: ');
    console.log(target);

    var outputBuffer = new Uint8Array(OriginalImgRenderer.width * OriginalImgRenderer.height * 4);

    OriginalImgRenderer.readRenderTargetPixels(target, 0, 0, OriginalImgRenderer.width, OriginalImgRenderer.height, outputBuffer);

    var pixelIndex = ((realClickedCanvasX * OriginalImgRenderer.width) + realClickedCanvasY) * 4;

    var color = {
        r: outputBuffer[pixelIndex + 0],
        g: outputBuffer[pixelIndex + 1],
        b: outputBuffer[pixelIndex + 2],
        a: outputBuffer[pixelIndex + 3]
    };

    console.log('Color of clicked pixel is: ');
    console.log(color);
};

I have no clue about how could we fix this issue.

EDIT:3

Thank you @TheJim01 your example code solved the problem, here I show the one I am using:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="chrome=1,IE=edge">
    <title>TEST</title>

    <script src="https://threejs.org/build/three.js"></script>
    <script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
    <script src="js/NRRDLoader.js"></script>
    <script src="js/Volume.js"></script>
    <script src="js/VolumeSlice.js"></script>

</head>

<body>

<canvas id="view" width="500" height="500" style="border: 1px black solid;"></canvas>


<script type="text/javascript">
    /**********************/
    /*   Initialization   */
    /**********************/

    let realClickedCanvasX, realClickedCanvasY, clickCount=0;

    var pixelBuffer = new Uint8Array(500 * 500 * 4);

    function testPixelBuffer() {
        var x, y, r, g, b, a, i1, i2;
        i1 = realClickedCanvasX * size.width;
        i2 = i1 + realClickedCanvasY;
        i2 *= 4;
        r = pixelBuffer[i2 + 0];
        g = pixelBuffer[i2 + 1];
        b = pixelBuffer[i2 + 2];
        a = pixelBuffer[i2 + 3];
            console.log(i2, r, g, b, a);


    }

    document.getElementById('view').addEventListener('mousedown', onDocumentMouseDown, false);

    function onDocumentMouseDown(event) {
        clickCount++;
        realClickedCanvasX = event.offsetX;
        realClickedCanvasY = event.offsetY;
        console.log('You have clicked on: ' + realClickedCanvasY
            + ' , ' + realClickedCanvasY +
            ' it is the click number: ' +
            clickCount);
        testPixelBuffer();
    }

    var renderer = new THREE.WebGLRenderer({
        canvas: document.getElementById("view"),
        antialias: true,
        alpha: true
    });

    var scene = new THREE.Scene();

    var camera = new THREE.PerspectiveCamera(28, 1, 1, 1000);
    camera.position.z = 50;

    camera.add(new THREE.PointLight(0xffffff, 1, Infinity));

    scene.add(camera);

    var controls = new THREE.TrackballControls(camera, renderer.domElement);
    controls.addEventListener("change", render);

    /**********************/
    /* Populate the Scene */
    /**********************/

    let loader = new THREE.NRRDLoader();
    loader.load('models/nrrd/columnasegmentado01.nrrd', function (volume) {
        //z plane
        let sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4));
        scene.add(sliceZ.mesh);
    });

    /**********************/
    /*   Render Function  */
    /**********************/

    var externalTarget = new THREE.WebGLRenderTarget();
    var size = renderer.getSize();
    externalTarget.setSize(size.width, size.height);

    function render() {
        renderer.render(scene, camera);
        renderer.render(scene, camera, externalTarget);
    }

    render();

    /**********************/
    /*   Animation Loop   */
    /**********************/

    function animate() {
        requestAnimationFrame(animate);
        renderer.readRenderTargetPixels(externalTarget, 0, 0, size.width, size.height, pixelBuffer);
        controls.update();
    }

    animate();

</script>
</body>

</html>

I have clicked on the following zones:

enter image description here

And the result were:

    You have clicked on: 81 , 81 it is the click number: 1
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 194324 0 0 0 255
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 94 , 94 it is the click number: 2
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 440376 23 23 23 255
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 150 , 150 it is the click number: 3
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 246600 23 23 23 255
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 195 , 195 it is the click number: 4
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 122780 0 0 0 0
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 210 , 210 it is the click number: 5
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 12840 0 0 0 0
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 310 , 310 it is the click number: 6
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 69240 0 0 0 0
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 271 , 271 it is the click number: 7
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 255084 255 255 255 255
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 360 , 360 it is the click number: 8
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 495440 23 23 23 255
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 318 , 318 it is the click number: 9
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 511272 23 23 23 255
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 291 , 291 it is the click number: 10
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 685164 92 92 92 255
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 283 , 283 it is the click number: 11
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 963132 0 0 0 0
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 180 , 180 it is the click number: 12
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 960720 0 0 0 0
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 174 , 174 it is the click number: 13
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 864696 0 0 0 0
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:51 You have clicked on: 216 , 216 it is the click number: 14
    index.html?_ijt=uvc1m089ec37ho5kobp4qir885:40 510864 46 46 46 255

I wonder why regions 2, 3 are logged as being with the same color!

I also wonder why zones 4,5,6 are logged as they are of equal color!.

I do not fully understand the following code in your example:

function testPixelBuffer() {
        var x, y, r, g, b, a, i1, i2;
        i1 = realClickedCanvasX * size.width;
        i2 = i1 + realClickedCanvasY;
        i2 *= 4;

Could you help me please?

Thank you in advance!

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Yone
  • 2,064
  • 5
  • 25
  • 56

1 Answers1

2

WebGLRenderer.readRenderTargetPixels

This gives you access to the render target's buffer, much like how you would read the data directly from a 2D canvas's ImageData buffer.

// re-usable objects defined outside your mouse event
var size = renderer.getSize();
var reusableTarget = new THREE.WebGLRenderTarget(renderer.width, renderer.height);

// inside your mouse event...
var target = renderer.getRenderTarget() || reusableTarget;

size = renderer.getSize();
target.setSize(size.width, size.height);

renderer.render( scene, camera, target );

var outputBuffer = new Uint8Array( width * height * 4 );

renderer.readRenderTargetPixels( target, 0, 0, width, height, outputBuffer );

var pixelIndex = ((mousePosition.y * width) + mousePosition.x) * 4;

var color = {
  r: outputBuffer[pixelIndex + 0],
  g: outputBuffer[pixelIndex + 1],
  b: outputBuffer[pixelIndex + 2],
  a: outputBuffer[pixelIndex + 3]
};

EDIT:

You're right, you have to render to an external render target. I've edited my code above.

If you're getting errors when trying to render to an external render target, I think that's worthy of creating a separate question (it goes beyond the scope of "how to I get a pixel color at x,y"). Do reference/link to this question, so readers can get the whole story.

Once the render target issue is resolved, this issue will likely become resolved in the process.

Adding a snippet-based test:

I'm honestly not sure why you're getting the results you're getting. Below you can find a simple snippet that implements readRenderTargetPixels, and succeeds.

(Open the Dev Console in your browser to see the results.)

/**********************/
/*   Initialization   */
/**********************/

var pixelBuffer = new Uint8Array( 500 * 500 * 4 );
function testPixelBuffer () {
 var x, y, r, g, b, a, i1, i2;
 for ( x = 0; x < size.width; ++x ) {
  i1 = x * size.width;
  for ( y = 0; y < size.height; ++y ) {
   i2 = i1 + y;
   i2 *= 4;
   r = pixelBuffer[ i2 + 0 ];
   g = pixelBuffer[ i2 + 1 ];
   b = pixelBuffer[ i2 + 2 ];
   a = pixelBuffer[ i2 + 3 ];
   if ( a > 0 ) {
    console.log( i2, r, g, b, a );
        console.count("Colored pixels count:");
   }
  }
 }
}
document.getElementById("test").addEventListener("click", testPixelBuffer);

var renderer = new THREE.WebGLRenderer( {
 canvas: document.getElementById( "view" ),
 antialias: true,
 alpha: true
} );

var scene = new THREE.Scene();

var camera = new THREE.PerspectiveCamera( 28, 1, 1, 1000 );
camera.position.z = 50;

camera.add( new THREE.PointLight( 0xffffff, 1, Infinity ) );

scene.add( camera );

var controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.addEventListener( "change", render );

/**********************/
/* Populate the Scene */
/**********************/

var geo = new THREE.BoxBufferGeometry( 5, 5, 5 );

var mat = new THREE.MeshPhongMaterial( {
 color: "red"
} );

var mesh = new THREE.Mesh( geo, mat );

scene.add( mesh );

/**********************/
/*   Render Function  */
/**********************/

var externalTarget = new THREE.WebGLRenderTarget();
var size = renderer.getSize();
externalTarget.setSize( size.width, size.height );

function render () {
 renderer.render( scene, camera );
 renderer.render( scene, camera, externalTarget );
}
render();

/**********************/
/*   Animation Loop   */
/**********************/

function animate () {
 requestAnimationFrame( animate );
 renderer.readRenderTargetPixels( externalTarget, 0, 0, size.width, size.height, pixelBuffer );
 controls.update();
}
animate();
<!DOCTYPE html>
<html>

<head>
 <meta charset="utf-8" />
 <meta http-equiv="X-UA-Compatible" content="chrome=1,IE=edge">
 <title>TEST</title>

 <script src="https://threejs.org/build/three.js"></script>
 <script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>

</head>

<body>

 <input id="test" value="Test Pixel Buffer" type="button" /><br />

 <canvas id="view" width="500" height="500" style="border: 1px black solid;"></canvas>

</body>

</html>
TheJim01
  • 8,411
  • 1
  • 30
  • 54
  • Thank you for your kind help. I have edited the question, and now Render Target is right, however the colors are all undefined! – Yone Mar 27 '18 at 20:24
  • @Enoy I've added a snippet to test checking pixels using `readRenderTargetPixels`. Please cross-check it against you implementation. If the code looks OK, you may still need to open another topic on why your values are undefined. – TheJim01 Mar 27 '18 at 21:15
  • Thank you @TheJim01 for your great help. Your answer solved the problem. I would like you could explain in you code why do you put i1 and i2. I mean, why do you need to add/multiply to x,y the size.width? I have edited the question to show what code worked for me. Plus some pictures about what have I tried to understand how colors are detected and to verify if they are good detected. Thank you for your help. – Yone Mar 28 '18 at 16:45
  • 1
    @Enoy I used `i1` and `i2` simply so I would only have to do the math of mapping a 2D coordinate into a 1D index once, rather than for every color value. [This answer](https://softwareengineering.stackexchange.com/questions/212808/treating-a-1d-data-structure-as-2d-grid) covers the 2D (screen coordinates) to 1D (`pixelBuffer`) mapping really well. I multiply the final value by `4` because there are 4 values per pixel (RGBA). Pixel index `1` actually covers indices `4`, `5`, `6`, and `7` in `pixelBuffer`. – TheJim01 Mar 28 '18 at 17:06