Last active
March 19, 2024 05:17
-
-
Save ShaneBrumback/aa9d8665166983409ca786f087c11bf6 to your computer and use it in GitHub Desktop.
Three.js Exmaples - Selecting a 3D Cube with a Raycaster
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--//////////////////////////////////////////////////////////////////////////////////////// | |
/// /// | |
/// Example Using Three.js Library, HTML, CSS & JavaScript /// | |
// 3D Interactive Web Apps & Games 2021-2024 /// | |
/// Contact Shane Brumback https://www.shanebrumback.com /// | |
/// Send a message if you have questions about this code /// | |
/// I am a freelance developer. I develop any and all web. /// | |
/// Apps Websites 3D 2D CMS Systems etc. Contact me anytime :) /// | |
/// /// | |
////////////////////////////////////////////////////////////////////////////////////////////--> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Three.js Examples - Selecting 3D Cube with Raycaster</title> | |
</head> | |
<body> | |
<script src="https://cdn.jsdelivr.net/npm/three@latest/build/three.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/three@latest/examples/js/controls/OrbitControls.js"></script> | |
<script> | |
// Set up the Three.js scene | |
const scene = new THREE.Scene(); | |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
camera.position.x = 5; // Move the camera further back along the x-axis | |
camera.position.y = 5; // Move the camera further back along the y-axis | |
camera.position.z = 5; // Move the camera further back along the z-axis | |
// Create a variable to control the rotation speed of the cube | |
const rotationSpeed = 0.01; | |
//Set up the renderer | |
let renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); | |
renderer.setPixelRatio(window.devicePixelRatio); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
renderer.toneMapping = THREE.ReinhardToneMapping; | |
renderer.shadowMap.enabled = true; | |
renderer.shadowMap.type = THREE.PCFSoftShadowMap; | |
renderer.domElement.id = 'renderer'; | |
renderer.setClearColor(0x000000, 1); // Set background color to black | |
renderer.domElement.style.position = 'fixed'; | |
renderer.domElement.style.zIndex = '-1'; | |
renderer.domElement.style.left = '0'; | |
renderer.domElement.style.top = '0'; | |
document.body.appendChild(renderer.domElement); | |
// Create an ambient light and add it to the scene | |
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // Soft white light with intensity 0.5 | |
scene.add(ambientLight); | |
// Create a directional light and add it to the scene | |
const light = new THREE.DirectionalLight(0xffffff, 1); | |
light.position.set(5, 10, 5); // Set the position of the light | |
scene.add(light); | |
// Create a mesh and add it to the scene | |
const geometry = new THREE.BoxGeometry(); | |
// Set up the cube material with MeshPhongMaterial | |
const cubeMaterial = new THREE.MeshPhongMaterial({ | |
color: 0x0000ff, // Blue color of the cube | |
specular: 0xffffff, // Specular highlight color | |
shininess: 100, // Shininess of the material (higher value makes it more reflective) | |
}); | |
// Create a mesh with the cube geometry and the new material | |
const mesh = new THREE.Mesh(geometry, cubeMaterial); | |
scene.add(mesh); | |
// Add a grid to the scene | |
const gridHelper = new THREE.GridHelper(10, 10); | |
scene.add(gridHelper); | |
// Set up OrbitControls | |
const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
// Create a variable to track the selected state of the cube | |
let isCubeSelected = false; | |
// Create variables to store the starting position and target position of the cube | |
const startingPosition = mesh.position.clone(); | |
const maxDistance = 2; // Maximum distance from the camera to stop the movement | |
// Store the initial position of the cube for returning | |
const initialPosition = mesh.position.clone(); | |
// Add event listener for mouse clicks | |
document.addEventListener('click', function (event) { | |
// Calculate mouse position in normalized device coordinates | |
const mouse = new THREE.Vector2(); | |
mouse.x = (event.clientX / window.innerWidth) * 2 - 1; | |
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; | |
// Raycasting from camera to object | |
const raycaster = new THREE.Raycaster(); | |
raycaster.setFromCamera(mouse, camera); | |
const intersects = raycaster.intersectObject(mesh); | |
if (intersects.length > 0) { | |
// If the cube was previously selected, move it back to the absolute position (0, 0, 0) | |
if (isCubeSelected) { | |
isCubeSelected = false; | |
moveCube(initialPosition); | |
// Set the cube's color back to green | |
mesh.material.color.set(0x0000ff); | |
} else { | |
// If the cube was not previously selected, move it towards the camera | |
isCubeSelected = true; | |
const targetPosition = camera.position.clone(); | |
if (mesh.position.distanceTo(targetPosition) <= maxDistance) { | |
// If the cube is already close to the camera, move it back to the absolute position (0, 0, 0) | |
moveCube(initialPosition); | |
// Set the cube's color back to blue | |
mesh.material.color.set(0x0000ff); | |
} else { | |
// Move the cube to the target position | |
moveCube(targetPosition); | |
// Set the cube's color to green when it's selected | |
mesh.material.color.set(0x00ff00,); | |
} | |
} | |
} | |
}); | |
// Function to move the cube to the specified position | |
function moveCube(position) { | |
const duration = 1; // Duration of the movement in seconds | |
const startPosition = mesh.position.clone(); | |
let elapsed = 0; | |
function updatePosition() { | |
elapsed += 0.016; // Assuming 60 FPS, use delta time for more accurate timing | |
const t = elapsed / duration; | |
if (t < 1) { | |
const currentPosition = mesh.position.clone(); | |
const distanceToTarget = currentPosition.distanceTo(position); | |
// Stop moving if the distance to the target position is within the specified range | |
if (distanceToTarget <= 2) { // Check if the distance is less than or equal to 2 units | |
mesh.position.copy(currentPosition); | |
return; | |
} | |
mesh.position.lerpVectors(startPosition, position, t); | |
requestAnimationFrame(updatePosition); | |
} else { | |
mesh.position.copy(position); | |
// Check if the cube has reached the target position (center of the scene) | |
if (position.equals(initialPosition)) { | |
isCubeSelected = false; | |
controls.target.set(0, 0, 0); // Reset the controls target to the scene's origin | |
} | |
} | |
} | |
updatePosition(); | |
} | |
// Function to calculate the center of the cube's bounding box | |
function getBoundingBoxCenter(mesh) { | |
const box = new THREE.Box3().setFromObject(mesh); | |
const center = new THREE.Vector3(); | |
box.getCenter(center); | |
return center; | |
} | |
// Render loop | |
function animate() { | |
requestAnimationFrame(animate); | |
// Rotate the cube around its y-axis | |
mesh.rotation.y += rotationSpeed; | |
// Set the OrbitControls center to the center of the cube's bounding box | |
const center = getBoundingBoxCenter(mesh); | |
controls.target.copy(center); | |
renderer.render(scene, camera); | |
} | |
animate(); | |
// Adjust the camera aspect ratio and update renderer size on window resize | |
window.addEventListener("resize", onWindowResize, false); | |
function onWindowResize() { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment