Raycaster working for generated geometry, but not for gltf models

Hi,

I want to create a highlight effect to an object if the mouse is hovering over it. The straight forward solution: raycasting. However I can’t seem to get it working and couldn’t solve it with online forum posts so far.

As mentioned it works accurately for a cube I generated by code, but it doesn’t detect hits on a imported gltf. I tried replacing the gltf to a simpler model and eventually created my own cube.gltf in blender (this also didn’t work).

When doing console.log(scene.children) all models are shown as expected.

Here is my code:

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

let camera, scene, container, renderer, loader, raycaster;

const mouse = new THREE.Vector2();
let INTERSECTED;

init();
animate();

function init(){

	scene = new THREE.Scene();
	
	camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 1000 );
	camera.position.z = 5;

	renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( 200, 200 );
	container =  document.getElementById( 'canvas' );
	document.body.appendChild( container );
	container.appendChild( renderer.domElement );

	loader = new GLTFLoader();

	raycaster = new THREE.Raycaster();
	document.addEventListener( 'mousemove', onDocumentMouseMove );

	const geometry = new THREE.BoxGeometry( 1, 1, 1 );
	const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
	const cube = new THREE.Mesh( geometry, material );
	scene.add( cube );

	// import gltf model and add it to scene
	loader.load( 'resources/model.gltf', function ( gltf ) {
		scene.add( gltf.scene );
	}, undefined, function ( error ) {
		console.error( error );
	} );

	console.log(scene.children);
}

function animate() {
	requestAnimationFrame( animate);
	renderer.render( scene, camera );
}

function onDocumentMouseMove( event ) {

	event.preventDefault();

	//I use a small <div> element for the canvas.
	//Scales mouse coordinates from -1.0 to 1.0 on canvas
	const rect = renderer.domElement.getBoundingClientRect();
	mouse.x = ( ( event.clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1;
	mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;

	raycaster.setFromCamera( mouse, camera );
	
	const intersects = raycaster.intersectObjects( scene.children, false );
	
	if ( intersects.length > 0 ) {
		const targetDistance = intersects[ 0 ].distance;
		console.log(intersects);
	}
	console.log(mouse);
}

Try replace

const intersects = raycaster.intersectObjects( scene.children, false );

with

const intersects = raycaster.intersectObjects( scene.children, true );

See the recursive option

1 Like

Thanks!

That feels like a rookie mistake :sweat_smile:
I was focused on the gltf since it worked with generated geometry that I didn’t look at the raycaster all that well…

For whom it might help:

const intersects = raycaster.intersectObjects( scene.children, true );

means that the objects are searched for recursively throughout the scene. And since the gltf meshes where children of the gltf object, it could not be found if recursive was false.

Also, in a very large scene, it would be better to only raycast against specific objects instead of every object in the scene as you do when using scene.children in the intersectObjects method.

In this example link below, I load a glb, and add only some of the child objects to a pickableObjects array which I then use in the intersectObjects method.

Example : Raycaster Mouse Picking

I created a simple scene in Blender, named each object, exported as glb, and then in my code, traversed the loaded model and did different things depending on the name of the object in the hierarchy.

image

1 Like