Raycaster not working or triggering properly

I’ve been going through the web to learn about raycaster, I have a glb model made in blender, I am using a “mousedown” event on the canvas to get the object clicked by the raycaster, But the raycaster itself is not working as intended, I also don’t know what is going wrong, Please help,

This is my main code,

import * as THREE from "./three.js/build/three.module.js";
import { GLTFLoader } from "./three.js/examples/jsm/loaders/GLTFLoader.js";

import { OrbitControls } from "./three.js/examples/jsm/controls/OrbitControls.js";
import { RGBELoader } from "./three.js/examples/jsm/loaders/RGBELoader.js";

var canvas;
var renderer;
var camera;
var scene;
var clock;
var directionalLight;
var orbitControls;
var raycaster
var mouse = { x : 0, y : 0 };

var mainScene;
var mainAnimation;
var mainAnimationMixer;
var animationAction;

var rotateClockwise;
var rotateAntiClockwise;

const animationState = {
    RotateForward : "ROTATEFORWARDS",
    RotateBackward : "ROTATEBACKWARDS"
}

let animationStateToggle = animationState.RotateForward;

function init(){
    // Canvas
    canvas = document.querySelector('#threejscanvas');

    var animateButton = document.getElementById('animate-button');
    animateButton.onclick = function(){
        ToggleAnimation();
    }

    // Renderer
    renderer = new THREE.WebGLRenderer({canvas, antialias: true, alpha: true, logarithmicDepthBuffer: true});
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 1;
    renderer.outputEncoding = THREE.sRGBEncoding;

    // Scene
    scene = new THREE.Scene();    
    
    clock = new THREE.Clock();

    // HDRI
    setupHDRI("three.js/main/hdr/", "christmas_photo_studio_04_1k.hdr");
    // Camera
    setupCamera();    
    // Lights
    setupLights();
    // GLFT Model Import
    importGltfModel("three.js/main/glb/NeonCorridor.glb");
    
    raycaster = new THREE.Raycaster();
    canvas.addEventListener( 'mousedown',onMouseDown , false );

    // Render
    renderer.render(scene, camera);
}

function animate(){
    requestAnimationFrame(animate);
    if (mainAnimationMixer !== undefined ){
        mainAnimationMixer.update(clock.getDelta());
    }
}

function render(time){
    time *= 0.001; // Time in seconds

    // Responsive display
    if(resizeRendererToDisplaySize(renderer)){
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
    }    

    renderer.render(scene, camera);

    // Called to start the loop
    requestAnimationFrame(render);   
}

function resizeRendererToDisplaySize(renderer){
    const pixelRatio = window.devicePixelRatio;
    const width = canvas.clientWidth * pixelRatio | 0;
    const height = canvas.clientHeight * pixelRatio | 0;
    const isResized = canvas.width !== width|| canvas.height !== height;
    if(isResized){
        renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
    }
    return isResized;
}

function setupLights(){
    // Directional Light
    const color =  0xffffff;
    const intensity = 1;
    directionalLight = new THREE.DirectionalLight(color, intensity);
    directionalLight.position.set(-1, 2, 4);
    scene.add(directionalLight);
}

function setupCamera() {
    const fov = 60;
    const aspect = 2; // the canvas default
    const near = 0.1;
    const far = 100;
    camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    camera.position.z = 2;
    orbitControls = new OrbitControls(camera, canvas);
    orbitControls.target.set(0, 0, 0);
    orbitControls.update();
}

function setupHDRI(path, name){
    new RGBELoader()
          .setDataType(THREE.UnsignedByteType)
          .setPath(path)
          .load(name, function (texture) {
            const envMap = pmremGenerator.fromEquirectangular(texture).texture;

            //scene.background = envMap;
            scene.environment = envMap;

            texture.dispose();
            pmremGenerator.dispose();
          });

    const pmremGenerator = new THREE.PMREMGenerator(renderer);
    pmremGenerator.compileEquirectangularShader();
}

function importGltfModel(path){
    const gltfLoader = new GLTFLoader();    
    gltfLoader.load(path, (loadedModel) => {
        mainScene = loadedModel.scene;
        mainScene.traverse(meshes => {
            if (meshes.isMesh) {
                meshes.castShadow = true;
                meshes.receiveShadow = true;
            }
          });

        mainAnimationMixer = new THREE.AnimationMixer(mainScene);

        mainAnimation = loadedModel.animations;
        rotateClockwise =  THREE.AnimationClip.findByName(mainAnimation, "NeonRotation");
        rotateAntiClockwise = THREE.AnimationClip.findByName(mainAnimation, "Neon_Rotate_Reverse");        

        scene.add(mainScene);
        
    }); 
} 

function PlayAnimation(clipName, timeScale){    
    animationAction = mainAnimationMixer.clipAction(clipName);
    animationAction.reset();
    animationAction.setLoop(THREE.LoopOnce);    
    animationAction.timeScale = timeScale;
    animationAction.play();
    animate();
}

function ToggleAnimation(){
    if(animationStateToggle == animationState.RotateBackward){
        animationStateToggle = animationState.RotateForward;
        PlayAnimation(rotateClockwise, 1);
    }
    else{
        animationStateToggle = animationState.RotateBackward;
        PlayAnimation(rotateAntiClockwise, 1);
    }
}

function onMouseDown() {

    //1. sets the mouse position with a coordinate system where the center
    //   of the screen is the origin
    mouse.x = ( event.clientX / renderer.domElement.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / renderer.domElement.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 );

    if(intersects.length > 0){
        for ( var i = 0; i < intersects.length; i++ ) {
            console.log( intersects[ i ] ); 
            intersects[0].object.material.color.setHex("0x2f2f2f");
            /*
                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)
            */
        }
    }    
}

requestAnimationFrame(render);
init();

hi Suraj_K_M
at first glance there is nothing strange in your code.
Your var mouse declaration is not an instance of THREE.Vector2 but in this case it’s almost the same because setFromCamera method should use only the constructor x and y.

It works with mousemove event? …some devices doesn’t like mousedown
Have you a console.log answer? is it executed?

So I changed my event to “click” and got my mouse down event working, In the mouse co-ord, I changed my code to,

    mouse.x = event.offsetX;
    mouse.y = event.offsetY;

Since the old mouse co-ord code was returning “nan” when the event was firing, Still the Raycaster part is not working…Not even working on a default box geometry mesh.

@Suraj_K_M

I think you are using a for loop where it is not neccisarry, as your mouse will only intersect one object at a time on mouse down, therefore this will not be an array, the intersects function already has scene.children as its array and will always return only one of them on click… Intersects[0] If that makes sense?

Try remove the for loop and do

if ( intersects.length > 0 ) {            
    if (intersects[0].object) {
   console.log(intersects[0].object);
 }
};  

Also your first mouse coordinates were right… Change to

    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
1 Like

Not working, I first tried in the mouse co-ord to use canvas.innerwidth/innerheight instead of window, That returned nan, Then using window.innerwidth/innerheight started working now,

But the Raycaster is not at all triggering any logs even without any for loops…

Here is the jsfiddle, It wont work without any local files and the model itself, But this will give you a whole preview of my code

https://jsfiddle.net/kf4tcps8/

hey @Suraj_K_M

canvas will not have innerWidth or innerHeight attributes, you have to simply use canvas.width and canvas.height…

i have got the example working and intersection works as expected… ( only thing is it’s loading a model that’s accessible online / not your original model but you can see in the console everything logs as expected…)

https://jsfiddle.net/svxk3t2q/3/

Works like a charm, Really thanks for your help.

1 Like

@Suraj_K_M Perfect! Happy to help, feel free to mark as solved :wink:

1 Like