Last active
May 30, 2025 08:54
-
-
Save ShaneBrumback/f21d6b15d295e8b07c55fb2ba7e472c2 to your computer and use it in GitHub Desktop.
Three.js Examples Programming 3D Spheres With Collision Events
This file contains hidden or 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> | |
| <meta charset="utf-8"> | |
| <title>Three.js Examples - Programming 3D Sphere Collision Events</title> | |
| <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> | |
| </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 type="module"> | |
| // Set up the scene, camera, and renderer | |
| const scene = new THREE.Scene(); | |
| // Set up the camera | |
| const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| camera.position.z = 50; | |
| camera.position.y = 25; | |
| // 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 the spotlight with shadows | |
| const spotLight = new THREE.SpotLight(0xffffff); | |
| spotLight.position.set(10, 20, 30); | |
| spotLight.castShadow = true; | |
| scene.add(spotLight); | |
| // Add ambient light | |
| const ambientLight = new THREE.AmbientLight(0xffffff, 1); | |
| scene.add(ambientLight); | |
| // Create a spotlight | |
| const spotlight = new THREE.SpotLight(0xffffff); | |
| spotlight.position.set(200, 200, 0); // Adjust the position to cover the plane | |
| spotlight.target.position.set(0, 0, 0); | |
| spotlight.castShadow = true; | |
| // Configure spotlight properties | |
| spotlight.intensity = 10; // Adjust the intensity as needed | |
| spotlight.angle = Math.PI / 2; // Set the spotlight cone angle to cover the entire plane | |
| spotlight.penumbra = 0; // Set the spotlight softness to 0 for a sharp shadow | |
| spotlight.decay = 2; // Set the spotlight decay | |
| // Enable shadows for the spotlight | |
| spotlight.castShadow = true; | |
| spotlight.shadow.mapSize.width = 2048; // Increase the shadow map size | |
| spotlight.shadow.mapSize.height = 2048; // Increase the shadow map size | |
| spotlight.shadow.camera.near = 1; // Adjust the near value as needed | |
| spotlight.shadow.camera.far = 500; // Adjust the far value as needed | |
| spotlight.shadow.camera.fov = 120; // Adjust the field of view to cover the entire plane | |
| // Add the spotlight to the scene | |
| scene.add(spotlight); | |
| scene.add(spotlight.target); | |
| // Create the plane's geometry | |
| const planeGeometry = new THREE.PlaneGeometry(100, 100); | |
| // Create a material to apply to the plane | |
| const planeMaterial = new THREE.MeshPhongMaterial({ | |
| color: 'blue', | |
| side: THREE.DoubleSide, | |
| depthWrite: false | |
| }); | |
| // Create the plane | |
| const plane = new THREE.Mesh(planeGeometry, planeMaterial); | |
| plane.rotation.x = Math.PI / 2; | |
| plane.position.y = -3; | |
| plane.receiveShadow = true; | |
| plane.castShadow = true; | |
| // Enable shadows for the plane | |
| plane.receiveShadow = true; | |
| // Add the plane to the scene | |
| scene.add(plane); | |
| //Add a grid helper | |
| var grid = new THREE.GridHelper(100, 30); | |
| grid.renderOrder = 0; | |
| grid.position.y = -3; | |
| scene.add(grid); | |
| // Set up the collision detection variables | |
| const collisionDistance = 10; | |
| // Set up the spheres | |
| const ballGeometry = new THREE.SphereGeometry(5, 32, 32); | |
| const ballMaterial = new THREE.MeshPhongMaterial({ color: 'white' }); | |
| const ballMaterial2 = new THREE.MeshPhongMaterial({ color: 'white' }); | |
| // Set shadow properties for the sphere materials | |
| ballMaterial.receiveShadow = true; | |
| ballMaterial.castShadow = true; | |
| ballMaterial2.receiveShadow = true; | |
| ballMaterial2.castShadow = true; | |
| const ball1 = new THREE.Mesh(ballGeometry, ballMaterial); | |
| ball1.name = 'ball1'; | |
| ball1.castShadow = true; // Enable shadows for ball1 | |
| const ball2 = new THREE.Mesh(ballGeometry, ballMaterial2); | |
| ball2.name = 'ball2'; | |
| ball2.castShadow = true; // Enable shadows for ball2 | |
| // Set the initial positions of the spheres | |
| ball1.position.set(-5, 1, 0); | |
| ball2.position.set(5, 1, 0); | |
| // Add the spheres to the scene | |
| scene.add(ball1); | |
| scene.add(ball2); | |
| // Add camera controls | |
| const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
| controls.dampingFactor = 0.1; // Reduce camera damping for smoother movement | |
| controls.autoRotate = true; // Make the camera rotate sinuously around the sphe | |
| // Set up the movement variables | |
| const ballSpeed = 2; | |
| let ball1Direction = new THREE.Vector3(Math.random() * ballSpeed, 0, Math.random() * ballSpeed); | |
| let ball2Direction = new THREE.Vector3(Math.random() * ballSpeed, 0, Math.random() * ballSpeed); | |
| // Set up the update loop | |
| function update() { | |
| // Update the sphere positions | |
| ball1.position.add(ball1Direction); | |
| ball2.position.add(ball2Direction); | |
| controls.update(); | |
| // Check if the spheres are colliding | |
| const distance = ball1.position.distanceTo(ball2.position); | |
| if (distance <= 10) { | |
| // Generate random direction changes for both balls | |
| const randomDirectionChange = () => Math.random() * 2 - 1; // Random value between -1 and 1 | |
| // Apply random direction changes to ball1 | |
| ball1Direction.x = -ball1Direction.x + randomDirectionChange(); | |
| ball1Direction.z = -ball1Direction.z + randomDirectionChange(); | |
| // Apply random direction changes to ball2 | |
| ball2Direction.x = -ball2Direction.x + randomDirectionChange(); | |
| ball2Direction.z = -ball2Direction.z + randomDirectionChange(); | |
| const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0x800080, 0xffa500]; // Red, Green, Blue, White, Purple, Orange | |
| // Set random color for ball1 | |
| const randomColorIndex1 = Math.floor(Math.random() * colors.length); | |
| const randomColor1 = colors[randomColorIndex1]; | |
| ball1.material.color.set(randomColor1); | |
| // Remove the selected color from the array | |
| colors.splice(randomColorIndex1, 1); | |
| // Set random color for ball2 | |
| const randomColorIndex2 = Math.floor(Math.random() * colors.length); | |
| const randomColor2 = colors[randomColorIndex2]; | |
| ball2.material.color.set(randomColor2); | |
| } | |
| // Check if the balls are close to the edge of the plane and change their direction if they are | |
| const randomDirectionChange = () => Math.random() * 2 - 1; // Random value between -1 and 1 | |
| const edgeThreshold = collisionDistance; // Adjust this value as needed | |
| if (Math.abs(ball1.position.x) > plane.geometry.parameters.width / 2 - edgeThreshold) { | |
| ball1Direction.x = -Math.sign(ball1.position.x) + randomDirectionChange(); | |
| ball1Direction.normalize(); // Normalize the direction vector | |
| } | |
| if (Math.abs(ball2.position.x) > plane.geometry.parameters.width / 2 - edgeThreshold) { | |
| ball2Direction.x = -Math.sign(ball2.position.x) + randomDirectionChange(); | |
| ball2Direction.normalize(); // Normalize the direction vector | |
| } | |
| if (Math.abs(ball1.position.z) > plane.geometry.parameters.height / 2 - edgeThreshold) { | |
| ball1Direction.z = -Math.sign(ball1.position.z) + randomDirectionChange(); | |
| ball1Direction.normalize(); // Normalize the direction vector | |
| } | |
| if (Math.abs(ball2.position.z) > plane.geometry.parameters.height / 2 - edgeThreshold) { | |
| ball2Direction.z = -Math.sign(ball2.position.z) + randomDirectionChange(); | |
| ball2Direction.normalize(); // Normalize the direction vector | |
| } | |
| // Render the scene | |
| renderer.render(scene, camera); | |
| // Update again on the next frame | |
| requestAnimationFrame(update); | |
| } | |
| update(); | |
| // Add window resize event listener | |
| window.addEventListener('resize', onWindowResize); | |
| // Function to handle window resize event | |
| 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