TRELLIS-Multiple3D / index.html
demohug's picture
v1
d9837fa
<!DOCTYPE html>
<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>