You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

three.js Raycaster功能异常如何修复?立方体随光标偏移过度

Three.js Raycaster 立方体光标偏移问题解决

我创建了一个新的three.js项目,按照官方文档添加Raycaster并绑定了一个立方体,原本期望立方体随光标位置移动,但实际立方体向两侧偏移过度。尝试将event.clientX替换为event.pageX,问题仍未解决。

以下是我的代码:

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import {MeshBasicMaterial} from "three";

const canvas = document.querySelector('#c')
const renderer = new THREE.WebGLRenderer({antialias: true, canvas})
const camera = new THREE.PerspectiveCamera(75, 2, 0.1, 1000)
const controls = new OrbitControls(camera, renderer.domElement)
controls.addEventListener('change', camera)
camera.position.z = 3.5;
camera.position.x = 3.5;
camera.position.y = 5;
camera.lookAt(-0.5,-0.5,-0.5)
controls.target = new THREE.Vector3(-0.5, -0.5, -0.5)

const scene = new THREE.Scene()
scene.background = new THREE.Color(0x36393e)
const loader = new THREE.TextureLoader()
let rollOverMaterial, rollOverMesh, line;
let planes = [];
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector3()

init()
render()

function init() {

    const gridHelper = new THREE.GridHelper(16, 16, 0xffffff)
    scene.add(gridHelper)
    gridHelper.position.y = -0.5
    gridHelper.position.x = -0.5
    gridHelper.position.z = -0.5

    const rollOverGeo = new THREE.BoxGeometry( 1, 0.999, 1 );
    rollOverMaterial = new THREE.MeshBasicMaterial( { color: 0x0077be, opacity: 0.5, transparent: true } );
    rollOverMesh = new THREE.Mesh( rollOverGeo, rollOverMaterial );
    const edges = new THREE.EdgesGeometry(rollOverGeo)
    line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({color: 0x6b6b6b}))

    scene.add( rollOverMesh, line );

    const planeGeometry = new THREE.PlaneGeometry(16, 16)
    planeGeometry.rotateX( - Math.PI / 2 );

    const plane = new THREE.Mesh( planeGeometry, new THREE.MeshBasicMaterial( { visible: false } ) );
    scene.add( plane );
    plane.position.x = -0.5
    plane.position.y = -1.5
    plane.position.z = -0.5
    planes.push(plane)

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );

    document.addEventListener( 'pointermove', onMouseMove );

    window.addEventListener( 'resize', onWindowResize );
}

function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );

    render();

}

function onMouseMove( event ) {

    pointer.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
    pointer.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
    pointer.z = 1;
    let mouse = [event.clientX, event.clientY];
    raycaster.setFromCamera(pointer, camera);
    let intersects = raycaster.intersectObjects(planes, true);

    if ( intersects.length > 0 ) {

        const intersect = intersects[ 0 ];

        rollOverMesh.position.copy( intersect.point ).add( intersect.face.normal );
        rollOverMesh.position.y += 0.5
        line.position.copy( intersect.point ).add( intersect.face.normal );
        line.position.y += 0.5

        render();

    }

}

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

问题原因与修正方案

1. 不可见平面位置偏移导致交点错位

你给用于射线检测的不可见平面设置了position.x = -0.5和position.z = -0.5,这会让平面和网格辅助线不对齐,射线检测的交点坐标自然会偏移。修改平面位置,只保留y方向的偏移:

// 原代码
plane.position.x = -0.5
plane.position.y = -1.5
plane.position.z = -0.5
// 修改后
plane.position.y = -1.5

2. 交点坐标未对齐网格格点

原代码直接使用intersect.point加法线的方式计算位置,没有考虑网格的格子大小(每个格子为1单位),导致立方体位置偏移。需要将交点坐标取整对齐到网格格点:

function onMouseMove( event ) {
    pointer.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
    pointer.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
    raycaster.setFromCamera(pointer, camera);
    let intersects = raycaster.intersectObjects(planes, true);

    if ( intersects.length > 0 ) {
        const intersect = intersects[ 0 ];
        // 对齐到1x1的网格格点
        const snapX = Math.floor(intersect.point.x) + 0.5;
        const snapZ = Math.floor(intersect.point.z) + 0.5;
        // 设置立方体位置,使其底部贴合网格(网格y=-0.5,立方体高度0.999,所以y=-0.5 + 0.5)
        rollOverMesh.position.set(snapX, 0, snapZ);
        line.position.copy(rollOverMesh.position);
    }
}

3. 移除冗余代码

  • 去掉pointer.z = 1:raycaster.setFromCamera只需要二维归一化坐标,z值会自动处理
  • 修正controls.addEventListener('change', camera):这里绑定的事件处理函数错误,改为controls.addEventListener('change', render),或者依靠requestAnimationFrame循环渲染可以直接移除这行
  • 去掉onMouseMove里的render()调用:requestAnimationFrame已经在持续渲染,无需重复调用

完成以上修改后,立方体就能准确跟随光标位置移动,和网格辅助线对齐了。


内容的提问来源于stack exchange,提问作者cha0ticCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 12:48:09