I'm trying to implement raycasting for the Points object.
The problem is that the raycaster selection doesn't match the pointer position.
I took as reference these 2 examples from three: webgl_interactive_raycasting_points webgl_interactive_points but i can't still figure out what i am doing wrong.
here is my code pen: https://codepen.io/simone-tasca/pen/YzapWMN
let scene = new THREE.Scene()
const near = 0.1
const far = 5000
const fov = 30
const aspect = window.innerWidth / window.innerHeight
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.set(-1.25, 0.8, -1.9)
let ambientLight = new THREE.AmbientLight('white', 1)
scene.add(ambientLight)
// POINTS CONTAINER ==========================================================
const worldCenter = [-1.07, 0, -6.85]
const rotationCorrection = [0.781, 4.305, 0.28]
const worldMap = new THREE.Object3D()
worldMap.position.set(...worldCenter)
worldMap.rotation.set(...rotationCorrection)
scene.add(worldMap)
// THREE.Points =============================================================
const PARTICLE_SIZE = 0.1
let particles, raycaster, INTERSECTED, pointer
let vertices = []
let names = []
let sizes = []
prepareData(sampleData).forEach(coords => {
vertices.push(...coords)
sizes.push(PARTICLE_SIZE)
})
const geometry = new THREE.BufferGeometry()
geometry.attributes.position = new THREE.Float32BufferAttribute(vertices, 3)
geometry.attributes.size = new THREE.Float32BufferAttribute(sizes, 1)
let material = new THREE.PointsMaterial({
color: 0xffffff,
transparent: true,
depthTest: true,
depthWrite: false
})
material.onBeforeCompile = shader => {
shader.vertexShader =
shader.vertexShader.replace('uniform float size;', 'attribute float size;')
}
particles = new THREE.Points(geometry, material)
worldMap.add(particles)
// RAYCASTER =============================================================
raycaster = new THREE.Raycaster()
pointer = new THREE.Vector2(99999, 99999)
document.addEventListener('pointermove', (event) => {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;
})
document.addEventListener('pointerout', () => pointer.set(99999, 99999))
// RENDERING ==================================================================
let canvas = document.querySelector('#c')
const renderer = new THREE.WebGLRenderer({
canvas: canvas, alpha: false
})
function resizeRendererToDisplaySize(renderer) {
const width = canvas.clientWidth
const height = canvas.clientHeight
const needResize = canvas.width !== width || canvas.height !== height
if (needResize) renderer.setSize(width, height, false)
return needResize
}
function render(time) {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement
camera.aspect = canvas.clientWidth / canvas.clientHeight
camera.updateProjectionMatrix()
}
const geometry = particles.geometry
const attributes = geometry.attributes
raycaster.setFromCamera(pointer, camera)
let intersects = raycaster.intersectObject(particles)
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].index) {
console.log(intersects[0].index)
attributes.size.array[INTERSECTED] = PARTICLE_SIZE
INTERSECTED = intersects[0].index
attributes.size.array[INTERSECTED] = PARTICLE_SIZE * 3
attributes.size.needsUpdate = true
}
} else if (INTERSECTED !== null) {
attributes.size.array[INTERSECTED] = PARTICLE_SIZE
attributes.size.needsUpdate = true
INTERSECTED = null
}
renderer.render(scene, camera)
requestAnimationFrame(render)
}
render()
You're very close. The only thing missing is to declare how "wide" you want your raycaster to be. Add this line after initiating the raycaster:
raycaster = new THREE.Raycaster()
raycaster.params.Points.threshold = 0.05;
The threshold is by default 1 unit wide. Think of this as painting with a very broad brush, the first particle you'll hit may not be the closest to your mouse pointer. So when you get intersects[0].index
, it's going to be the first particle you hit with that broad ray (closest to the camera), not the closest one to your mouse. If you declare a narrower threshold, your ray will be more precise and you'll get more accurate results.