3

I'm working with the last version of three.js and I got a scene with a 2D mesh, with gradient color. The color depends of a value assigned to each vertex. I would like to get the value of every point of my gradient by clicking on it with the mouse (by getting the color, and make some math for my value)

I tried to use a system like this one, since it works here : http://jsbin.com/wepuca/2/edit?html,js,output

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

function echoColor(e){
    var imgData = ctx.getImageData(e.pageX, e.pageX, 1, 1);
    red = imgData.data[0];
    green = imgData.data[1];
    blue = imgData.data[2];
    alpha = imgData.data[3];
    console.log(red + " " + green + " " + blue + " " + alpha);  
}

My problem is that this technique is used to get data from the canvas, when an image is displayed, and not a mesh. When I tried to use it on my mesh, I have that error :

"TypeError: ctx is null" or "imageData is not a function" (depending on how I declared my canvas and his context).

Since I didn't find if this method can be used with mesh, I'm here asking if it's possible or not, more than how to do it.

Thanks in advance

Myakko
  • 45
  • 1
  • 8
  • Similar question: http://stackoverflow.com/questions/33433305/threejs-how-to-pick-the-intersection-point-color-of-an-object – Brakebein Apr 24 '17 at 13:07

1 Answers1

4

First, you cannot get a 2D context from a canvas which is already using a WebGL context. This is why you're getting the ctx is null error.

To get the pixel color, you have two options.

1. Transfer the WebGL canvas image onto a canvas with a 2D context.

To do this, create a new canvas and size it to be the same size as your THREE.js canvas, and get a 2D context from it. You can then draw the rendered image from the THREE.js canvas onto your new canvas as an image, using the 2D context:

var img2D = new Image();
twoDeeCanvasImage.addEventListener("load", function () {
    ctx2D.clearRect(0, 0, canvas2D.width, canvas2D.height);
    ctx2D.drawImage(img2D, 0, 0);
    // from here, get your pixel data
});
img2D.src = renderer.domElement.toDataURL("img/png");

From there you can use getImageData, like you did before.

One caveat of this method is that you may see image degradation due to compression introduced when exporting the image via toDataURL. You can circumvent this in some cases:

if(ctx2D.imageSmoothingEnabled !== undefined){
    ctx2D.imageSmoothingEnabled  = false;
}
else if(ctx2D.mozImageSmoothingEnabled  !== undefined){
    ctx2D.mozImageSmoothingEnabled   = false;
}
else if(ctx2D.webkitImageSmoothingEnabled  !== undefined){
    ctx2D.webkitImageSmoothingEnabled   = false;
}
else{
    console.warn("Browser does not support disabling canvas antialiasing. Results image may be blurry.");
}

2. You can also get a pixel value directly from the renderer, but it will cost you an extra render pass.

See this THREE.js example: https://threejs.org/examples/?q=instanc#webgl_interactive_instances_gpu

This example uses readRenderTargetPixels in the THREE.WebGLRenderer. https://threejs.org/docs/#api/renderers/WebGLRenderer

I don't have any personal experience using this method, so hopefullysomeone else can fill in some of the blanks.

TheJim01
  • 8,411
  • 1
  • 30
  • 54
  • Thanks a lot, I'm working on the example you gave me. I don't know if I will succeed, but I know that what I was trying to do won't work ! – Myakko Apr 25 '17 at 12:42
  • @WestLangley where do I get the renderTarget from? I have a normal webgl scene without a renderTarget yet. – vinni Apr 11 '19 at 10:04
  • @vinni Please look at the code for the example WestLangley linked. If you still have questions, please create a new question by using the "Ask a question" button. – TheJim01 Apr 11 '19 at 10:43
  • @TheJim01, I realize this may be a long shot, but - if you, or anybody, remembers the link to the code that WestLangley linked, or what the name of the example was, could you please re-post? WestLangley's post seems to have disappeared completely. – astro_bear Jan 20 '23 at 17:52
  • @astro_bear I'm sorry, I do not remember. There was no guarantee the link was still good anyway. Three.js has also evolved since 2017. I recommend creating a new question to focus on your specific implementation. – TheJim01 Jan 20 '23 at 20:58