three.js
Objektplockning
Sök…
Objektplockning / Raycasting
Raycasting betyder att man kastar en stråle från muspositionen på skärmen till scenen, det här är hur threejs bestämmer vilket objekt du vill klicka på om du har implementerat det. Threejs får den informationen med hjälp av en octree , men fortfarande i produktion kanske du inte vill beräkna resultatet vid varje ram eller på mousemove
händelsen, utan snarare på click
för en mer tillgänglig app med låga krav.
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)
*/
}
}
VARNING! Du kan tappa tid på att titta på den tomma skärmen om du inte läser nästa del.
Om du vill upptäcka ljushjälpen ställer du in den andra parametern för raycaster.intersectObjects( scene.children );
till sant.
Det betyder raycaster.intersectObjects( scene.children , true);
Raycast-koden kommer bara att upptäcka ljushjälpen.
Om du vill att det ska upptäcka normala objekt såväl som ljushjälpare måste du kopiera ovanstående raycast-funktion igen. Se den här frågan .
Den fullständiga raycastkoden är
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)
*/
}
}
Objektplockning / GPU
Objektplockning med hjälp av Raycasting kan vara en tung uppgift för din CPU beroende på din installation (till exempel om du inte har en octree som installation) och antalet objekt i scenen.
Om du inte behöver världskoordinaterna under muspekaren men bara för att identifiera objektet under det kan du använda GPU-plockning.
Kort förklaring, GPU kan vara ett kraftfullt verktyg för beräkning men du måste veta hur du får resultaten tillbaka. Tanken är att om du återger objekt med en färg som representerar deras ID kan du läsa färgen på pixeln under markören och hitta ID för objektet som väljs. Kom ihåg att RGB bara är ett hexvärde så det finns en konvertering mellan id (heltal) och färg (hex).
- Skapa en ny scen och ett nytt renderingsmål för ditt objekt
var pickingScene = new THREE.Scene();
var pickingTexture = new THREE.WebGLRenderTarget(renderer.domElement.clientWidth, renderer.domElement.clientHeight);
pickingTexture.texture.minFilter = THREE.LinearFilter;
- Skapa ett nytt skuggmaterial för objektplockning;
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
});
- Lägg till dina mesh / radgeometrier ett nytt attribut som representerar deras id i RGB, skapa pickingobjektet med samma geometri och lägg till det i plickscenen och lägg till det faktiska meshet i en id-> objektordbok
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;
}
- Slutligen på din musklickhanterare
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
}