真实接口预热

This commit is contained in:
yinsx
2026-01-04 09:58:26 +08:00
parent b56492f80f
commit 2bd183463d
10 changed files with 386 additions and 207 deletions

View File

@ -27,6 +27,10 @@ function init() {
});
animator.setMorphTargetAdapter(morphAdapter);
animator.scene = sceneManager.scene;
// 把动画更新挂到 Babylon 渲染循环
sceneManager.onBeforeRender = () => animator.tick();
// 导出全局变量供表情库使用
window.animator = animator;
@ -41,7 +45,7 @@ function init() {
// 加载3D模型
sceneManager.loadModel('head_a01.glb',
(meshes) => {
async (meshes) => {
showStatus("模型加载成功", "success");
const totalTargets = morphAdapter.buildCache(meshes);
@ -50,43 +54,39 @@ function init() {
} else {
console.log(`✓ 构建缓存完成,共 ${totalTargets} 个形态键`);
// 预热着色器以避免首次动画卡顿
console.log('预热着色器中...');
morphAdapter.warmupShaders(sceneManager.scene);
console.log('✓ 着色器预热完成');
// 播放120帧预热动画4秒
console.log('预热渲染管线中...');
const dummyFrames = [];
const allBlendShapes = Object.keys(morphAdapter.morphTargetCache);
console.log(`使用 ${allBlendShapes.length} 个blendshapes进行预热`);
for (let i = 0; i < 120; i++) {
const blendShapes = {};
const factor = Math.sin(i * 0.12) * 0.5 + 0.5;
allBlendShapes.forEach((name, index) => {
const phase = (i + index * 5) * 0.18;
const value = Math.sin(phase) * 0.5 + 0.5;
if (value > 0.25) {
blendShapes[name] = value * factor * 0.7;
// 预热:直接调用生成动画流程(和用户点击按钮完全一样)
showStatus("预热中...", "info");
const apiUrl = document.getElementById('apiUrl').value;
const streamEnabled = document.getElementById('streamEnabled')?.checked;
console.log('预热 apiUrl:', apiUrl, 'stream:', streamEnabled);
if (apiUrl) {
try {
// 和 generateAnimation 走完全一样的路径
if (streamEnabled) {
await generateAnimationStream('你好', apiUrl);
} else {
await generateAnimationBatch('你好', apiUrl);
playAnimation();
}
});
dummyFrames.push({
timeCode: i / 30,
blendShapes: blendShapes
});
// 等播放完
await new Promise(resolve => {
const check = () => animator.isPlaying ? requestAnimationFrame(check) : resolve();
check();
});
// 完全重置状态
animator.stopAnimation();
animator.loadAnimationFrames([]);
animator.endStreaming();
morphAdapter.resetAll();
console.log('✓ 预热完成');
} catch (e) {
console.warn('预热失败:', e);
}
} else {
console.warn('预热跳过: apiUrl 为空');
}
animator.loadAnimationFrames(dummyFrames);
animator.playAnimation();
setTimeout(() => {
animator.stopAnimation();
animator.loadAnimationFrames([]);
console.log('✓ 渲染管线预热完成');
}, 4000);
showStatus("就绪", "success");
}
},
(message) => {
@ -95,6 +95,44 @@ function init() {
);
}
// 偷偷调接口预热 - 完全走一遍生成动画流程
async function warmupWithApi() {
console.log('=== warmupWithApi 开始 ===');
const apiUrl = document.getElementById('apiUrl').value;
console.log('apiUrl:', apiUrl);
try {
// 完全走一遍 generateAnimationBatch 流程
console.log('调用 generateAnimationBatch...');
await generateAnimationBatch('你', apiUrl);
console.log('generateAnimationBatch 完成, frames:', animator.animationFrames.length);
// 播放动画
console.log('调用 playAnimation...');
animator.playAnimation();
// 等待播放完成
await new Promise(resolve => {
const checkDone = () => {
if (!animator.isPlaying) {
resolve();
} else {
requestAnimationFrame(checkDone);
}
};
requestAnimationFrame(checkDone);
});
// 清空
animator.loadAnimationFrames([]);
morphAdapter.resetAll();
console.log('✓ 预热完成');
} catch (e) {
console.warn('预热失败:', e.message, e);
}
}
async function generateAnimation() {
const text = document.getElementById('textInput').value.trim();
const apiUrl = document.getElementById('apiUrl').value;
@ -185,6 +223,10 @@ async function generateAnimationStream(text, apiUrl) {
const handleMessage = (message) => {
if (message.type === 'frame') {
pendingFrames.push(message.frame);
// 每100帧打印一次进度
if (pendingFrames.length % 100 === 0) {
console.log(`[前端] 已接收 ${pendingFrames.length} 帧, sentenceIndex=${message.frame.sentenceIndex}`);
}
flushFrames();
return;
}