1<!DOCTYPE html> 2<html> 3<head> 4 <meta charset="utf-8"> 5 <meta name="description" 6 content="InfiniHuman: Generate infinite 3D humans with precise control from text, clothing, body shape, and pose. Automatic pipeline creating 111K diverse identities with multi-modal annotations."> 7 <meta name="keywords" content="3D Human Generation, Diffusion Model, Text-to-3D, Controllable Avatar, 3D Gaussian Splatting, SIGGRAPH Asia 2025"> 8 <meta name="viewport" content="width=device-width, initial-scale=1"> 9 <title>InfiniHuman: Infinite 3D Human Creation with Precise Control</title> 10 11 <!-- Global site tag (gtag.js) - Google Analytics -->
vendor: 66 bytes, lines 11-12
11 12 <script async src="https://www.googletagmanager.com/gtag/js?id=
12G-PYVRSFMDRL
vendor: 14 bytes, lines 12-13
12"></script> 13
13<script> 14
vendor: 155 bytes, lines 14-22
14window.dataLayer = window.dataLayer || []; 15 16 function gtag() { 17 dataLayer.push(arguments); 18 } 19 20 gtag('js', new Date()); 21 22 gtag('config', '
22G-PYVRSFMDRL
vendor: 6 bytes, lines 22-23
22'); 23
23</script>
23 24 25 <link href="https://fonts.googleapis.com/css?family=Google+Sans|Noto+Sans|Castoro" 26 rel="stylesheet"> 27 28 <link rel="stylesheet" href="./static/css/bulma.min.css"> 29 <link rel="stylesheet" href="./static/css/bulma-carousel.min.css"> 30 <link rel="stylesheet" href="./static/css/bulma-slider.min.css"> 31 <link rel="stylesheet" href="./static/css/fontawesome.all.min.css"> 32 <link rel="stylesheet" 33 href="https://cdn.jsdelivr.net/gh/jpswalsh/academicons@1/css/academicons.min.css"> 34 <link rel="stylesheet" href="./static/css/index.css"> 35 <link rel="icon" href="./static/images/einstein_icon.png"> 36 37 <!-- Import map must come BEFORE any module scripts --> 38
38<script type="importmap"> 39 { 40 "imports": { 41 "three": "https://unpkg.com/[email protected]/build/three.module.js", 42 "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/" 43 } 44 } 45 </script>
45 46 47
47<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
47 48
48<script defer src="./static/js/fontawesome.all.min.js"></script>
48 49
49<script src="./static/js/bulma-carousel.min.js"></script>
49 50
50<script src="./static/js/bulma-slider.min.js"></script>
50 51
51<script src="./static/js/index.js"></script>
51 52 53 <style> 54 .imagesrow { 55 display: flex; 56 justify-content: center; 57 align-items: center; 58 gap: 30px; /* Gap between images */ 59 } 60 .row_img { 61 max-width: 100%; /* Adjust the width as needed */ 62 } 63 </style> 64</head> 65<body> 66 67<section class="hero"> 68 <div class="hero-body"> 69 <div class="container is-max-desktop"> 70 <div class="columns is-centered"> 71 <div class="column has-text-centered"> 72 <h1 class="title is-2 publication-title">InfiniHuman: Infinite 3D Human Creation with Precise Control</h1> 73 <div class="is-size-5 publication-authors"> 74 <span class="author-block"> 75 <a href="https://yuxuan-xue.com">Yuxuan Xue</a><sup>1,2</sup>,</span> 76 <span class="author-block"> 77 <a href="https://virtualhumans.mpi-inf.mpg.de/people/Xie.html">Xianghui Xie</a><sup>1,2,3</sup>,</span> 78 <span class="author-block"> 79 <a href="https://yuxuan-xue.com">Margaret Kostyrko</a><sup>1,2</sup>, 80 </span> 81 <span class="author-block"> 82 <a href="https://virtualhumans.mpi-inf.mpg.de/people/pons-moll.html">Gerard Pons-Moll</a><sup>1,2,3</sup> 83 </span> 84 </div> 85 86 <div class="is-size-5 publication-authors"> 87 <span class="author-block"><sup>1</sup>University of Tübingen, </span> 88 <span class="author-block"><sup>2</sup>Tübingen AI Center,</span> <br> 89 <span class="author-block"><sup>3</sup>Max Planck Institute for Informatics, Saarland Informatics Campus </sp
89an><br> 90 <!-- <span class="author-block">Technical Report, 2024</span> --> 91 </div> 92 93 <div class="column has-text-centered"> 94 <div class="publication-links"> 95 <!-- PDF Link. --> 96 <span class="link-block"> 97 <a href="paper/infinihuman.pdf" 98 class="external-link button is-normal is-rounded is-dark"> 99 <span class="icon"> 100 <i class="fas fa-file-pdf"></i> 101 </span> 102 <span>Paper</span> 103 </a> 104 </span> 105 <span class="link-block"> 106 <a href="https://arxiv.org/abs/2510.11650" 107 class="external-link button is-normal is-rounded is-dark"> 108 <span class="icon"> 109 <i class="ai ai-arxiv"></i> 110 </span> 111 <span>arXiv</span> 112 </a> 113 </span> 114 <!-- Video Link. --> 115 <span class="link-block"> 116 <a href="https://youtu.be/nqoomWVDR6w" 117 class="external-link button is-normal is-rounded is-dark"> 118 <span class="icon"> 119 <i class="fab fa-youtube"></i> 120 </span> 121 <span>Video</span> 122 </a> 123 </span> 124 <!-- Code Link. --> 125 <span class="link-block"> 126 <a href="https://github.com/YuxuanSnow/InfiniHuman/" 127 class="external-link button is-normal is-rounded is-dark"> 128 <span class="icon"> 129 <i class="fab fa-github"></i> 130 </span> 131 <span>Code</span> 132 </a> 133 </span> 134 </div> 135 136 <div class="is-size-5 publication-journal" style="margin-top: 10px;"> 137 <span class="journal-block"><strong>SIGGRAPH Asia 2025</strong></span> 138 </div> 139 140 </div> 141 </div> 142 </div> 143 </div> 144 </div> 145</section> 146 147<section class="hero teaser"> 148 <div class="container is-max-desktop"> 149 <div class="hero-body"> 150 <video id="teaser" autoplay muted loop playsinline height="100%"> 151 <source src="./static/videos/infinihuman_teaser_crop.mp4" 152 type="video/mp4"> 153 </video> 154 <h2 class="subtitle has-text-centered"> 155 <span class="dnerf">InfiniHuman</span> can create infinite 3D humans with precise control. 156 </h2> 157 </div> 158 </div> 159</section> 160 161 162 <div class="container is-max-desktop"> 163 <!-- Abstract. --> 164 <div class="columns is-centered has-text-centered"> 165 <div class="column is-four-fifths"> 166 <h2 class="title is-4">Abstract</h2> 167 <div class="content has-text-justified"> 168 <p> 169 <strong>TL;DR: We distill foundation models to generate theoretically unbounded, richly annotated 3D human data with 111K identities, enabling fast, realistic, and precisely controllable avatar generation from text descriptions, body shape, and clothing images.</strong> 170 <br> 171 <br> 172 Generating diverse and controllable 3D human avatars is challenging due to the prohibitive cost of capturing and annotating large-scale datasets. 173 We introduce <strong>InfiniHuman</strong>, a framework that distills foundation models to generate richly annotated human data with minimal cost and unlimited scalability. 174 Our <strong>InfiniHumanData</strong> pipeline automatically creates <strong>111K identities</strong> with unprecedented diversity in ethnicity, age, and clothing styles, each annotated with text descriptions, multi-view images, and SMPL body parameters. 175 Based on this, <strong>InfiniHumanGen</strong> enables fast, realistic, and precisely controllable avatar generation conditioned on text, body shape, and clothing assets. 176 </p> 177 </div> 178 </div> 179 </div> 180 </div> 181 182 <div class="container is-max-desktop"> 183 <br> 184 <!-- <h2 class="title is-3">Pipeline</h2>--> 185 <div class="hero-body"> 186 <img src="./static/images/pipeline_infiniHumanGen_3.png"/> 187 <p> Taking text description, explicit SMPL shape, and a cloth image as input, Gen-Schnell generates 3D-GS end-to-e
187nd, while Gen-HRes generates high-resolution textured mesh, both matched to input conditions. </p> 188 </div> 189 </div> 190 191 192 193 194<section class="section"> 195 <div class="container is-max-desktop"> 196 <h2 class="title is-3">Generation Results</h2> 197 <div class="content has-text-justified"> 198 <p> 199 InfiniHuman enables diverse and controllable 3D human generation from multiple input modalities. 200 Below we showcase results from four different generation tasks: 201 <a href="#gen-text-prompts"><strong>Generation from Text Prompts</strong></a>, 202 <a href="#gen-clothing"><strong>Generation from Clothing</strong></a>, 203 <a href="#gen-body-pose-shape"><strong>Generation from Body Pose & Shape</strong></a>, and 204 <a href="#edit-text"><strong>Editing from Text</strong></a>. 205 Click on any link to jump directly to that section, or scroll down to explore all results. 206 </p> 207 </div> 208 209 <!-- Subsection: Generation from Text Prompts --> 210 <div class="columns is-centered has-text-centered" id="gen-text-prompts"> 211 <div class="column is-four-fifths"> 212 <h3 class="title is-4">Generation from Text Prompts</h3> 213 214 <!-- Single 3D Viewer for Text Results --> 215 <div style="margin-bottom: 2rem;"> 216 <div id="text-viewer" style="width: 100%; height: 500px; background-color: #f5f5f5; border-radius: 8px; border: 1px solid #ddd; position: relative;"> 217 <div class="text-loading-text" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #888; z-index: 10;">Loading 3D model...</div> 218 </div> 219 </div> 220 221 <!-- Tabs for Text Prompts --> 222 <div class="tabs is-centered is-boxed" style="margin-top: 2rem;"> 223 <ul id="text-tabs-container"></ul> 224 </div> 225 226
226<script type="module"> 227 import * as THREE from 'three'; 228 import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; 229 import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; 230 import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 231 232 // Define text-based results - easily customizable 233 const textResults = [ 234 { id: 1, prompt: 'Albert Einstein', file: 'einstein_1.obj', format: 'obj' }, 235 { id: 3, prompt: 'Joker', file: 'joker.obj', format: 'obj' }, 236 { id: 7, prompt: 'Donald Trump', file: 'trump.obj', format: 'obj' }, 237 { id: 4, prompt: 'Angela Merkel', file: 'merkel.obj', format: 'obj' }, 238 { id: 6, prompt: 'Barack Obama', file: 'obama.obj', format: 'obj' }, 239 { id: 5, prompt: 'Spider Man', file: 'spiderman.obj', format: 'obj' }, 240 { id: 2, prompt: 'Iron Man', file: 'ironman.obj', format: 'obj' }, 241 { id: 8, prompt: 'Lionel Messi', file: 'messi.obj', format: 'obj' }, 242 ]; 243 244 let textScene, textCamera, textRenderer, textControls, textCurrentMesh; 245 let textLoadingText; 246 247 // Initialize the text viewer 248 function initTextViewer() { 249 const container = document.getElementById('text-viewer'); 250 textLoadingText = container.querySelector('.text-loading-text'); 251 const width = container.clientWidth; 252 const height = 500; 253 254 // Scene setup 255 textScene = new THREE.Scene(); 256 textScene.background = new THREE.Color(0xf5f5f5); 257 258 // Camera setup 259 textCamera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); 260 textCamera.position.set(0, 0, 2.5); 261 textCamera.lookAt(0, 0, 0); 262 263 // Renderer setup 264 textRenderer = new THREE.WebGLRenderer({ antialias: true }); 265 textRenderer.setSize(width, height); 266 textRenderer.setPixelRatio(window.devicePixelRatio); 267 container.appendChild(textRenderer.domElement); 268 269 // Controls 270 textControls = new OrbitControls(textCamera, textRenderer.domElement); 271 textControls.enableDamping = true; 272 textControls.autoRotate = true; 273 textControls.autoRotateSpeed = 2.0; 274 275 // Lighting 276 const ambientLight = new THREE.AmbientLight(0xffffff, 2.0); 277 textScene.add(ambientLight); 278 279 const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5); 280 directionalLight1.position.set(1, 1, 1); 281 textScene.add(directionalLight1); 282 283 const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.2); 284 directionalLight2.position.set(-1, -1, -1); 285 textScene.add(directionalLight2); 286 287 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x888888, 1.0); 288 textScene.add(hemisphereLight); 289 290 // Animation loop 291 function animate() { 292 requestAnimationFrame(animate); 293 textControls.update(); 294 textRenderer.render(textScene, textCamera); 295 } 296 animate(); 297 298 // Handle window resize 299 window.addEventListener('resize', () => { 300 const newWidth = container.clientWidth; 301 textCamera.aspect = newWidth / height; 302 textCamera.updateProjectionMatrix(); 303 textRenderer.setSize(newWidth, height); 304 }); 305 } 306 307 // Load a 3D model for text results 308 function loadTextModel(modelPath, prompt, format = 'ply') { 309 textLoadingText.style.display = 'block'; 310 textLoadingText.textContent = 'Loading 3D model...'; 311 312 // Remove current mesh if exists 313 if (textCurrentMesh) { 314 textScene.remove(textCurrentMesh); 315 if (textCurrentMesh.geometry) textCurrentMesh.geometry.dispose(); 316 if (textCurrentMesh.material) { 317 if (Array.isArray(textCurrentMesh.material)) { 318 textCurrentMesh.material.forEach(m => m.dispose()); 319 } else { 320 textCurrentMesh.material.dispose(); 321 } 322 } 323 if (textCurrentMesh.children) { 324 textCurrentMesh.traverse((child) => { 325 if (child.geometry) child.geometry.dispose(); 326 if (child.material) { 327 if (Array.isArray(child.material)) { 328 child.material.forEach(m => m.dispose()); 329 } else { 330 child.material.dispose(); 331 } 332 } 333 }); 334 } 335 } 336 337 const loader = format === 'obj' ? new OBJLoader() : new PLYLoader(); 338 339 loader.load( 340 modelPath, 341 function (loadedObject) { 342 console.log(`Successfully loaded ${format.toUpperCase()} for text prompt: ${prompt}`); 343 344 let mesh; 345 346 if (format === 'obj') { 347 mesh = loadedObject; 348 349 mesh.traverse((child) => { 350 if (child.isMesh) { 351 child.geometry.computeVertexNormals(); 352 const hasVertexColors = child.geometry.attributes.color !== undefined; 353 354 child.material = new THREE.MeshStandardMaterial({ 355 color: hasVertexColors ? 0xffffff : 0x606060, 356 flatShading: false, 357 side: THREE.DoubleSide, 358 vertexColors: hasVertexColors, 359 roughness: 0.8, 360 metalness: 0.0 361 }); 362 } 363 }); 364 365 const box = new THREE.Box3().setFromObject(mesh); 366 const center = new THREE.Vector3(); 367 box.getCenter(center); 368 369 mesh.traverse((child) => { 370 if (child.isMesh) { 371 child.geometry.translate(-center.x, -center.y, -center.z); 372 } 373 }); 374 375 const size = new THREE.Vector3(); 376 box.getSize(size); 377 const maxDim = Math.max(size.x, size.y, size.z); 378 const scale = 1.8 / maxDim; 379 mesh.scale.set(scale, scale, scale); 380 381 } else { 382 const geometry = loadedObject; 383 geometry.computeVertexNormals(); 384 385 const material = new THREE.MeshStandardMaterial({ 386 color: 0xaaaaaa, 387 flatShading: false, 388 side: THREE.DoubleSide, 389 vertexColors: geometry.attributes.color ? true : false 390 }); 391 392 mesh = new THREE.Mesh(geometry, material); 393 394 geometry.computeBoundingBox(); 395 const center = new THREE.Vector3(); 396 geometry.boundingBox.getCenter(center); 397 mesh.geometry.translate(-center.x, -center.y, -center.z); 398 399 const size = new THREE.Vector3(); 400 geometry.boundingBox.getSize(size); 401 const maxDim = Math.max(size.x, size.y, size.z); 402 const scale = 1.8 / maxDim; 403 mesh.scale.set(scale, scale, scale); 404 405 mesh.rotation.x = Math.PI; 406 } 407 408 textCurrentMesh = mesh; 409 textScene.add(textCurrentMesh); 410 textLoadingText.style.display = 'none'; 411 412 textCamera.position.set(0, 0, 2.5); 413 textCamera.lookAt(0, 0, 0); 414 textControls.target.set(0, 0, 0); 415 textControls.update(); 416 }, 417 function (xhr) { 418 const percentComplete = xhr.loaded / xhr.total * 100; 419 textLoadingText.textContent = `Loading: ${Math.round(percentComplete)}%`; 420 }, 421 function (error) { 422 console.error(`Error loading ${format.toUpperCase()} for text prompt: ${prompt}`, error); 423 textLoadingText.textContent = 'Error loading 3D model'; 424 textLoadingText.style.fontSize = '0.9em'; 425 } 426 ); 427 } 428 429 // Generate tabs 430 const tabsContainer = document.getElementById('text-tabs-container');
431 textResults.forEach((result, index) => { 432 const li = document.createElement('li'); 433 if (index === 0) li.className = 'is-active'; 434 435 li.innerHTML = `<a>${result.prompt}</a>`; 436 437 li.addEventListener('click', () => { 438 // Update active state 439 document.querySelectorAll('#text-tabs-container li').forEach((tab, idx) => { 440 if (idx === index) { 441 tab.className = 'is-active'; 442 } else { 443 tab.className = ''; 444 } 445 }); 446 447 const format = result.format || 'ply'; 448 loadTextModel(`./static/results/${result.file}`, result.prompt, format); 449 }); 450 451 tabsContainer.appendChild(li); 452 }); 453 454 // Initialize viewer and load first model 455 initTextViewer(); 456 loadTextModel(`./static/results/${textResults[0].file}`, textResults[0].prompt, textResults[0].format); 457 </script>
457 458 </div> 459 </div> 460 461 <!-- Subsection: Generation from Clothing --> 462 <div class="columns is-centered has-text-centered" style="margin-top: 3rem;" id="gen-clothing"> 463 <div class="column is-four-fifths"> 464 <h3 class="title is-4">Generation from Clothing</h3> 465 466 <!-- Single 3D Viewer --> 467 <div style="margin-bottom: 2rem;"> 468 <div id="main-viewer" style="width: 100%; height: 500px; background-color: #f5f5f5; border-radius: 8px; border: 1px solid #ddd; position: relative;"> 469 <div class="loading-text" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #888; z-index: 10;">Loading 3D model...</div> 470 </div> 471 </div> 472 473 <!-- Thumbnail Grid --> 474 <div id="thumbnail-container" style="display: flex; gap: 1rem; margin-top: 2rem; overflow-x: auto; padding-bottom: 1rem;"></div> 475 476
476<script type="module"> 477 import * as THREE from 'three'; 478 import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; 479 import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; 480 import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 481 482 // Define results data - easily portable to other repos 483 // You can specify format as 'ply' or 'obj' (defaults to 'ply' if not specified) 484 const results = [ 485 { id: '01_0079_010', image: '01_0079_010.png', format: 'obj' }, 486 { id: '03_0145_018', image: '03_0145_018.png', format: 'obj' }, 487 { id: '04_0062_012', image: '04_0062_012.png', format: 'obj' }, 488 { id: '04_0111_011', image: '04_0111_011.png', format: 'obj' }, 489 { id: '08_0089_015', image: '08_0089_015.png', format: 'obj' }, 490 { id: '22_0211_005', image: '22_0211_005.png', format: 'obj' }, 491 { id: '23_0134_001', image: '23_0134_001.png', format: 'obj' }, 492 { id: '33_0092_016', image: '33_0092_016.png', format: 'obj' }, 493 ]; 494 495 let scene, camera, renderer, controls, currentMesh; 496 let loadingText; 497 498 // Initialize the viewer once 499 function initViewer() { 500 const container = document.getElementById('main-viewer'); 501 loadingText = container.querySelector('.loading-text'); 502 const width = container.clientWidth; 503 const height = 500; 504 505 // Scene setup 506 scene = new THREE.Scene(); 507 scene.background = new THREE.Color(0xf5f5f5); 508 509 // Camera setup 510 camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); 511 camera.position.set(0, 0, 2.5); 512 camera.lookAt(0, 0, 0); 513 514 // Renderer setup 515 renderer = new THREE.WebGLRenderer({ antialias: true }); 516 renderer.setSize(width, height); 517 renderer.setPixelRatio(window.devicePixelRatio); 518 container.appendChild(renderer.domElement); 519 520 // Controls 521 controls = new OrbitControls(camera, renderer.domElement); 522 controls.enableDamping = true; 523 controls.autoRotate = true; 524 controls.autoRotateSpeed = 2.0; 525 526 // Improved Lighting - Much Brighter 527 const ambientLight = new THREE.AmbientLight(0xffffff, 2.0); 528 scene.add(ambientLight); 529 530 const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5); 531 directionalLight1.position.set(1, 1, 1); 532 scene.add(directionalLight1); 533 534 const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.2); 535 directionalLight2.position.set(-1, -1, -1); 536 scene.add(directionalLight2); 537 538 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x888888, 1.0); 539 scene.add(hemisphereLight); 540 541 // Animation loop 542 function animate() { 543 requestAnimationFrame(animate); 544 controls.update(); 545 renderer.render(scene, camera); 546 } 547 animate(); 548 549 // Handle window resize 550 window.addEventListener('resize', () => { 551 const newWidth = container.clientWidth; 552 camera.aspect = newWidth / height; 553 camera.updateProjectionMatrix(); 554 renderer.setSize(newWidth, height); 555 }); 556 } 557 558 // Load a 3D model (PLY or OBJ) into the viewer 559 function loadModel(modelPath, resultId, format = 'ply') { 560 loadingText.style.display = 'block'; 561 loadingText.textContent = 'Loading 3D model...'; 562 563 // Remove current mesh if exists 564 if (currentMesh) { 565 scene.remove(currentMesh); 566 if (currentMesh.geometry) currentMesh.geometry.dispose(); 567 if (currentMesh.material) { 568 if (Array.isArray(currentMesh.material)) { 569 currentMesh.material.forEach(m => m.dispose()); 570 } else { 571 currentMesh.material.dispose(); 572 } 573 } 574 // For OBJ groups, dispose all children 575 if (currentMesh.children) { 576 currentMesh.traverse((child) => { 577 if (child.geometry) child.geometry.dispose(); 578 if (child.material) { 579 if (Array.isArray(child.material)) { 580 child.material.forEach(m => m.dispose()); 581 } else { 582 child.material.dispose(); 583 } 584 } 585 }); 586 } 587 } 588 589 const loader = format === 'obj' ? new OBJLoader() : new PLYLoader(); 590 591 loader.load( 592 modelPath, 593 function (loadedObject) { 594 console.log(`Successfully loaded ${format.toUpperCase()} for result ${resultId}`); 595 596 let mesh; 597 598 if (format === 'obj') { 599 // OBJLoader returns a Group/Object3D 600 mesh = loadedObject; 601 602 // Apply material to all meshes in the group 603 mesh.traverse((child) => { 604 if (child.isMesh) { 605 child.geometry.computeVertexNormals(); 606 607 // Check if the geometry has vertex colors 608 const hasVertexColors = child.geometry.attributes.color !== undefined; 609 console.log(`OBJ mesh has vertex colors: ${hasVertexColors}`); 610 611 child.material = new THREE.MeshStandardMaterial({ 612 color: hasVertexColors ? 0xffffff : 0x606060, // Use white if vertex colors exist, darker gray otherwise 613 flatShading: false, 614 side: THREE.DoubleSide, 615 vertexColors: hasVertexColors, 616 roughness: 0.8, 617 metalness: 0.0 618 }); 619 } 620 }); 621 622 // Center the OBJ by computing bounding box and offsetting position 623 const box = new THREE.Box3().setFromObject(mesh); 624 const center = new THREE.Vector3(); 625 box.getCenter(center); 626 627 // Translate all child meshes to center the group 628 mesh.traverse((child) => { 629 if (child.isMesh) { 630 child.geometry.translate(-center.x, -center.y, -center.z); 631 } 632 }); 633 634 // Scale the model 635 const size = new THREE.Vector3(); 636 box.getSize(size); 637 const maxDim = Math.max(size.x, size.y, size.z); 638 const scale = 1.8 / maxDim; 639 mesh.scale.set(scale, scale, scale); 640 641 // OBJ files typically don't need rotation (unlike PLY files) 642 // If your OBJ is upside down, uncomment the line below: 643 // mesh.rotation.x = Math.PI; 644 645 } else { 646 // PLYLoader returns a BufferGeometry 647 const geometry = loadedObject; 648 geometry.computeVertexNormals(); 649 650 const material = new THREE.MeshStandardMaterial({ 651 color: 0xaaaaaa, 652 flatShading: false, 653 side: THREE.DoubleSide, 654 vertexColors: geometry.attributes.color ? true : false 655 }); 656 657 mesh = new THREE.Mesh(geometry, material); 658 659 // Center and scale the model (original PLY approach) 660 geometry.computeBoundingBox(); 661 const center = new THREE.Vector3(); 662 geometry.boundingBox.getCenter(center); 663 mesh.geometry.translate(-center.x, -center.y, -center.z); 664 665 const size = new THREE.Vector3(); 666 geometry.boundingBox.getSize(size); 667 const maxDim = Math.max(size.x, size.y, size.z); 668 const scale = 1.8 / maxDim; 669 mesh.scale.set(scale, scale, scale); 670
671 // Fix upside-down orientation - rotate 180 degrees around X axis 672 mesh.rotation.x = Math.PI; 673 } 674 675 currentMesh = mesh; 676 scene.add(currentMesh); 677 loadingText.style.display = 'none'; 678 679 // Reset camera position and look at center 680 camera.position.set(0, 0, 2.5); 681 camera.lookAt(0, 0, 0); 682 controls.target.set(0, 0, 0); 683 controls.update(); 684 }, 685 function (xhr) { 686 const percentComplete = xhr.loaded / xhr.total * 100; 687 loadingText.textContent = `Loading: ${Math.round(percentComplete)}%`; 688 }, 689 function (error) { 690 console.error(`Error loading ${format.toUpperCase()} for result ${resultId}:`, error); 691 loadingText.textContent = 'Error loading 3D model'; 692 loadingText.style.fontSize = '0.9em'; 693 } 694 ); 695 } 696 697 // Generate thumbnail grid 698 const thumbnailContainer = document.getElementById('thumbnail-container'); 699 results.forEach((result, index) => { 700 const thumbDiv = document.createElement('div'); 701 thumbDiv.style.cursor = 'pointer'; 702 thumbDiv.style.transition = 'transform 0.2s, box-shadow 0.2s'; 703 thumbDiv.style.minWidth = '150px'; 704 thumbDiv.style.flexShrink = '0'; 705 thumbDiv.className = index === 0 ? 'active-thumb' : ''; 706 707 thumbDiv.innerHTML = ` 708 <img src="./static/results/${result.image}" 709 alt="Result ${result.id}" 710 style="width: 150px; height: 150px; object-fit: cover; border-radius: 8px; border: ${index === 0 ? '3px solid #3273dc' : '2px solid #ddd'}; display: block;"> 711 `; 712 713 thumbDiv.addEventListener('click', () => { 714 // Update active state 715 document.querySelectorAll('#thumbnail-container > div').forEach((div, idx) => { 716 const img = div.querySelector('img'); 717 if (idx === index) { 718 img.style.border = '3px solid #3273dc'; 719 } else { 720 img.style.border = '2px solid #ddd'; 721 } 722 }); 723 724 const format = result.format || 'ply'; 725 loadModel(`./static/results/${result.id}.${format}`, result.id, format); 726 }); 727 728 thumbDiv.addEventListener('mouseenter', () => { 729 if (!thumbDiv.classList.contains('active-thumb')) { 730 thumbDiv.style.transform = 'scale(1.05)'; 731 } 732 }); 733 734 thumbDiv.addEventListener('mouseleave', () => { 735 thumbDiv.style.transform = 'scale(1)'; 736 }); 737 738 thumbnailContainer.appendChild(thumbDiv); 739 }); 740 741 // Initialize viewer and load first model 742 initViewer(); 743 loadModel('./static/results/01_0079_010.obj', '01_0079_010', 'obj'); 744 </script>
744 745 </div> 746 </div> 747 748 <!-- Subsection: Generation from Body Pose & Shape --> 749 <div class="columns is-centered has-text-centered" style="margin-top: 3rem;" id="gen-body-pose-shape"> 750 <div class="column is-four-fifths"> 751 <h3 class="title is-4">Generation from Body Pose & Shape</h3> 752 753 <!-- Single 3D Viewer --> 754 <div style="margin-bottom: 2rem;"> 755 <div id="shape-viewer" style="width: 100%; height: 500px; background-color: #f5f5f5; border-radius: 8px; border: 1px solid #ddd; position: relative;"> 756 <div class="shape-loading-text" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #888; z-index: 10;">Loading 3D model...</div> 757 </div> 758 </div> 759 760 <!-- Thumbnail Grid --> 761 <div id="shape-thumbnail-container" style="display: flex; gap: 1rem; margin-top: 2rem; overflow-x: auto; padding-bottom: 1rem;"></div> 762 763
763<script type="module"> 764 import * as THREE from 'three'; 765 import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; 766 import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; 767 import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 768 769 // Define body shape editing results 770 const shapeResults = [ 771 { id: "pose21", image: 'pose21.png', format: 'obj' }, 772 { id: "pose22", image: 'pose22.png', format: 'obj' }, 773 { id: "pose23", image: 'pose23.png', format: 'obj' }, 774 { id: "shape5", image: 'shape5.png', format: 'obj' }, 775 { id: "shape10", image: 'shape10.png', format: 'obj' }, 776 { id: "shape12", image: 'shape12.png', format: 'obj' }, 777 ]; 778 779 let shapeScene, shapeCamera, shapeRenderer, shapeControls, shapeCurrentMesh; 780 let shapeLoadingText; 781 782 // Initialize the shape viewer 783 function initShapeViewer() { 784 const container = document.getElementById('shape-viewer'); 785 shapeLoadingText = container.querySelector('.shape-loading-text'); 786 const width = container.clientWidth; 787 const height = 500; 788 789 // Scene setup 790 shapeScene = new THREE.Scene(); 791 shapeScene.background = new THREE.Color(0xf5f5f5); 792 793 // Camera setup 794 shapeCamera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); 795 shapeCamera.position.set(0, 0, 2.5); 796 shapeCamera.lookAt(0, 0, 0); 797 798 // Renderer setup 799 shapeRenderer = new THREE.WebGLRenderer({ antialias: true }); 800 shapeRenderer.setSize(width, height); 801 shapeRenderer.setPixelRatio(window.devicePixelRatio); 802 container.appendChild(shapeRenderer.domElement); 803 804 // Controls 805 shapeControls = new OrbitControls(shapeCamera, shapeRenderer.domElement); 806 shapeControls.enableDamping = true; 807 shapeControls.autoRotate = true; 808 shapeControls.autoRotateSpeed = 2.0; 809 810 // Lighting 811 const ambientLight = new THREE.AmbientLight(0xffffff, 2.0); 812 shapeScene.add(ambientLight); 813 814 const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5); 815 directionalLight1.position.set(1, 1, 1); 816 shapeScene.add(directionalLight1); 817 818 const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.2); 819 directionalLight2.position.set(-1, -1, -1); 820 shapeScene.add(directionalLight2); 821 822 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x888888, 1.0); 823 shapeScene.add(hemisphereLight); 824 825 // Animation loop 826 function animate() { 827 requestAnimationFrame(animate); 828 shapeControls.update(); 829 shapeRenderer.render(shapeScene, shapeCamera); 830 } 831 animate(); 832 833 // Handle window resize 834 window.addEventListener('resize', () => { 835 const newWidth = container.clientWidth; 836 shapeCamera.aspect = newWidth / height; 837 shapeCamera.updateProjectionMatrix(); 838 shapeRenderer.setSize(newWidth, height); 839 }); 840 } 841 842 // Load a 3D model for shape results 843 function loadShapeModel(modelPath, resultId, format = 'ply') { 844 shapeLoadingText.style.display = 'block'; 845 shapeLoadingText.textContent = 'Loading 3D model...'; 846 847 // Remove current mesh if exists 848 if (shapeCurrentMesh) { 849 shapeScene.remove(shapeCurrentMesh); 850 if (shapeCurrentMesh.geometry) shapeCurrentMesh.geometry.dispose(); 851 if (shapeCurrentMesh.material) { 852 if (Array.isArray(shapeCurrentMesh.material)) { 853 shapeCurrentMesh.material.forEach(m => m.dispose()); 854 } else { 855 shapeCurrentMesh.material.dispose(); 856 } 857 } 858 if (shapeCurrentMesh.children) { 859 shapeCurrentMesh.traverse((child) => { 860 if (child.geometry) child.geometry.dispose(); 861 if (child.material) { 862 if (Array.isArray(child.material)) { 863 child.material.forEach(m => m.dispose()); 864 } else { 865 child.material.dispose(); 866 } 867 } 868 }); 869 } 870 } 871 872 const loader = format === 'obj' ? new OBJLoader() : new PLYLoader(); 873 874 loader.load( 875 modelPath, 876 function (loadedObject) { 877 console.log(`Successfully loaded ${format.toUpperCase()} for shape result ${resultId}`); 878 879 let mesh; 880 881 if (format === 'obj') { 882 mesh = loadedObject; 883 884 mesh.traverse((child) => { 885 if (child.isMesh) { 886 child.geometry.computeVertexNormals(); 887 const hasVertexColors = child.geometry.attributes.color !== undefined; 888 console.log(`OBJ mesh has vertex colors: ${hasVertexColors}`); 889 890 child.material = new THREE.MeshStandardMaterial({ 891 color: hasVertexColors ? 0xffffff : 0x606060, 892 flatShading: false, 893 side: THREE.DoubleSide, 894 vertexColors: hasVertexColors, 895 roughness: 0.8, 896 metalness: 0.0 897 }); 898 } 899 }); 900 901 const box = new THREE.Box3().setFromObject(mesh); 902 const center = new THREE.Vector3(); 903 box.getCenter(center); 904 905 mesh.traverse((child) => { 906 if (child.isMesh) { 907 child.geometry.translate(-center.x, -center.y, -center.z); 908 } 909 }); 910 911 const size = new THREE.Vector3(); 912 box.getSize(size); 913 const maxDim = Math.max(size.x, size.y, size.z); 914 const scale = 1.8 / maxDim; 915 mesh.scale.set(scale, scale, scale); 916 917 } else { 918 const geometry = loadedObject; 919 geometry.computeVertexNormals(); 920 921 const material = new THREE.MeshStandardMaterial({ 922 color: 0xaaaaaa, 923 flatShading: false, 924 side: THREE.DoubleSide, 925 vertexColors: geometry.attributes.color ? true : false 926 }); 927 928 mesh = new THREE.Mesh(geometry, material); 929 930 geometry.computeBoundingBox(); 931 const center = new THREE.Vector3(); 932 geometry.boundingBox.getCenter(center); 933 mesh.geometry.translate(-center.x, -center.y, -center.z); 934 935 const size = new THREE.Vector3(); 936 geometry.boundingBox.getSize(size); 937 const maxDim = Math.max(size.x, size.y, size.z); 938 const scale = 1.8 / maxDim; 939 mesh.scale.set(scale, scale, scale); 940 941 mesh.rotation.x = Math.PI; 942 } 943 944 shapeCurrentMesh = mesh; 945 shapeScene.add(shapeCurrentMesh); 946 shapeLoadingText.style.display = 'none'; 947 948 shapeCamera.position.set(0, 0, 2.5); 949 shapeCamera.lookAt(0, 0, 0); 950 shapeControls.target.set(0, 0, 0); 951 shapeControls.update(); 952 }, 953 function (xhr) { 954 const percentComplete = xhr.loaded / xhr.total * 100; 955 shapeLoadingText.textContent = `Loading: ${Math.round(percentComplete)}%`; 956 }, 957 function (error) { 958 console.error(`Error loading ${format.toUpperCase()} for shape result ${resultId}:`, error); 959 shapeLoadingText.textContent = 'Error loading 3D model'; 960 shapeLoadingText.style.fontSize = '0.9em'; 961 } 962 ); 963 } 964 965 // Generate thumbnail grid 966 const shapeThumbnailContainer = document.getElementById('shape-thumbnail-container');
967 shapeResults.forEach((result, index) => { 968 const thumbDiv = document.createElement('div'); 969 thumbDiv.style.cursor = 'pointer'; 970 thumbDiv.style.transition = 'transform 0.2s, box-shadow 0.2s'; 971 thumbDiv.style.minWidth = '150px'; 972 thumbDiv.style.flexShrink = '0'; 973 thumbDiv.className = index === 0 ? 'active-thumb' : ''; 974 975 thumbDiv.innerHTML = ` 976 <img src="./static/results/${result.image}" 977 alt="Result ${result.id}" 978 style="width: 150px; height: 150px; object-fit: cover; border-radius: 8px; border: ${index === 0 ? '3px solid #3273dc' : '2px solid #ddd'}; display: block;"> 979 `; 980 981 thumbDiv.addEventListener('click', () => { 982 // Update active state 983 document.querySelectorAll('#shape-thumbnail-container > div').forEach((div, idx) => { 984 const img = div.querySelector('img'); 985 if (idx === index) { 986 img.style.border = '3px solid #3273dc'; 987 } else { 988 img.style.border = '2px solid #ddd'; 989 } 990 }); 991 992 const format = result.format || 'ply'; 993 loadShapeModel(`./static/results/${result.id}.${format}`, result.id, format); 994 }); 995 996 thumbDiv.addEventListener('mouseenter', () => { 997 if (!thumbDiv.classList.contains('active-thumb')) { 998 thumbDiv.style.transform = 'scale(1.05)'; 999 } 1000 }); 1001 1002 thumbDiv.addEventListener('mouseleave', () => { 1003 thumbDiv.style.transform = 'scale(1)'; 1004 }); 1005 1006 shapeThumbnailContainer.appendChild(thumbDiv); 1007 }); 1008 1009 // Initialize viewer and load first model 1010 initShapeViewer(); 1011 const firstFormat = shapeResults[0].format || 'ply'; 1012 loadShapeModel(`./static/results/${shapeResults[0].id}.${firstFormat}`, shapeResults[0].id, firstFormat); 1013 </script>
1013 1014 </div> 1015 </div> 1016 1017 <!-- Subsection: Editing from Text --> 1018 <div class="columns is-centered has-text-centered" style="margin-top: 3rem;" id="edit-text"> 1019 <div class="column is-four-fifths"> 1020 <h3 class="title is-4">Editing from Text</h3> 1021 1022 <!-- Single 3D Viewer --> 1023 <div style="margin-bottom: 2rem;"> 1024 <div id="edit-viewer" style="width: 100%; height: 500px; background-color: #f5f5f5; border-radius: 8px; border: 1px solid #ddd; position: relative;"> 1025 <div class="edit-loading-text" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #888; z-index: 10;">Loading 3D model...</div> 1026 </div> 1027 </div> 1028 1029 <!-- Tabs for Editing --> 1030 <div class="tabs is-centered is-boxed" style="margin-top: 2rem;"> 1031 <ul id="edit-tabs-container"></ul> 1032 </div> 1033 1034
1034<script type="module"> 1035 import * as THREE from 'three'; 1036 import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'; 1037 import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; 1038 import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 1039 1040 // Define text editing results 1041 const editResults = [ 1042 { id: 6, prompt: 'Blue jeans', file: 'bluejeans.obj', format: 'obj' }, 1043 { id: 8, prompt: 'Short jeans', file: 'shortjeans.obj', format: 'obj' }, 1044 { id: 12, prompt: 'Sunglasses', file: 'sunglasses.obj', format: 'obj' }, 1045 { id: 7, prompt: 'Black jeans', file: 'blackjeans.obj', format: 'obj' }, 1046 { id: 9, prompt: 'Red high heels', file: 'redheels.obj', format: 'obj' }, 1047 { id: 10, prompt: 'Scarf', file: 'scarf.obj', format: 'obj' }, 1048 { id: 11, prompt: 'Black stockings', file: 'blackstockings.obj', format: 'obj' } 1049 ]; 1050 1051 let editScene, editCamera, editRenderer, editControls, editCurrentMesh; 1052 let editLoadingText; 1053 1054 // Initialize the edit viewer 1055 function initEditViewer() { 1056 const container = document.getElementById('edit-viewer'); 1057 editLoadingText = container.querySelector('.edit-loading-text'); 1058 const width = container.clientWidth; 1059 const height = 500; 1060 1061 // Scene setup 1062 editScene = new THREE.Scene(); 1063 editScene.background = new THREE.Color(0xf5f5f5); 1064 1065 // Camera setup 1066 editCamera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); 1067 editCamera.position.set(0, 0, 2.5); 1068 editCamera.lookAt(0, 0, 0); 1069 1070 // Renderer setup 1071 editRenderer = new THREE.WebGLRenderer({ antialias: true }); 1072 editRenderer.setSize(width, height); 1073 editRenderer.setPixelRatio(window.devicePixelRatio); 1074 container.appendChild(editRenderer.domElement); 1075 1076 // Controls 1077 editControls = new OrbitControls(editCamera, editRenderer.domElement); 1078 editControls.enableDamping = true; 1079 editControls.autoRotate = true; 1080 editControls.autoRotateSpeed = 2.0; 1081 1082 // Lighting 1083 const ambientLight = new THREE.AmbientLight(0xffffff, 2.0); 1084 editScene.add(ambientLight); 1085 1086 const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5); 1087 directionalLight1.position.set(1, 1, 1); 1088 editScene.add(directionalLight1); 1089 1090 const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.2); 1091 directionalLight2.position.set(-1, -1, -1); 1092 editScene.add(directionalLight2); 1093 1094 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x888888, 1.0); 1095 editScene.add(hemisphereLight); 1096 1097 // Animation loop 1098 function animate() { 1099 requestAnimationFrame(animate); 1100 editControls.update(); 1101 editRenderer.render(editScene, editCamera); 1102 } 1103 animate(); 1104 1105 // Handle window resize 1106 window.addEventListener('resize', () => { 1107 const newWidth = container.clientWidth; 1108 editCamera.aspect = newWidth / height; 1109 editCamera.updateProjectionMatrix(); 1110 editRenderer.setSize(newWidth, height); 1111 }); 1112 } 1113 1114 // Load a 3D model for edit results 1115 function loadEditModel(modelPath, resultId, format = 'ply') { 1116 editLoadingText.style.display = 'block'; 1117 editLoadingText.textContent = 'Loading 3D model...'; 1118 1119 // Remove current mesh if exists 1120 if (editCurrentMesh) { 1121 editScene.remove(editCurrentMesh); 1122 if (editCurrentMesh.geometry) editCurrentMesh.geometry.dispose(); 1123 if (editCurrentMesh.material) { 1124 if (Array.isArray(editCurrentMesh.material)) { 1125 editCurrentMesh.material.forEach(m => m.dispose()); 1126 } else { 1127 editCurrentMesh.material.dispose(); 1128 } 1129 } 1130 if (editCurrentMesh.children) { 1131 editCurrentMesh.traverse((child) => { 1132 if (child.geometry) child.geometry.dispose(); 1133 if (child.material) { 1134 if (Array.isArray(child.material)) { 1135 child.material.forEach(m => m.dispose()); 1136 } else { 1137 child.material.dispose(); 1138 } 1139 } 1140 }); 1141 } 1142 } 1143 1144 const loader = format === 'obj' ? new OBJLoader() : new PLYLoader(); 1145 1146 loader.load( 1147 modelPath, 1148 function (loadedObject) { 1149 console.log(`Successfully loaded ${format.toUpperCase()} for edit result ${resultId}`); 1150 1151 let mesh; 1152 1153 if (format === 'obj') { 1154 mesh = loadedObject; 1155 1156 mesh.traverse((child) => { 1157 if (child.isMesh) { 1158 child.geometry.computeVertexNormals(); 1159 const hasVertexColors = child.geometry.attributes.color !== undefined; 1160 1161 child.material = new THREE.MeshStandardMaterial({ 1162 color: hasVertexColors ? 0xffffff : 0x606060, 1163 flatShading: false, 1164 side: THREE.DoubleSide, 1165 vertexColors: hasVertexColors, 1166 roughness: 0.8, 1167 metalness: 0.0 1168 }); 1169 } 1170 }); 1171 1172 const box = new THREE.Box3().setFromObject(mesh); 1173 const center = new THREE.Vector3(); 1174 box.getCenter(center); 1175 1176 mesh.traverse((child) => { 1177 if (child.isMesh) { 1178 child.geometry.translate(-center.x, -center.y, -center.z); 1179 } 1180 }); 1181 1182 const size = new THREE.Vector3(); 1183 box.getSize(size); 1184 const maxDim = Math.max(size.x, size.y, size.z); 1185 const scale = 1.8 / maxDim; 1186 mesh.scale.set(scale, scale, scale); 1187 1188 } else { 1189 const geometry = loadedObject; 1190 geometry.computeVertexNormals(); 1191 1192 const material = new THREE.MeshStandardMaterial({ 1193 color: 0xaaaaaa, 1194 flatShading: false, 1195 side: THREE.DoubleSide, 1196 vertexColors: geometry.attributes.color ? true : false 1197 }); 1198 1199 mesh = new THREE.Mesh(geometry, material); 1200 1201 geometry.computeBoundingBox(); 1202 const center = new THREE.Vector3(); 1203 geometry.boundingBox.getCenter(center); 1204 mesh.geometry.translate(-center.x, -center.y, -center.z); 1205 1206 const size = new THREE.Vector3(); 1207 geometry.boundingBox.getSize(size); 1208 const maxDim = Math.max(size.x, size.y, size.z); 1209 const scale = 1.8 / maxDim; 1210 mesh.scale.set(scale, scale, scale); 1211 1212 mesh.rotation.x = Math.PI; 1213 } 1214 1215 editCurrentMesh = mesh; 1216 editScene.add(editCurrentMesh); 1217 editLoadingText.style.display = 'none'; 1218 1219 editCamera.position.set(0, 0, 2.5); 1220 editCamera.lookAt(0, 0, 0); 1221 editControls.target.set(0, 0, 0); 1222 editControls.update(); 1223 }, 1224 function (xhr) { 1225 const percentComplete = xhr.loaded / xhr.total * 100; 1226 editLoadingText.textContent = `Loading: ${Math.round(percentComplete)}%`; 1227 }, 1228 function (error) { 1229 console.error(`Error loading ${format.toUpperCase()} for edit result ${resultId}:`, error); 1230 editLoadingText.textContent = 'Error loading 3D model'; 1231 editLoadingText.style.fontSize = '0.9em'; 1232 } 1233 ); 1234 } 1235 1236 // Generate tabs 1237 const editTabsContainer = document.getElementById('edit-tabs-container');
1238 editResults.forEach((result, index) => { 1239 const li = document.createElement('li'); 1240 if (index === 0) li.className = 'is-active'; 1241 1242 li.innerHTML = `<a>${result.prompt}</a>`; 1243 1244 li.addEventListener('click', () => { 1245 // Update active state 1246 document.querySelectorAll('#edit-tabs-container li').forEach((tab, idx) => { 1247 if (idx === index) { 1248 tab.className = 'is-active'; 1249 } else { 1250 tab.className = ''; 1251 } 1252 }); 1253 1254 const format = result.format || 'ply'; 1255 loadEditModel(`./static/results/${result.file}`, result.prompt, format); 1256 }); 1257 1258 editTabsContainer.appendChild(li); 1259 }); 1260 1261 // Initialize viewer and load first model 1262 initEditViewer(); 1263 loadEditModel(`./static/results/${editResults[0].file}`, editResults[0].prompt, editResults[0].format); 1264 </script>
1264 1265 </div> 1266 </div> 1267 </div> 1268</section> 1269 1270 1271<div class="container is-max-desktop"> 1272 <div class="columns is-centered"> 1273 <!-- Visual Effects. --> 1274 <div class="column"> 1275 <div class="content"> 1276 <h2 class="title is-3">Method Overview</h2> 1277 <p> 1278 Watch our 5-minute voice-over explanation to understand how InfiniHuman works. 1279 This video provides a comprehensive overview of our approach, from data generation to the final controllable 3D human creation pipeline. 1280 </p> 1281 <video id="dollyzoom" autoplay controls muted loop playsinline height="100%"> 1282 <source src="./static/videos/infinihuman_5min.mp4" 1283 type="video/mp4"> 1284 </video> 1285 </div> 1286 </div> 1287 <!--/ Visual Effects. --> 1288 1289 </div> 1290 <!--/ Matting. --> 1291 </div> 1292 1293</section> 1294 1295 1296<section class="section"> 1297 <div class="container is-max-desktop"> 1298 1299 <!-- Concurrent Work. --> 1300 <div class="columns is-centered"> 1301 <div class="column is-full-width"> 1302 <h2 class="title is-3">Acknowledgement</h2> 1303 1304 <div class="content has-text-justified"> 1305 <p> 1306 We appreciate our friends and colleagues in RVH for their feedback to improve the work. 1307 This work is made possible by funding from the Carl Zeiss Foundation. 1308This work is also funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) - 409792180 (EmmyNoether Programme, project: Real Virtual Humans) and the German Federal Ministry of Education and Research (BMBF): Tübingen AI Center, FKZ: 01IS18039A. 1309The authors thank the International Max Planck Research School for Intelligent Systems (IMPRS-IS) for supporting Y.Xue. 1310G. Pons-Moll is a member of the Machine Learning Cluster of Excellence, EXC number 2064/1 â Project number 390727645. 1311 </p> 1312 </div> 1313 </div> 1314 </div> 1315 <!--/ Concurrent Work. --> 1316 1317 </div> 1318<br> 1319<br> 1320<center> 1321 <div class="imagesrow"> 1322 <img class="row_img" src="static/images/Carl-Zeiss-Stiftung_Logo.png" alt="Carl-Zeiss-Stiftung" style="max-width:5%;"> 1323 <img class="row_img" src="static/images/ai_center_3.png" alt="Tübingen AI Center" style="max-width:7%;"> 1324 <img class="row_img" src="static/images/Tuebingen_logo_scaled.png" alt="University of Tübingen" style="max-width:7%;"> 1325 <img class="row_img" src="static/images/IMPRS-IS.png" alt="IMPRS" style="max-width:5%;"> 1326 <img class="row_img" src="static/images/mpi-logo-new.svg" alt="mpi-inf" style="max-width:8%;"> 1327 </div> 1328</center> 1329</section> 1330 1331 1332<section class="section" id="BibTeX"> 1333 <div class="container is-max-desktop content"> 1334 <h2 class="title">BibTeX</h2> 1335 <pre><code>@article{xue2025infinihuman, 1336 author = {Xue, Yuxuan and Xie, Xianghui and Kostyrko, Margaret and Pons-Moll, Gerard}, 1337 title = {InfiniHuman: Infinite 3D Human Creation with Precise Control}, 1338 booktitle = {SIGGRAPH Asia 2025 Conference Papers}, 1339 year = {2025}, 1340}</code></pre> 1341 </div> 1342</section> 1343 1344<footer class="footer"> 1345 <div class="container"> 1346 <div class="content has-text-centered"> 1347 <a class="icon-link" 1348 href="./paper/infinihuman.pdf"> 1349 <i class="fas fa-file-pdf"></i> 1350 </a> 1351 <a class="icon-link" href="https://github.com/yuxuansnow/InfiniHuman" class="external-link" disabled> 1352 <i class="fab fa-github"></i> 1353 </a> 1354 </div> 1355 <div class="columns is-centered"> 1356 <div class="column is-8"> 1357 <div class="content"> 1358 <p> 1359 The website template is from <a 1360 href="https://github.com/nerfies/nerfies.github.io">Nerfies</a> and <a 1361 href="https://yuxuan-xue.com/nsf">NSF</a>. 1362 </p> 1363 </div> 1364 </div> 1365 </div> 1366 </div> 1367</footer> 1368 1369</body> 1370</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.