| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Metallic Infinity Möbius Strip</title> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| | <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script> |
| | <style> |
| | body { |
| | margin: 0; |
| | overflow: hidden; |
| | background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); |
| | } |
| | #info { |
| | position: absolute; |
| | bottom: 20px; |
| | width: 100%; |
| | text-align: center; |
| | color: white; |
| | font-family: 'Arial', sans-serif; |
| | pointer-events: none; |
| | text-shadow: 0 0 5px rgba(0,0,0,0.5); |
| | } |
| | .title { |
| | position: absolute; |
| | top: 20px; |
| | width: 100%; |
| | text-align: center; |
| | color: white; |
| | font-family: 'Arial', sans-serif; |
| | font-size: 2.5rem; |
| | font-weight: bold; |
| | text-shadow: 0 0 15px rgba(255, 215, 0, 0.8); |
| | } |
| | .controls { |
| | position: absolute; |
| | top: 80px; |
| | right: 20px; |
| | background: rgba(0,0,0,0.5); |
| | padding: 15px; |
| | border-radius: 10px; |
| | color: white; |
| | font-family: 'Arial', sans-serif; |
| | z-index: 100; |
| | backdrop-filter: blur(5px); |
| | border: 1px solid rgba(255, 215, 0, 0.3); |
| | } |
| | .control-group { |
| | margin-bottom: 12px; |
| | } |
| | label { |
| | display: block; |
| | margin-bottom: 5px; |
| | color: #ffd700; |
| | } |
| | input[type="range"] { |
| | -webkit-appearance: none; |
| | width: 100%; |
| | height: 6px; |
| | background: rgba(255, 215, 0, 0.2); |
| | border-radius: 3px; |
| | outline: none; |
| | } |
| | input[type="range"]::-webkit-slider-thumb { |
| | -webkit-appearance: none; |
| | width: 16px; |
| | height: 16px; |
| | background: #ffd700; |
| | border-radius: 50%; |
| | cursor: pointer; |
| | } |
| | button { |
| | transition: all 0.3s ease; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <div class="title">Metallic Infinity Möbius Strip</div> |
| | <div class="controls"> |
| | <div class="control-group"> |
| | <label for="rotationSpeed">Rotation Speed</label> |
| | <input type="range" id="rotationSpeed" min="0" max="0.02" step="0.001" value="0.005"> |
| | </div> |
| | <div class="control-group"> |
| | <label class="flex items-center"> |
| | <input type="checkbox" id="wireframe" class="mr-2 h-4 w-4 accent-yellow-400"> |
| | <span>Show Wireframe</span> |
| | </label> |
| | </div> |
| | <div class="flex space-x-2 mt-4"> |
| | <button id="pauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white px-4 py-2 rounded-lg font-medium shadow-md hover:shadow-lg transform hover:-translate-y-0.5 transition-all"> |
| | Pause |
| | </button> |
| | </div> |
| | </div> |
| | <div id="info">Drag to rotate | Scroll to zoom</div> |
| | <script> |
| | |
| | const scene = new THREE.Scene(); |
| | const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
| | const renderer = new THREE.WebGLRenderer({ antialias: true }); |
| | renderer.setSize(window.innerWidth, window.innerHeight); |
| | renderer.setPixelRatio(window.devicePixelRatio); |
| | renderer.shadowMap.enabled = true; |
| | renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
| | document.body.appendChild(renderer.domElement); |
| | |
| | |
| | const createInfinityMobiusStrip = (radius = 1, tubeRadius = 0.3, radialSegments = 128, tubularSegments = 128) => { |
| | const geometry = new THREE.BufferGeometry(); |
| | const vertices = []; |
| | const indices = []; |
| | const normals = []; |
| | const uvs = []; |
| | |
| | for (let i = 0; i <= radialSegments; i++) { |
| | const v = i / radialSegments * Math.PI * 2; |
| | |
| | for (let j = 0; j <= tubularSegments; j++) { |
| | const u = j / tubularSegments * Math.PI * 4; |
| | |
| | |
| | const scale = 1.5; |
| | const x = scale * Math.cos(u) / (1 + Math.sin(u)*Math.sin(u)); |
| | const y = scale * Math.sin(u) * Math.cos(u) / (1 + Math.sin(u)*Math.sin(u)); |
| | |
| | |
| | const nx = x + tubeRadius * Math.cos(v/2) * Math.cos(u); |
| | const ny = y + tubeRadius * Math.cos(v/2) * Math.sin(u); |
| | const nz = tubeRadius * Math.sin(v/2); |
| | |
| | vertices.push(nx, ny, nz); |
| | |
| | |
| | const normal = new THREE.Vector3( |
| | Math.cos(u) * Math.cos(v/2), |
| | Math.sin(u) * Math.cos(v/2), |
| | Math.sin(v/2) |
| | ).normalize(); |
| | normals.push(normal.x, normal.y, normal.z); |
| | |
| | uvs.push(j / tubularSegments, i / radialSegments); |
| | } |
| | } |
| | |
| | |
| | for (let i = 0; i < radialSegments; i++) { |
| | for (let j = 0; j < tubularSegments; j++) { |
| | const a = i * (tubularSegments + 1) + j; |
| | const b = a + tubularSegments + 1; |
| | const c = a + 1; |
| | const d = b + 1; |
| | |
| | indices.push(a, b, d); |
| | indices.push(a, d, c); |
| | } |
| | } |
| | |
| | geometry.setIndex(indices); |
| | geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); |
| | geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3)); |
| | geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)); |
| | |
| | return geometry; |
| | }; |
| | |
| | |
| | const material = new THREE.MeshPhysicalMaterial({ |
| | color: 0xffd700, |
| | metalness: 1.0, |
| | roughness: 0.1, |
| | clearcoat: 1.0, |
| | clearcoatRoughness: 0.05, |
| | ior: 2.5, |
| | specularIntensity: 1.5, |
| | envMapIntensity: 2.0, |
| | emissive: 0xffcc00, |
| | emissiveIntensity: 0.2, |
| | side: THREE.DoubleSide, |
| | }); |
| | |
| | const wireframeMaterial = new THREE.MeshBasicMaterial({ |
| | color: 0xffffff, |
| | wireframe: true, |
| | transparent: true, |
| | opacity: 0.3 |
| | }); |
| | |
| | |
| | const mobiusStrip = new THREE.Mesh(createInfinityMobiusStrip(2, 0.4, 128, 128), material); |
| | scene.add(mobiusStrip); |
| | mobiusStrip.rotation.x = Math.PI / 4; |
| | mobiusStrip.rotation.y = Math.PI / 4; |
| | |
| | |
| | const wireframe = new THREE.Mesh(mobiusStrip.geometry, wireframeMaterial); |
| | scene.add(wireframe); |
| | wireframe.visible = false; |
| | |
| | |
| | const ambientLight = new THREE.AmbientLight(0x404040, 0.3); |
| | scene.add(ambientLight); |
| | |
| | |
| | const directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.8); |
| | directionalLight1.position.set(2, 2, 2); |
| | directionalLight1.castShadow = true; |
| | directionalLight1.shadow.mapSize.width = 2048; |
| | directionalLight1.shadow.mapSize.height = 2048; |
| | scene.add(directionalLight1); |
| | |
| | |
| | const directionalLight2 = new THREE.DirectionalLight(0xff9d00, 0.6); |
| | directionalLight2.position.set(-1, -1, -1); |
| | scene.add(directionalLight2); |
| | |
| | |
| | const pointLight = new THREE.PointLight(0xffcc00, 2, 15); |
| | pointLight.position.set(3, 5, 0); |
| | pointLight.castShadow = true; |
| | scene.add(pointLight); |
| | |
| | |
| | const pointLight2 = new THREE.PointLight(0xff6600, 1.5, 15); |
| | pointLight2.position.set(-3, -5, 0); |
| | scene.add(pointLight2); |
| | |
| | |
| | camera.position.set(0, 3, 10); |
| | camera.lookAt(0, 0, 0); |
| | |
| | |
| | const controls = new THREE.OrbitControls(camera, renderer.domElement); |
| | controls.enableDamping = true; |
| | controls.dampingFactor = 0.05; |
| | controls.screenSpacePanning = false; |
| | controls.minDistance = 5; |
| | controls.maxDistance = 20; |
| | controls.maxPolarAngle = Math.PI * 0.9; |
| | controls.minPolarAngle = Math.PI * 0.1; |
| | |
| | |
| | const cubeTextureLoader = new THREE.CubeTextureLoader(); |
| | const envMap = cubeTextureLoader.load([ |
| | 'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_px.jpg', |
| | 'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_nx.jpg', |
| | 'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_py.jpg', |
| | 'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_ny.jpg', |
| | 'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_pz.jpg', |
| | 'https://threejs.org/examples/textures/cube/MilkyWay/dark-s_nz.jpg' |
| | ]); |
| | scene.background = envMap; |
| | scene.environment = envMap; |
| | material.envMap = envMap; |
| | |
| | |
| | const rotationSpeedInput = document.getElementById('rotationSpeed'); |
| | const wireframeCheckbox = document.getElementById('wireframe'); |
| | const pauseBtn = document.getElementById('pauseBtn'); |
| | |
| | let isPaused = false; |
| | let autoRotateSpeed = 0.005; |
| | |
| | wireframeCheckbox.addEventListener('change', function() { |
| | wireframe.visible = this.checked; |
| | }); |
| | |
| | pauseBtn.addEventListener('click', function() { |
| | isPaused = !isPaused; |
| | pauseBtn.textContent = isPaused ? 'Play' : 'Pause'; |
| | pauseBtn.classList.toggle('bg-yellow-700'); |
| | pauseBtn.classList.toggle('bg-yellow-600'); |
| | }); |
| | |
| | |
| | let time = 0; |
| | function animate() { |
| | requestAnimationFrame(animate); |
| | time += 0.01; |
| | |
| | if (!isPaused) { |
| | |
| | mobiusStrip.rotation.x += autoRotateSpeed * 0.8; |
| | mobiusStrip.rotation.y += autoRotateSpeed * 0.5; |
| | mobiusStrip.rotation.z += autoRotateSpeed * 0.2; |
| | |
| | |
| | pointLight.intensity = 2 + Math.sin(time) * 0.5; |
| | pointLight2.intensity = 1.5 + Math.cos(time * 0.8) * 0.3; |
| | |
| | |
| | autoRotateSpeed = parseFloat(rotationSpeedInput.value); |
| | |
| | wireframe.rotation.copy(mobiusStrip.rotation); |
| | } |
| | |
| | controls.update(); |
| | renderer.render(scene, camera); |
| | } |
| | |
| | |
| | window.addEventListener('resize', function() { |
| | camera.aspect = window.innerWidth / window.innerHeight; |
| | camera.updateProjectionMatrix(); |
| | renderer.setSize(window.innerWidth, window.innerHeight); |
| | }); |
| | |
| | animate(); |
| | </script> |
| | <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=etnom/mobius" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> |
| | </html> |