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
相关产品推荐
相关产品推荐

