Three.js Points对象Raycasting拾取与指针位置不匹配问题
Three.js Points 射线拾取位置不匹配问题修复
核心问题原因
代码存在3处直接导致拾取错位的逻辑错误:
- 指针归一化基准错误:
pointermove事件中用window.innerWidth/innerHeight做坐标换算,但实际渲染canvas并未占满整个窗口,计算出的NDC坐标和相机实际视口不匹配,从输入源就产生了偏移。 - 拾取判定和渲染逻辑不对齐:自定义了顶点
size属性修改点大小,但没有给射线拾取器设置对应匹配的判定阈值,默认拾取范围和屏幕上实际渲染的粒子大小不匹配。 - 状态初始值bug:
INTERSECTED初始值为undefined,第一次触发选中时会访问数组的undefined索引,写入无效数据,同时导致后续选中/未选中的状态切换逻辑异常。
修复方案
- 指针坐标换算统一以canvas的实际布局位置、尺寸为基准,不再使用window全局尺寸
- 给射线拾取器的Points类型设置匹配粒子尺寸的判定阈值,和渲染逻辑对齐
- 修正
INTERSECTED初始值为null,补全边界判断,避免访问无效数组索引 - 点材质开启
sizeAttenuation,保证自定义的size属性计算逻辑和Three.js内置的透视衰减逻辑一致,避免渲染大小和3D空间判定大小脱节。
修复后关键代码
// 修正状态初始值 let particles, raycaster, INTERSECTED = null, pointer raycaster = new THREE.Raycaster() pointer = new THREE.Vector2(99999, 99999) // 绑定canvas引用,修正指针坐标计算 const canvas = document.querySelector('#c') document.addEventListener('pointermove', (event) => { const rect = canvas.getBoundingClientRect() pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 }) document.addEventListener('pointerout', () => pointer.set(99999, 99999)) // 设置和粒子尺寸匹配的拾取阈值 raycaster.params.Points.threshold = PARTICLE_SIZE / 2 // 修正材质配置,对齐尺寸计算逻辑 let material = new THREE.PointsMaterial({ color: 0xffffff, transparent: true, depthTest: true, depthWrite: false, size: PARTICLE_SIZE, sizeAttenuation: true }) material.onBeforeCompile = shader => { shader.vertexShader = shader.vertexShader.replace( 'uniform float size;', 'attribute float size;' ) }
替换对应代码后,射线拾取结果会和指针实际位置完全匹配,粒子选中放大的交互逻辑也能正常运行。
内容的提问来源于stack exchange,提问作者Simone Tasca
相关产品推荐
相关产品推荐

