kamaji-threejs.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Kamaji - Professional Three.js 3D</title> 7 <style> 8 body { 9 margin: 0; 10 padding: 0; 11 overflow: hidden; 12 font-family: Verdana, sans-serif; 13 background: #0a0a0a; 14 } 15 #container { 16 position: absolute; 17 top: 0; 18 left: 0; 19 width: 100%; 20 height: 100%; 21 display: flex; 22 justify-content: center; 23 align-items: center; 24 } 25 canvas { 26 border: 2px solid #ff6600; 27 border-radius: 8px; 28 box-shadow: 0 0 20px rgba(255, 102, 0, 0.3); 29 } 30 #info { 31 position: absolute; 32 top: 20px; 33 left: 50%; 34 transform: translateX(-50%); 35 color: #ff6600; 36 text-align: center; 37 text-shadow: 0 0 10px rgba(255, 102, 0, 0.5); 38 pointer-events: none; 39 z-index: 100; 40 } 41 h1 { 42 margin: 0; 43 font-size: 1.8rem; 44 } 45 .version { 46 color: #aaa; 47 font-size: 0.9rem; 48 margin-top: 0.5rem; 49 } 50 .controls { 51 color: #aaa; 52 font-size: 0.75rem; 53 margin-top: 0.5rem; 54 } 55 </style> 56 </head> 57 <body> 58 <div id="info"> 59 <h1>🔥 KAMAJI</h1> 60 <div class="version">Professional Three.js • PBR Materials</div> 61 <div class="controls">Drag to Rotate • Scroll to Zoom • Right-Click Pan</div> 62 </div> 63 <div id="container"></div> 64 65 <script type="importmap"> 66 { 67 "imports": { 68 "three": "https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.module.js", 69 "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.158.0/examples/jsm/" 70 } 71 } 72 </script> 73 74 <script type="module"> 75 import * as THREE from 'three'; 76 import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 77 78 // === SCENE SETUP === 79 const scene = new THREE.Scene(); 80 scene.background = new THREE.Color(0x0a0a0a); 81 scene.fog = new THREE.Fog(0x0a0a0a, 100, 300); 82 83 // === CAMERA === 84 const camera = new THREE.PerspectiveCamera( 85 50, 86 600 / 600, 87 0.1, 88 1000 89 ); 90 camera.position.set(0, 30, 180); 91 92 // === RENDERER === 93 const renderer = new THREE.WebGLRenderer({ 94 antialias: true, 95 alpha: false 96 }); 97 renderer.setSize(600, 600); 98 renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); 99 renderer.shadowMap.enabled = true; 100 renderer.shadowMap.type = THREE.PCFSoftShadowMap; 101 renderer.toneMapping = THREE.ACESFilmicToneMapping; 102 renderer.toneMappingExposure = 1.0; 103 document.getElementById('container').appendChild(renderer.domElement); 104 105 // === PROFESSIONAL THREE-POINT LIGHTING === 106 107 // Key Light (main directional) 108 const keyLight = new THREE.DirectionalLight(0xffffff, 0.9); 109 keyLight.position.set(-30, 50, 40); 110 keyLight.castShadow = true; 111 keyLight.shadow.mapSize.width = 2048; 112 keyLight.shadow.mapSize.height = 2048; 113 keyLight.shadow.camera.near = 0.5; 114 keyLight.shadow.camera.far = 200; 115 keyLight.shadow.camera.left = -50; 116 keyLight.shadow.camera.right = 50; 117 keyLight.shadow.camera.top = 50; 118 keyLight.shadow.camera.bottom = -50; 119 scene.add(keyLight); 120 121 // Fill Light (ambient hemisphere) 122 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6); 123 hemisphereLight.position.set(0, 50, 0); 124 scene.add(hemisphereLight); 125 126 // Rim/Back Light (separation) 127 const rimLight = new THREE.DirectionalLight(0x8899ff, 0.4); 128 rimLight.position.set(30, 30, -40); 129 scene.add(rimLight); 130 131 // === MATERIALS === 132 133 // Skin material (PBR) 134 const skinMaterial = new THREE.MeshStandardMaterial({ 135 color: 0xd4a574, 136 roughness: 0.6, 137 metalness: 0.0 138 }); 139 140 // Shirt material (blue fabric) 141 const shirtMaterial = new THREE.MeshStandardMaterial({ 142 color: 0x3a4f6b, 143 roughness: 0.8, 144 metalness: 0.0 145 }); 146 147 // Beard material (brown, rough) 148 const beardMaterial = new THREE.MeshStandardMaterial({ 149 color: 0x6b5638, 150 roughness: 0.9, 151 metalness: 0.0 152 }); 153 154 // Glasses material (black, glossy with transmission) 155 const glassesMaterial = new THREE.MeshPhysicalMaterial({ 156 color: 0x0a0a0a, 157 metalness: 0.0, 158 roughness: 0.1, 159 transparent: true, 160 transmission: 0.8, 161 opacity: 0.3, 162 reflectivity: 0.9, 163 clearcoat: 1.0, 164 clearcoatRoughness: 0.0 165 }); 166 167 // Glasses frame material (black, metallic) 168 const frameMaterial = new THREE.MeshStandardMaterial({ 169 color: 0x1a1a1a, 170 roughness: 0.3, 171 metalness: 0.8 172 }); 173 174 // === CHARACTER GROUP === 175 const kamaji = new THREE.Group(); 176 177 // === HEAD === 178 const headGeo = new THREE.SphereGeometry(20, 32, 32); 179 const head = new THREE.Mesh(headGeo, skinMaterial); 180 head.position.y = 40; 181 head.castShadow = true; 182 head.receiveShadow = true; 183 kamaji.add(head); 184 185 // === SUNGLASSES === 186 const glassesGroup = new THREE.Group(); 187 188 // Left lens 189 const lensGeo = new THREE.SphereGeometry(7, 32, 32); 190 const leftLens = new THREE.Mesh(lensGeo, glassesMaterial); 191 leftLens.position.set(-9, 42, 17); 192 leftLens.scale.z = 0.5; 193 glassesGroup.add(leftLens); 194 195 // Right lens 196 const rightLens = new THREE.Mesh(lensGeo, glassesMaterial); 197 rightLens.position.set(9, 42, 17); 198 rightLens.scale.z = 0.5; 199 glassesGroup.add(rightLens); 200 201 // Frames 202 const frameGeo = new THREE.TorusGeometry(7, 0.5, 8, 32); 203 const leftFrame = new THREE.Mesh(frameGeo, frameMaterial); 204 leftFrame.position.set(-9, 42, 17); 205 glassesGroup.add(leftFrame); 206 207 const rightFrame = new THREE.Mesh(frameGeo, frameMaterial); 208 rightFrame.position.set(9, 42, 17); 209 glassesGroup.add(rightFrame); 210 211 // Bridge 212 const bridgeGeo = new THREE.CylinderGeometry(0.8, 0.8, 6, 8); 213 const bridge = new THREE.Mesh(bridgeGeo, frameMaterial); 214 bridge.rotation.z = Math.PI / 2; 215 bridge.position.set(0, 42, 17); 216 glassesGroup.add(bridge); 217 218 // White reflections 219 const reflectGeo = new THREE.SphereGeometry(1.5, 16, 16); 220 const reflectMat = new THREE.MeshBasicMaterial({ 221 color: 0xffffff, 222 transparent: true, 223 opacity: 0.7 224 }); 225 const leftReflect = new THREE.Mesh(reflectGeo, reflectMat); 226 leftReflect.position.set(-11, 45, 20); 227 glassesGroup.add(leftReflect); 228 229 const rightReflect = new THREE.Mesh(reflectGeo, reflectMat); 230 rightReflect.position.set(7, 45, 20); 231 glassesGroup.add(rightReflect); 232 233 kamaji.add(glassesGroup); 234 235 // === BEARD (MULTI-LAYER) === 236 const beardGroup = new THREE.Group(); 237 238 // Center main mass 239 const beardCenterGeo = new THREE.SphereGeometry(10, 24, 24); 240 const beardCenter = new THREE.Mesh(beardCenterGeo, beardMaterial); 241 beardCenter.position.set(0, 30, 14); 242 beardCenter.scale.set(1.2, 1.6, 0.8); 243 beardCenter.castShadow = true; 244 beardGroup.add(beardCenter); 245 246 // Left side 247 const beardLeftGeo = new THREE.SphereGeometry(7, 20, 20); 248 const beardLeft = new THREE.Mesh(beardLeftGeo, beardMaterial); 249 beardLeft.position.set(-9, 32, 13); 250 beardLeft.scale.set(1, 1.4, 0.8); 251 beardLeft.castShadow = true; 252 beardGroup.add(beardLeft); 253 254 // Right side 255 const beardRight = new THREE.Mesh(beardLeftGeo, beardMaterial); 256 beardRight.position.set(9, 32, 13); 257 beardRight.scale.set(1, 1.4, 0.8); 258 beardRight.castShadow = true; 259 beardGroup.add(beardRight); 260 261 // Lower layers for fullness 262 const beardLowerGeo = new THREE.SphereGeometry(6, 20, 20); 263 const beardLower1 = new THREE.Mesh(beardLowerGeo, beardMaterial); 264 beardLower1.position.set(-6, 24, 12); 265 beardLower1.scale.set(1, 1.5, 0.7); 266 beardLower1.castShadow = true; 267 beardGroup.add(beardLower1); 268 269 const beardLower2 = new THREE.Mesh(beardLowerGeo, beardMaterial); 270 beardLower2.position.set(6, 24, 12); 271 beardLower2.scale.set(1, 1.5, 0.7); 272 beardLower2.castShadow = true; 273 beardGroup.add(beardLower2); 274 275 // Bottom center 276 const beardBottom = new THREE.Mesh(beardLowerGeo, beardMaterial); 277 beardBottom.position.set(0, 22, 13); 278 beardBottom.scale.set(1, 1.3, 0.7); 279 beardBottom.castShadow = true; 280 beardGroup.add(beardBottom); 281 282 kamaji.add(beardGroup); 283 284 // === TORSO (BLUE SHIRT) === 285 const torsoGeo = new THREE.CylinderGeometry(18, 24, 55, 24); 286 const torso = new THREE.Mesh(torsoGeo, shirtMaterial); 287 torso.position.y = 0; 288 torso.castShadow = true; 289 torso.receiveShadow = true; 290 kamaji.add(torso); 291 292 // === FUNCTION: CREATE DETAILED ARM === 293 function createDetailedArm() { 294 const armGroup = new THREE.Group(); 295 296 // Upper arm (blue sleeve) 297 const upperArmGeo = new THREE.CylinderGeometry(3.5, 3, 20, 16); 298 const upperArm = new THREE.Mesh(upperArmGeo, shirtMaterial); 299 upperArm.position.y = 8; 300 upperArm.castShadow = true; 301 armGroup.add(upperArm); 302 303 // Lower arm (blue sleeve) 304 const lowerArmGeo = new THREE.CylinderGeometry(3, 2.8, 20, 16); 305 const lowerArm = new THREE.Mesh(lowerArmGeo, shirtMaterial); 306 lowerArm.position.y = -8; 307 lowerArm.castShadow = true; 308 armGroup.add(lowerArm); 309 310 // Hand (tan skin) 311 const handGeo = new THREE.SphereGeometry(3.5, 16, 16); 312 const hand = new THREE.Mesh(handGeo, skinMaterial); 313 hand.position.y = -18; 314 hand.scale.set(1.1, 1.4, 0.85); 315 hand.castShadow = true; 316 armGroup.add(hand); 317 318 // === FINGERS (5 DETAILED FINGERS) === 319 const fingerPositions = [ 320 { x: -3, z: 1.5, scale: 0.85, name: 'pinky' }, 321 { x: -1.5, z: 2, scale: 0.95, name: 'ring' }, 322 { x: 0, z: 2.2, scale: 1.0, name: 'middle' }, 323 { x: 1.5, z: 2, scale: 0.95, name: 'index' }, 324 { x: 3, z: 1, scale: 0.8, name: 'thumb' } 325 ]; 326 327 fingerPositions.forEach((pos, i) => { 328 const fingerGroup = new THREE.Group(); 329 330 // Create 3 segments per finger 331 for (let seg = 0; seg < 3; seg++) { 332 const segmentGeo = new THREE.CylinderGeometry( 333 0.4 * pos.scale, 334 0.35 * pos.scale, 335 1.8, 336 8 337 ); 338 const segment = new THREE.Mesh(segmentGeo, skinMaterial); 339 segment.position.y = seg * 1.8; 340 segment.castShadow = true; 341 fingerGroup.add(segment); 342 } 343 344 fingerGroup.position.set(pos.x, -21, pos.z); 345 fingerGroup.rotation.x = Math.PI / 2; 346 347 // Thumb special rotation 348 if (i === 4) { 349 fingerGroup.rotation.z = -0.5; 350 fingerGroup.rotation.y = -0.3; 351 } 352 353 armGroup.add(fingerGroup); 354 }); 355 356 return armGroup; 357 } 358 359 // === ADD 6 ARMS (3 PER SIDE) === 360 const armConfigs = [ 361 // Left side 362 { side: -1, yPos: 15, zPos: 0, rotZ: 0.8, rotY: -0.3 }, 363 { side: -1, yPos: 5, zPos: 5, rotZ: 0.6, rotY: -0.2 }, 364 { side: -1, yPos: -5, zPos: -5, rotZ: 0.4, rotY: -0.1 }, 365 // Right side 366 { side: 1, yPos: 15, zPos: 0, rotZ: -0.8, rotY: 0.3 }, 367 { side: 1, yPos: 5, zPos: 5, rotZ: -0.6, rotY: 0.2 }, 368 { side: 1, yPos: -5, zPos: -5, rotZ: -0.4, rotY: 0.1 } 369 ]; 370 371 const arms = []; 372 armConfigs.forEach(config => { 373 const arm = createDetailedArm(); 374 arm.position.set(config.side * 22, config.yPos, config.zPos); 375 arm.rotation.z = config.rotZ; 376 arm.rotation.y = config.rotY; 377 kamaji.add(arm); 378 arms.push(arm); 379 }); 380 381 // === FLOOR (FOR SHADOWS) === 382 const floorGeo = new THREE.PlaneGeometry(200, 200); 383 const floorMat = new THREE.ShadowMaterial({ opacity: 0.3 }); 384 const floor = new THREE.Mesh(floorGeo, floorMat); 385 floor.rotation.x = -Math.PI / 2; 386 floor.position.y = -30; 387 floor.receiveShadow = true; 388 scene.add(floor); 389 390 scene.add(kamaji); 391 392 // === ORBIT CONTROLS (PROFESSIONAL) === 393 const controls = new OrbitControls(camera, renderer.domElement); 394 controls.enableDamping = true; 395 controls.dampingFactor = 0.05; 396 controls.target.set(0, 20, 0); 397 controls.minDistance = 80; 398 controls.maxDistance = 300; 399 controls.maxPolarAngle = Math.PI / 2; 400 controls.enablePan = true; 401 402 // === ANIMATION LOOP === 403 const clock = new THREE.Clock(); 404 405 function animate() { 406 requestAnimationFrame(animate); 407 408 const time = clock.getElapsedTime(); 409 410 // Subtle breathing animation 411 const breathScale = 1 + Math.sin(time * 1.5) * 0.008; 412 torso.scale.set(1, breathScale, 1); 413 414 // Gentle arm sway 415 arms.forEach((arm, i) => { 416 const offset = i * 0.5; 417 arm.rotation.x = Math.sin(time * 0.8 + offset) * 0.08; 418 }); 419 420 // Slight head bob 421 head.position.y = 40 + Math.sin(time * 1.5) * 0.3; 422 423 controls.update(); 424 renderer.render(scene, camera); 425 } 426 427 animate(); 428 429 // === RESPONSIVE RESIZE === 430 window.addEventListener('resize', () => { 431 const width = Math.min(window.innerWidth, 600); 432 const height = Math.min(window.innerHeight, 600); 433 camera.aspect = width / height; 434 camera.updateProjectionMatrix(); 435 renderer.setSize(width, height); 436 }); 437 438 </script> 439 </body> 440 </html>