【发布时间】:2013-12-08 19:08:19
【问题描述】:
我正在使用 Three.js,我想知道如何获取给定区域中的所有对象?
例如,获取在绿色方块中找到的所有对象:
解决方案:
getEntitiesInSelection: function(x, z, width, height, inGroup) {
var self = this,
entitiesMap = [],
color = 0,
colors = [],
ids = [],
pickingGeometry = new THREE.Geometry(),
pickingMaterial = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } ),
pickingScene = new THREE.Scene(),
pickingTexture = new THREE.WebGLRenderTarget( this._renderer.domElement.width, this._renderer.domElement.height),
cloneMesh,
entities = inGroup ?
engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();
pickingTexture.generateMipmaps = false;
//Go over each entity, change its color into its ID
_.forEach(entities, function(entity) {
if(undefined == entity.threeRenderable) {
return ;
}
//Clone entity
cloneMesh = entity.threeRenderable.mesh().clone();
cloneMesh.material = entity.threeRenderable.mesh().material.clone();
cloneMesh.material.map = null;
cloneMesh.material.vertexColors = THREE.VertexColors;
cloneMesh.geometry = entity.threeRenderable.mesh().geometry.clone();
cloneMesh.position.copy( entity.threeRenderable.mesh().position );
cloneMesh.rotation.copy( entity.threeRenderable.mesh().rotation );
cloneMesh.scale.copy( entity.threeRenderable.mesh().scale );
//Cancel shadow
cloneMesh.castShadow = false;
cloneMesh.receiveShadow = false;
//Set color as entity ID
entitiesMap[color] = entity.id();
self._applyVertexColors(cloneMesh.geometry, new THREE.Color( color ) );
color++;
THREE.GeometryUtils.merge( pickingGeometry, cloneMesh);
});
pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
//render the picking scene off-screen
this._renderer.render(pickingScene, this._objs[this._mainCamera], pickingTexture );
var gl = this._renderer.getContext();
//read the pixel under the mouse from the texture
var pixelBuffer = new Uint8Array( 4 * width * height );
gl.readPixels( x, this._renderer.domElement.height - z, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer );
//Convert RGB in the selected area back to color
for(var i=0; i<pixelBuffer.length; i+=4) {
if( 0 == pixelBuffer[i] && 0 == pixelBuffer[i+1] && 0 == pixelBuffer[i+2] && 0 == pixelBuffer[i+3] ) {
continue;
}
color = ( pixelBuffer[i] << 16 ) | ( pixelBuffer[i+1] << 8 ) | ( pixelBuffer[i+2] );
colors.push(color);
}
colors = _.unique(colors);
//Convert colors to ids
_.forEach(colors, function(color) {
ids.push(entitiesMap[color]);
});
return ids;
}
线engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();
只返回一个实体数组,然后我遍历这些实体:
_.forEach(entities, function(entity) { ...
只有具有“threeRenderable”属性(对象)的实体是可见的,因此,我忽略那些没有它的实体:
if(undefined == entity.threeRenderable) {
return ;
}
然后我将实体的克隆网格与 PickingGeometry 合并:
THREE.GeometryUtils.merge( pickingGeometry, cloneMesh);
最后,我将 PickingGeometry 添加到 PickingScene:
pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
然后我读取选定区域的颜色,并返回一个 ID 数组。
您可以查看我当时写的Node.js game engine。
【问题讨论】:
-
你现在使用什么样的选择?请参阅 GPU 拾取示例。您可以使用颜色 ID 渲染通道并使用 readPixels 读取给定区域中的所有颜色信息,从而返回该区域中的所有对象颜色 ID。这将为您提供所需的信息。不确定光线投射器的其他可能性......顺便说一句:你在做什么看起来很酷;)
-
我不确定 GPU 拾取对这里有什么帮助。在该示例中,每个立方体都以不同的颜色呈现 - 这样,使用 readPixel (返回给定区域中的所有像素/颜色),您可以“知道”用户单击了哪个立方体。而我需要的是返回给定区域中的对象列表。
-
是的,正如您所说,readPixels 返回给定区域中的所有像素/颜色。给定区域是您的矩形选择。颜色是您的 objectID。使用 readPixels 输出,您可以评估颜色并返回此给定区域中的对象列表,从而准确产生您想要实现的目标?您只需要将所有对象 ID 渲染到帧缓冲区/纹理中。
-
@GuyGood , 1. 如何将我的所有对象渲染到 frameBuffer + 将它们的颜色设置为 ID? 2. 我尝试这样做,但得到了很多结果:
var gl = canvas.getContext("webgl", {preserveDrawingBuffer: true}); var pixelBuffer = new Uint8Array( width * height ); // here 100 gl.readPixels( posX, posY, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer );现在返回了一个包含很多值(如宽度*高度)的数组,并且并非所有结果都是唯一的。请指教。 -
好吧,请参阅 gpu 示例,了解如何设置 colorID。同样为了更好的视觉效果,使用 gpu-example 并从第一个 render-call 中删除 rendertarget 并取消注释一些内容,这样您就可以看到带有 colorIDs 的图片的样子。如果你从 readPixels 得到一个宽度*高度的数组,这正是你想要的。 (100 次调用?1 应该足以得到这个数组)当然不是所有的值都是唯一的。区域中具有绿色 colorID 的 1 个对象将在该区域中具有 x 个像素。但是你只需要关心第一个外观,因为那时你知道obj在里面。
标签: three.js