three.js
Picking degli oggetti
Ricerca…
Raccolta oggetti / Raycasting
Raycasting significa lanciare un raggio dalla posizione del mouse sullo schermo alla scena, questo è il modo in cui threejs determina l'oggetto su cui vuoi fare clic se lo hai implementato. Threejs ottiene quell'informazione usando un octree , ma ancora in produzione potresti non voler calcolare il risultato su ogni frame o sull'evento mousemove
, ma piuttosto sull'evento click
per un'app più accessibile con requisiti bassi.
var raycaster, mouse = { x : 0, y : 0 };
init();
function init () {
//Usual setup code here.
raycaster = new THREE.Raycaster();
renderer.domElement.addEventListener( 'click', raycast, false );
//Next setup code there.
}
function raycast ( e ) {
//1. sets the mouse position with a coordinate system where the center
// of the screen is the origin
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
//2. set the picking ray from the camera position and mouse coordinates
raycaster.setFromCamera( mouse, camera );
//3. compute intersections
var intersects = raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
console.log( intersects[ i ] );
/*
An intersection has the following properties :
- object : intersected object (THREE.Mesh)
- distance : distance from camera to intersection (number)
- face : intersected face (THREE.Face3)
- faceIndex : intersected face index (number)
- point : intersection point (THREE.Vector3)
- uv : intersection point in the object's UV coordinates (THREE.Vector2)
*/
}
}
ATTENZIONE! Potresti perdere il tuo tempo guardando lo schermo vuoto se non leggi la parte successiva.
Se si desidera rilevare l'helper della luce, impostare il secondo parametro di raycaster.intersectObjects( scene.children );
al vero
Significa raycaster.intersectObjects( scene.children , true);
Il codice raycast rileva solo l'helper della luce.
Se si desidera che rilevi oggetti normali e aiuto luce, è necessario copiare nuovamente la funzione raycast sopra. Vedi questa domanda
Il codice completo di raycast è
function raycast ( e ) {
// Step 1: Detect light helper
//1. sets the mouse position with a coordinate system where the center
// of the screen is the origin
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
//2. set the picking ray from the camera position and mouse coordinates
raycaster.setFromCamera( mouse, camera );
//3. compute intersections (note the 2nd parameter)
var intersects = raycaster.intersectObjects( scene.children, true );
for ( var i = 0; i < intersects.length; i++ ) {
console.log( intersects[ i ] );
/*
An intersection has the following properties :
- object : intersected object (THREE.Mesh)
- distance : distance from camera to intersection (number)
- face : intersected face (THREE.Face3)
- faceIndex : intersected face index (number)
- point : intersection point (THREE.Vector3)
- uv : intersection point in the object's UV coordinates (THREE.Vector2)
*/
}
// Step 2: Detect normal objects
//1. sets the mouse position with a coordinate system where the center
// of the screen is the origin
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
//2. set the picking ray from the camera position and mouse coordinates
raycaster.setFromCamera( mouse, camera );
//3. compute intersections (no 2nd parameter true anymore)
var intersects = raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
console.log( intersects[ i ] );
/*
An intersection has the following properties :
- object : intersected object (THREE.Mesh)
- distance : distance from camera to intersection (number)
- face : intersected face (THREE.Face3)
- faceIndex : intersected face index (number)
- point : intersection point (THREE.Vector3)
- uv : intersection point in the object's UV coordinates (THREE.Vector2)
*/
}
}
Selezione oggetto / GPU
Il picking degli oggetti con Raycasting potrebbe essere un compito gravoso per la tua CPU, a seconda della tua configurazione (ad esempio se non hai una configurazione di tipo ottomano) e del numero di oggetti nella scena.
Se non hai bisogno delle coordinate del mondo sotto il cursore del mouse ma solo per identificare l'oggetto sotto di esso puoi utilizzare la selezione GPU.
Breve spiegazione, la GPU può essere un potente strumento per il calcolo, ma è necessario sapere come ottenere i risultati. L'idea è, se si rendono gli oggetti con un colore che rappresenta il loro id, è possibile leggere il colore del pixel sotto il cursore e trovare l'id dell'oggetto che viene selezionato. Ricorda che RGB è solo un valore esadecimale quindi esiste una conversione tra id (intero) e colore (hex).
- Crea una nuova scena e un nuovo obiettivo di rendering per il tuo oggetto
var pickingScene = new THREE.Scene();
var pickingTexture = new THREE.WebGLRenderTarget(renderer.domElement.clientWidth, renderer.domElement.clientHeight);
pickingTexture.texture.minFilter = THREE.LinearFilter;
- Creare un nuovo materiale shader per il prelievo di oggetti;
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
});
- Aggiungi le tue geometrie mesh / linea a un nuovo attributo che rappresenta il loro id in RGB, crea pickingObject usando la stessa geometria e aggiungilo alla scena picking, e aggiungi la mesh effettiva a un id-> dizionario degli oggetti
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;
}
- Finalmente, con il tuo click del gestore del mouse
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
}