Mouse Normalization and Raycasting

New to web development and threejs, trying to use a raycaster to show a point on my scene where the mouse intersects a plane.

My intersection point always seems to be just a little bit off. see below and codesandbox link:

https://codesandbox.io/p/sandbox/3jsplay-6k29lv?file=%2Fsrc%2Findex.html

I think the issue is due to the small white outline around my scene. Is this correct? If so, how can correctly calculate my mouse normalization for correct raycasting?

Thanks!

Yes. Its both the small border, and also that your viewport is in a scroll pane.

The default raycasting sample code assumes a full window setup.

To fix it, you’ll need to get the cursor position in canvas space and work with that.

From one of @Mugen87 's stackoverflow posts:

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;

const intersects = raycaster.intersectObject(plane);

recreate
const intersects = raycaster.intersectObject(plane, false);

ill show you my code, how to using mouse as a raycaster with ecsy method

import { System } from "three/examples/jsm/libs/ecsy.module.js";
import { RaycasterComponent } from "../components/RaycasterComponent";
import * as THREE from "three";
import { Object3D } from "../components/Object3DComponent";
import { ButtonComponent } from "../components/ButtonComponent";

export class RaycasterSystem extends System {
    init() {
        this.mouse = new THREE.Vector2();

        window.addEventListener('mousemove', (event) => {
            this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        }, false);
    }

    execute() {
        this.queries.raycaster.results.forEach(entity => {
            const component = entity.getComponent(RaycasterComponent);
            if (!component || !component.raycaster || !component.camera) {
                console.error("RaycasterComponent atau komponennya tidak valid.");
                return;
            }

            const raycaster = component.raycaster;
            const camera = component.camera;

            const object = entity.getComponent(Object3D).object;
            if (!object) {
                console.error("Object3D tidak ditemukan atau belum diinisialisasi pada entitas.");
                return;
            }

            raycaster.setFromCamera(this.mouse, camera);
            const intersections = raycaster.intersectObject(object, false);

            if (intersections.length > 0) {
                const intersection = intersections[0];
                this.#HoverInteraction(entity);
            }
        });
    }

    #HoverInteraction(entity) {
        if (entity.hasComponent(ButtonComponent)) {
            if (entity.hasComponent(ButtonComponent)) {
                const button = entity.getMutableComponent(ButtonComponent);
                if (button.currState != 'pressed') {
                    button.currState = 'hovered';
                    window.addEventListener('click', () => {
                        button.currState = 'pressed';
                    }, false);
                }
            }
        }
    }
}

RaycasterSystem.queries = {
    raycaster: {
        components: [RaycasterComponent]
    }
};

1 Like