Spaces:
Paused
Paused
| <html lang="zh"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>TRELLIS 3D Demo</title> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <style> | |
| .preview-container { | |
| max-width: 100%; | |
| margin: 20px 0; | |
| } | |
| .preview-video { | |
| max-width: 100%; | |
| height: auto; | |
| } | |
| .loading { | |
| display: none; | |
| margin: 20px 0; | |
| } | |
| .progress { | |
| display: none; | |
| margin: 20px 0; | |
| } | |
| .error-message { | |
| display: none; | |
| color: red; | |
| margin: 10px 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container mt-5"> | |
| <h1 class="mb-4">TRELLIS 3D Demo</h1> | |
| <!-- 图片上传区域 --> | |
| <div class="card mb-4"> | |
| <div class="card-body"> | |
| <h5 class="card-title">上传图片</h5> | |
| <p class="card-text">请上传1-3张图片来生成3D模型</p> | |
| <input type="file" class="form-control" id="imageInput" multiple accept="image/*"> | |
| <div class="mt-3"> | |
| <button class="btn btn-primary" id="generateBtn" disabled>生成3D模型</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 生成设置 --> | |
| <div class="card mb-4"> | |
| <div class="card-body"> | |
| <h5 class="card-title">生成设置</h5> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="mb-3"> | |
| <label class="form-label">Stage 1: Sparse Structure Generation</label> | |
| <div class="mb-2"> | |
| <label class="form-label">Guidance Strength</label> | |
| <input type="range" class="form-range" id="ssGuidanceStrength" min="0" max="10" step="0.1" value="7.5"> | |
| <span id="ssGuidanceStrengthValue">7.5</span> | |
| </div> | |
| <div class="mb-2"> | |
| <label class="form-label">Sampling Steps</label> | |
| <input type="range" class="form-range" id="ssSamplingSteps" min="1" max="50" step="1" value="12"> | |
| <span id="ssSamplingStepsValue">12</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="mb-3"> | |
| <label class="form-label">Stage 2: Structured Latent Generation</label> | |
| <div class="mb-2"> | |
| <label class="form-label">Guidance Strength</label> | |
| <input type="range" class="form-range" id="slatGuidanceStrength" min="0" max="10" step="0.1" value="3.0"> | |
| <span id="slatGuidanceStrengthValue">3.0</span> | |
| </div> | |
| <div class="mb-2"> | |
| <label class="form-label">Sampling Steps</label> | |
| <input type="range" class="form-range" id="slatSamplingSteps" min="1" max="50" step="1" value="12"> | |
| <span id="slatSamplingStepsValue">12</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mb-3"> | |
| <label class="form-label">Multi-image Algorithm</label> | |
| <select class="form-select" id="multiimageAlgo"> | |
| <option value="stochastic">Stochastic</option> | |
| <option value="multidiffusion">Multi-diffusion</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 加载状态 --> | |
| <div class="loading text-center"> | |
| <div class="spinner-border text-primary" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| <p class="mt-2">正在生成3D模型,请稍候...</p> | |
| </div> | |
| <!-- 进度条 --> | |
| <div class="progress"> | |
| <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div> | |
| </div> | |
| <!-- 错误信息 --> | |
| <div class="error-message alert alert-danger"></div> | |
| <!-- 预览区域 --> | |
| <div class="preview-container"> | |
| <h3>预览</h3> | |
| <video id="previewVideo" class="preview-video" controls style="display: none;"></video> | |
| </div> | |
| <!-- GLB 下载区域 --> | |
| <div class="card mb-4" id="glbCard" style="display: none;"> | |
| <div class="card-body"> | |
| <h5 class="card-title">GLB 文件</h5> | |
| <p class="card-text">3D模型已生成,点击下方按钮下载GLB文件</p> | |
| <button class="btn btn-success" id="downloadGlbBtn">下载GLB文件</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const API_BASE_URL = 'https://demohug-trellis-multiple3d.hf.space'; | |
| let currentSessionId = null; | |
| // 更新滑块值显示 | |
| document.querySelectorAll('input[type="range"]').forEach(input => { | |
| const valueDisplay = document.getElementById(input.id + 'Value'); | |
| input.addEventListener('input', () => { | |
| valueDisplay.textContent = input.value; | |
| }); | |
| }); | |
| // 检查文件选择 | |
| document.getElementById('imageInput').addEventListener('change', function() { | |
| const generateBtn = document.getElementById('generateBtn'); | |
| generateBtn.disabled = this.files.length < 1 || this.files.length > 3; | |
| }); | |
| // 生成3D模型 | |
| document.getElementById('generateBtn').addEventListener('click', async function() { | |
| const files = document.getElementById('imageInput').files; | |
| if (files.length < 1 || files.length > 3) { | |
| showError('请选择1-3张图片'); | |
| return; | |
| } | |
| // 显示加载状态 | |
| showLoading(true); | |
| hideError(); | |
| try { | |
| // 准备表单数据 | |
| const formData = new FormData(); | |
| for (let file of files) { | |
| formData.append('files', file); | |
| } | |
| // 添加生成参数 | |
| const params = { | |
| seed: 0, | |
| ss_guidance_strength: parseFloat(document.getElementById('ssGuidanceStrength').value), | |
| ss_sampling_steps: parseInt(document.getElementById('ssSamplingSteps').value), | |
| slat_guidance_strength: parseFloat(document.getElementById('slatGuidanceStrength').value), | |
| slat_sampling_steps: parseInt(document.getElementById('slatSamplingSteps').value), | |
| multiimage_algo: document.getElementById('multiimageAlgo').value | |
| }; | |
| formData.append('params', JSON.stringify(params)); | |
| // 发送生成请求 | |
| const response = await fetch(`${API_BASE_URL}/api/generate`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (!response.ok) { | |
| throw new Error('生成失败'); | |
| } | |
| const result = await response.json(); | |
| currentSessionId = result.session_id; | |
| // 显示预览视频 | |
| const video = document.getElementById('previewVideo'); | |
| video.src = `${API_BASE_URL}${result.preview_url}`; | |
| video.style.display = 'block'; | |
| video.play(); | |
| // 显示GLB下载按钮 | |
| document.getElementById('glbCard').style.display = 'block'; | |
| } catch (error) { | |
| showError(error.message); | |
| } finally { | |
| showLoading(false); | |
| } | |
| }); | |
| // 下载GLB文件 | |
| document.getElementById('downloadGlbBtn').addEventListener('click', async function() { | |
| if (!currentSessionId) { | |
| showError('请先生成3D模型'); | |
| return; | |
| } | |
| showLoading(true); | |
| hideError(); | |
| try { | |
| // 提取GLB文件 | |
| const extractResponse = await fetch(`${API_BASE_URL}/api/extract_glb`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| session_id: currentSessionId, | |
| params: { | |
| mesh_simplify: 0.95, | |
| texture_size: 1024 | |
| } | |
| }) | |
| }); | |
| if (!extractResponse.ok) { | |
| throw new Error('GLB提取失败'); | |
| } | |
| const extractResult = await extractResponse.json(); | |
| // 下载GLB文件 | |
| const glbResponse = await fetch(`${API_BASE_URL}${extractResult.glb_url}`); | |
| if (!glbResponse.ok) { | |
| throw new Error('GLB下载失败'); | |
| } | |
| const blob = await glbResponse.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'model.glb'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| document.body.removeChild(a); | |
| } catch (error) { | |
| showError(error.message); | |
| } finally { | |
| showLoading(false); | |
| } | |
| }); | |
| // 辅助函数 | |
| function showLoading(show) { | |
| document.querySelector('.loading').style.display = show ? 'block' : 'none'; | |
| document.getElementById('generateBtn').disabled = show; | |
| } | |
| function showError(message) { | |
| const errorDiv = document.querySelector('.error-message'); | |
| errorDiv.textContent = message; | |
| errorDiv.style.display = 'block'; | |
| } | |
| function hideError() { | |
| document.querySelector('.error-message').style.display = 'none'; | |
| } | |
| </script> | |
| </body> | |
| </html> |