1

I am wondering how to get the light intensity and or the pixel values (rgba) of a given point in a scene.

i for example have a scene with a moving light shining on a cube and want to measure how bright certain points at the cube are:

// Our Javascript will go here.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true });
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshLambertMaterial( );
var cube = new THREE.Mesh( geometry, material );
cube.rotation.x += 0.4;
cube.rotation.y += 0.4;
scene.add( cube );

var plane_geo = new THREE.PlaneGeometry(2,2,2);
var plane = new THREE.Mesh( plane_geo, material );
plane.rotation.x = -1;
plane.position.y = -0.5;
scene.add( plane );

var light = new THREE.SpotLight( 0xff0000, 1, 100 );
//var light = new THREE.PointLight( 0xff0000, 1, 100 );
light.position.set( 3, 2, 2 );
scene.add( light );

var sphereSize = 0.1;
var pointLightHelper = new THREE.PointLightHelper( light, sphereSize );
scene.add( pointLightHelper );

var lightX = 0.5;
var lightY = 0.5;

camera.position.z = 5;



animate();


document.addEventListener("mousemove",mousemove_handler);


function animate() {
 requestAnimationFrame( animate );


 light.position.set(lightX,lightY,1);

 renderer.render( scene, camera );

}

function mousemove_handler(event)
{

 // get Mouse Coords mapped to the 3D Vector
 var vector = new THREE.Vector3();

 vector.set(
     (event.clientX/window.innerWidth) * 2 - 1,
     - (event.clientY/window.innerHeight) * 2 + 1, 
     0.5);

 vector.unproject(camera);

 var dir = vector.sub(camera.position).normalize();

 var distance = - camera.position.z / dir.z;

 var pos = camera.position.clone().add(dir.multiplyScalar(distance));

 lightX = pos.x;
 lightY = pos.y;


}
body { margin: 0; }
   canvas { width: 100%; height: 100% }
<!DOCTYPE html>
<html>

 <head>
  <meta charset=utf-8>
  <title>My first three.js app</title>
  <style>
   body { margin: 0; }
   canvas { width: 100%; height: 100% }
  </style>
 </head>


 <body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
  </body>
 </html>
gaugau
  • 765
  • 1
  • 9
  • 30

1 Answers1

5

There's so many things to consider here.

Start with the obvious:

Light calculation in real life is dependent on interference, candela, wavelength, etc. I do not recommend using webGL lights to do scientific calcs for say, solar performance.

Further:

Light in 3d uses shader passes. This means lighting is a pass over the scene. Depending on things in your scene like semi transparent things like a window, you'll want to move the camera directly over the point in question thereby rendering the point as clearly as possible. Importantly, we can only measure a point when it's facing the camera. But if your scene is simple, it's not to big of a deal unless you have an aggressive fog setting.

Getting the light intensity on a particular pixel is as follows:

  • Get the point of the surface you want to know about.
  • Get the screen position of that pixel.
  • Get the canvas context.
  • Get the context pixel position
  • Get the RGBA value of the pixel
  • Convert the RGBA value to greyscale.

But there's still a catch:

Converting to grey scale is not as straight forward as it seems. Recall how photoshop gives you different methods for converting to grey scale. You have average, luminosity, intensity, lightness, etc.

Here's a fiddle that shows you onclick. The values are presented as 0-1 float. 0 being black.

Working fiddle:

https://jsfiddle.net/schnfteb/

//assumes wegbGL renderer
renderer.domElement.addEventListener("click",function(event){

    var mousePos = getMousePos(this, event);
    var y = mousePos.y;
    var x = mousePos.x;

    var offscreenCanvas = document.createElement("canvas");
    offscreenCanvas.width = this.width;
    offscreenCanvas.height = this.height;
    var ctx = offscreenCanvas.getContext("2d");

    ctx.drawImage(this,0,0);
    var imageData = ctx.getImageData(x,y, 1, 1);
    var c = imageData.data;
    c = [c[0], c[1],c[2]];
    var lightness = ((Math.max.apply(null, c) + Math.min.apply(null, c)) / 2)/255;
    var intensity = (65536 * c[0] + 256 * c[1] + c[2])/16777216;
    var average = ((c[0]+c[1]+c[2]) / 3)/255;
    var luminosity = ((0.21 * c[0]) + (0.72 * c[1]) + (0.07 * c[2]))/255;
    console.log(lightness, intensity, average, luminosity);

},false);

Going the rest of the way:

To get a specific point in world to screen pixel instead of mouse position, see this stackoverflow: Converting 3D position to 2d screen position [r69!]

Radio
  • 2,810
  • 1
  • 21
  • 43
  • `body { margin: 0 }` may help with the accuracy of the mouse clicks. Setting the cube color to `0xffffff` may give more intuitive results. – WestLangley Sep 21 '17 at 18:37
  • Yeah I was setting it to red to show the conversion to greyscale to get a truer lumin. but yeah. and I figured OP would follow the 3d position to screen position link making the margin moot. But totally, yes, answer shows the concept and needs contextual adjustment. – Radio Sep 21 '17 at 18:39
  • 1
    thank you. that helped tremendously! one thing occurs thought, that I don't quite understand in your code: you commented .setPixelRatio out. If I uncomment this i do not get proper values back anymore. how come? – gaugau Sep 24 '17 at 15:04
  • 1
    You're changing the buffer pixel size in relation to physical pixel size. The example code requires a 1to 1 relationship since it's using mouse physical pixel position to find a canvas pixel.if you're on a retina display and you set the pixel ratio to 2 buffer pixels per physical pixel, the mouse will be off by the same order of magnitude. You'd need to multiply the mouse pixel position to obtain the correct drawing buffer position that's drawn to the traditional canvas. In some cases applying the ratio is desired to sharpen the output on ultra dpi devices. – Radio Sep 24 '17 at 20:32