Hyperlink objects with raycaster and camera

Good afternoon evereyone,

Brief disclaimer, I have some experience 3d programming but non whatsoever with javascript so sorry in advance if this is an easy question. I whould like to trigger a hyperlink redirect when the camera intersects with a specific 3d object using the raycaster. I have found a bunch of threads concerning linking objects on events link clicks but nothing that answers my question.

Here’s my code:

/* eslint-disable unused-imports/no-unused-vars */
import React, { useEffect } from ‘react’;

import * as THREE from ‘three’;
// import { FBXLoader } from ‘three/examples/jsm/loaders/FBXLoader’;
// import { OBJLoader } from ‘three/examples/jsm/loaders/OBJLoader’;
// import { VOXLoader } from ‘three/examples/jsm/loaders/VOXLoader’;
import { GLTFLoader } from ‘three/examples/jsm/loaders/GLTFLoader.js’;

import SceneInit from ‘./lib/SceneInit’;

function objectClickHandler() {
window.open(‘http://www.pericror.com/’, ‘_blank’);
}

function Scene() {
useEffect(() => {
const test = new SceneInit(‘myThreeJsCanvas’);
test.initialize();
test.animate();
const mycolor = 0xff0000;
const video = document.getElementById(‘video’);
const spaceTexture = new THREE.VideoTexture(video);
//const spaceTexture = new THREE.TextureLoader().load(‘./assets/Experiment-21.avi’);
// create the video element
//assuming you have created a HTML video element with id=“video”

test.scene.background = spaceTexture;
video.play();
//const gridHelper = new THREE.GridHelper(200, 50);
//test.scene.add(gridHelper);
//test.scene.rotation.y = Math.PI / 8;

// const boxGeometry = new THREE.BoxGeometry(8, 8, 8);
// const boxMaterial = new THREE.MeshNormalMaterial();
// const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
// test.scene.add(boxMesh);



const geometry = new THREE.SphereGeometry( 3 , 3, 3 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const sphere = new THREE.Mesh( geometry, material );
sphere.position.set(3,3,-8.5);
sphere.callback = objectClickHandler;
test.scene.add(sphere);


const material1 = new THREE.MeshBasicMaterial( { color: 0xffffff } );
const sphere1 = new THREE.Mesh( geometry, material1 );
sphere1.position.set(4,1,-8.5);
test.scene.add(sphere1);

const material2 = new THREE.MeshBasicMaterial( { color: 0xffffff } );
const sphere2 = new THREE.Mesh( geometry, material2 );
sphere1.position.set(4,-3,-8.5);
test.scene.add(sphere1);



let loadedModel;
const glftLoader = new GLTFLoader();
glftLoader.load('./assets/room/try.gltf', (gltfScene) => {
  loadedModel = gltfScene;
  // console.log(loadedModel);

  gltfScene.scene.position.y = 0;
  gltfScene.scene.position.x = 5;
  gltfScene.scene.scale.set(2.8, 2.8, 2.8);
  test.scene.add(gltfScene.scene);
  const pointLight = new THREE.AmbientLight(0xffffff, 0.5);
  pointLight.position.set(20, 20, 20);
  test.scene.add(pointLight);
  test.scene.rotation.y = Math.PI + 0.3;
  test.scene.rotation.z = -0.18;

});








const animate = () => {
  if (loadedModel) {
    loadedModel.scene.rotation.x += 0.01;
    loadedModel.scene.rotation.y += 0.01;
    loadedModel.scene.rotation.z += 0.01;

  }
  requestAnimationFrame(animate);
};
//animate();

}, );

return (




);

}

export default Scene;

Does anybody have any idea where should I define the raycaster and where should I put the intersects code? How can I see intersects between an object and the camera?

Well, your code has its specifics and obviously I can’t test it or something like that, but some things should be clarified first:

  • What do you mean by “triggering a hyperlink redirect”? Do you want the said address to be executed automatically when the raycaster from the camera intersects the object, or only when actually clicking the object?
  • What exactly is the idea behind “intersect between object and camera”? Do you mean the intersect between a ray casting from the camera along the mouse direction, or a ray casting from the camera along the camera direction?

Other than that, as a general idea, you can “define” the raycaster anywhere in your code (preferably as a global variable so you can work with it in whatever functions you like), however you’ll usually cast a ray with it in either your render or your animation function, depending on the objective.

For example, if it’s a ray cast from the camera along the camera direction (and not the mouse direction), a function like this will do (here, I define / instantiate a raycaster every time the function runs, but usually you’d want that to happen just once, and cast of the ray in such moments instead):

      function raycast()
      {
        var cameradirection = new THREE.Vector3();
        camera.getWorldDirection(cameradirection);
        cameradirection.normalize();
        var cameraorigin = new THREE.Vector3();
        camera.getWorldPosition(cameraorigin);
        var raycaster = new THREE.Raycaster(cameraorigin, cameradirection);
        var intersects = raycaster.intersectObjects([yourobjecthere], false);
        if (intersects.length) {/* do stuff here, e.g.*/ console.log(intersects[0].distance);};
      };

Now, if you add raycast(); in your animate() function (a renderer.render(scene, camera); should typically exist there as well), the console output will happen each time a frame from the animation is “played” and the ray cast from the camera along its direction intersects the object.

On the other hand, if the ray is cast from the camera along the “mouse direction”, a code like in the example from the Raycaster page in the manual will do. Obviously, you’d have to adjust what happens if the .length of the intersect points array is greater than 0 (aka one or more intersects with the object exist), but that’s related to the hyperlink question above.

Note: Along the camera direction means towards the point the camera is “looking at” (hence computing the camera direction in that case), while along the mouse direction means towards the point where the mouse cursor is (hence the presence of the pointer vector that holds the mouse’s X and Y in normalized device coordinates).