【问题标题】:ThreeJS - How do I measure light intensity / pixel values at a given point in x/y/z?ThreeJS - 如何测量 x/y/z 中给定点的光强度/像素值?
【发布时间】:2025-12-28 11:35:15
【问题描述】:

我想知道如何获取场景中给定点的光强度和/或像素值 (rgba)。

例如,我有一个场景,一个移动的灯光照射在一个立方体上,并且想要测量立方体上某些点的亮度:

// 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>

【问题讨论】:

标签: javascript 3d three.js


【解决方案1】:

这里有很多事情要考虑。

从显而易见的开始:

现实生活中的光计算取决于干涉、坎德拉、波长等。我不建议使用 webGL 光来进行科学计算,例如太阳能性能。

进一步:

3d 中的光使用着色器通道。这意味着照明是场景的传递。根据场景中的事物(例如窗户等半透明事物),您需要将相机直接移动到相关点上,从而尽可能清晰地渲染该点。重要的是,我们只能在它面向相机时测量一个点。但是如果你的场景很简单,除非你有一个激进的雾设置,否则这没什么大不了的。

获取特定像素上的光强如下:

  • 获取您想了解的曲面点。
  • 获取该像素的屏幕位置。
  • 获取画布上下文。
  • 获取上下文像素位置
  • 获取像素的RGBA值
  • 将 RGBA 值转换为灰度。

但仍有一个问题:

转换为灰度并不像看起来那么简单。回想一下 Photoshop 如何为您提供转换为灰度的不同方法。您有平均、亮度、强度、亮度等等。

这是一个向您展示 onclick 的小提琴。这些值显示为 0-1 浮点数。 0 为黑色。

工作小提琴:

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);

继续前进:

要获取世界中的特定点以屏幕像素而不是鼠标位置,请参阅此堆栈溢出:Converting 3D position to 2d screen position [r69!]

【讨论】:

  • body { margin: 0 } 可能有助于提高鼠标点击的准确性。将立方体颜色设置为0xffffff 可能会得到更直观的结果。
  • 是的,我将其设置为红色以显示转换为灰度以获得更真实的亮度。但是,是的。我认为 OP 会遵循 3d 位置到屏幕位置链接,从而使边距没有意义。但总的来说,是的,答案显示了概念,需要根据上下文进行调整。
  • 谢谢。这帮助很大!想到一件事,我在您的代码中不太了解:您将 .setPixelRatio 注释掉了。如果我取消注释,我将不再获得正确的值。怎么来的?
  • 您正在更改与物理像素大小相关的缓冲区像素大小。示例代码需要 1 对 1 的关系,因为它使用鼠标物理像素位置来查找画布像素。如果您使用的是视网膜显示器,并且您将像素比设置为每个物理像素 2 个缓冲区像素,则鼠标将关闭同一个数量级。您需要乘以鼠标像素位置以获得绘制到传统画布的正确绘图缓冲区位置。在某些情况下,需要应用该比率来锐化超 dpi 设备上的输出。
最近更新 更多