对象选择 GPU

根据你的设置(例如,如果你没有像设置这样的八叉树)和场景中的对象数量,使用 Raycasting 进行对象拾取对你的 CPU 来说可能是一项繁重的任务。

如果你不需要鼠标光标下的世界坐标,但仅用于识别其下的对象,则可以使用 GPU 拾取。

简短的解释,GPU 可以是一个强大的计算工具,但你需要知道如何获得结果。我们的想法是,如果使用表示其 id 的颜色渲染对象,则可以读取光标下像素的颜色并找出所拾取对象的 id。记住 RGB 只是十六进制值,因此 id(整数)和颜色(hex)之间存在转换。

  1. 为对象创建新场景和新渲染目标
var pickingScene = new THREE.Scene();
var pickingTexture = new THREE.WebGLRenderTarget(renderer.domElement.clientWidth, renderer.domElement.clientHeight);
    pickingTexture.texture.minFilter = THREE.LinearFilter;
  1. 为对象拾取创建新的着色器材质;
var vs3D = `
attribute vec3 idcolor;
varying vec3 vidcolor;
void main(){
vidcolor = idcolor;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0);
}`;

var fs3D = `
varying vec3 vidcolor;
void main(void) {
gl_FragColor = vec4(vidcolor,1.0);
}`;

var pickingMaterial = new THREE.ShaderMaterial(
    {
        vertexShader: vs3D,
        fragmentShader: fs3D,
        transparent: false,
        side: THREE.DoubleSide
    });
  1. 添加网格/线几何图形,在 RGB 中表示其 id 的新属性,使用相同的几何图形创建 pickingObject 并将其添加到拾取场景,并将实际网格添加到 id-> object 字典
var selectionObjects = [];

for(var i=0; i<myMeshes.length; i++){
    var mesh = myMeshes[i];
    var positions = mesh.geometry.attributes["position"].array;
    var idColor = new Float32Array(positions.length);

    var color = new THREE.Color();
    color.setHex(mesh.id);

    for (var j=0; j< positions.length; j+=3){
        idColor[j] = color.r;
        idColor[j+1] = color.g;
        idColor[j+2] = color.b;
    }

    mesh.geometry.addAttribute('idcolor', new THREE.BufferAttribute(idColor, 3));

    var pickingObject = new THREE.Mesh(mesh.geometry, pickingMaterial);
    
    pickingScene.add(pickingObject);
    selectionObjects[mesh.id] = mesh;
}
  1. 最后,在鼠标单击处理程序上
renderer.render(pickingScene, camera, pickingTexture);
var pixelBuffer = new Uint8Array(4);
renderer.readRenderTargetPixels(pickingTexture, event.pageX, pickingTexture.height - event.pageY, 1, 1, pixelBuffer);
var id = (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | (pixelBuffer[2]);

if (id>0){
    //this is the id of the picked object
}else{
    //it's 0. clicked on an empty space
}