lhg-mv-engine:纯代码渲染引擎
目标:整部片子是一个时间函数,同一输入永远同一帧。 核心纪律:seek(t) 是唯一可信源;先看静帧,再全量渲染。
1. 何时使用
- 决定"怎么写"这条片子时:读第 3.1–3.2 节定路线
- 写渲染代码时:按第 3.3 节的 seek(t) 模式
- 渲染前:按第 3.4 节出探针静帧
- 动画发虚/卡顿:看第 3.5 节动态模糊与性能
2. 输入 / 输出
输入:拍板后的 brief.md(含分镜表与拍点网格)。
输出:film.json(宽高/帧率/时长/场景,唯一可信源)+ 渲染 HTML(含 seek(t))+ probe/ 静帧 + 全量帧序列。
3. 分步方法论
3.1 为什么用纯代码,不用剪辑软件
| 维度 | 纯代码(HTML+无头浏览器+ffmpeg) | 剪辑软件 |
|---|---|---|
| 确定性 | seek(t) 函数,修 A 场景不影响 B | 时间轴手工拖,易误触 |
| 成本 | 免费(浏览器+ffmpeg 开源) | 订阅费/买断费 |
| AI 协作 | "转场再快 0.2 秒"=改个数字 | 得 reopen 工程手工调 |
| 版本管理 | git diff 看得见每一帧的逻辑 | 二进制工程文件 diff 无意义 |
一句话:动效片是"算"出来的,不是"拖"出来的。
3.2 film.json:唯一可信源
{
"title": "新品发布动效",
"width": 1920, "height": 1080, "fps": 30, "duration": 60,
"caption_safe_margin": 0.9,
"scenes": [
{"name": "开场", "duration": 3, "transition": "叠化", "desc": "Logo 入场"}
]
}
纪律:宽高帧率时长只在 film.json 里定一次,渲染 HTML、导出脚本都从它读(或人工对齐,审计脚本会查)。
3.3 seek(t):整部片子是一个函数
// 伪代码:时间 t(秒)→ 画面状态
function seek(t) {
const scene = sceneAt(t); // t 落在哪个场景
const lt = t - scene.start; // 场景内本地时间
drawBackground(lt);
drawTitle(easeOutCubic(lt / 1.2)); // 1.2 秒入场动画
if (lt > scene.duration - 0.5) drawTransition(lt); // 转场
}
三条铁律:
- t 是唯一输入:画面状态只由 t 决定,不许用"上一帧的状态"做累积(否则修一帧要重渲全片)。
- 缓动函数显式:所有动画写清 easing(easeOutCubic 入场、easeInOut 转场),不许线性硬切——线性动画是"AI 味"动效的头号来源。
- 运动语言三段式:每个动效元素走"预备(0.2s 蓄力)→ 冲击(主体运动)→ 回稳(0.3s overshoot 回弹)",片子立刻有"呼吸感"。
3.4 静帧先行:探针 workflow
- 渲染 3–5 张关键帧静帧到
probe/:开场定调帧、高潮记忆点帧、结尾行动帧必含。 - 用户确认:构图/配色/字体/字号。这一步改=改几个数字;渲染完再改=重渲几小时。
- 确认后才全量渲染。hook 会在你写渲染文件而无 probe/ 时提醒。
3.5 动态模糊与性能
- 子帧混合:高速运动元素发虚是物理正确的。对运动快的元素,一帧内采 2–4 个子时间点混合,解决"瞬移感"。
- 硬切处关模糊:转场切点前后各 1 帧不许混(否则切点变"糊点")。
- 性能:单帧渲染>200ms 就要优化(降分辨率探针/减元素);全量渲染前先算"帧数×单帧耗时"报预估。
3.6 素材纪律
- 全部本地化:
assets/目录,外链一律下载进来。外链 404=渲染失败,外链变了=成片变了。 - 文件名:小写英文连字符(如
logo-main.png),空格/中文在 ffmpeg 里是转义地狱。 - 大文件:视频素材先压到渲染分辨率,别拿 4K 原片进 1080p 时间线。
3.7 多分辨率适配:一源多发
- 横屏母版优先:先做 1920x1080 横屏版,竖屏版重排分镜——不是简单裁剪。
- 为什么不能裁:裁剪会切掉字幕和视觉主体;竖屏的信息密度和横屏完全不同,分镜要重写。
- 复用策略:seek(t) 逻辑复用,布局参数按画幅重调;film.json 里给每个画幅建独立条目,审计脚本逐条查。
4. 反模式
- ❌ 时间轴手工拖完导出——改一个字重做一遍,AI 协作优势全丢
- ❌ 所有动画线性——"PPT 翻页感"的来源,缓动函数是免费的
- ❌ 跳过探针直接全量渲染——返工按分钟计费
- ❌ 画面状态依赖上一帧——修一帧重渲全片,确定性全毁
- ❌ 素材用外链——"在我机器上能渲"是动效界最贵的五个字
5. 常见问题
Q:一定要无头浏览器吗?canvas 录屏行不行? A:行,但要保证帧率恒定。无头浏览器的价值是"逐帧、可复现";录屏的价值是快。探针阶段用录屏,全量用逐帧。
Q:3D 元素怎么办? A:WebGL 写进同一个 seek(t) 体系,时间照样是唯一输入。别为了 3D 引入第二套时间轴。
Q:渲染太慢? A:三板斧:降探针分辨率(540p 看构图够了)、减子帧采样、把静态背景预渲染成一张图。先报预估耗时再开跑。
Q:字体在不同机器渲染不一致? A:字体文件放进 assets/ 本地引用,不许依赖系统字体。差一个字重,slogan 的气质就变了。
Q:seek(t) 里想加随机粒子效果? A:随机=不确定。用带种子的伪随机(seeded random),种子写进 film.json。同一输入同一帧,铁律不破。
Q:探针静帧和最终成片色差? A:探针和全量用同一套渲染代码、同一色彩配置。探针只是降分辨率+抽帧,不许走另一套管线。
Q:seek(t) 写起来太啰嗦? A:抽公共函数:场景调度器、缓动库、转场库各一个文件。第三个场景开始,写新场景就是填表。
6. 自检与反馈
渲染方案完成自检:
- film.json 四要素齐全且与 brief 一致吗?
- seek(t) 只依赖 t 吗?缓动函数显式吗?
- probe/ 有 3–5 张关键帧且用户确认了吗?
- 素材全本地化、文件名合规吗?
- 报过渲染耗时预估了吗?
标准冒烟用例:用 fixtures/film-good 跑审计脚本,确认 28 条规则零 blocker;改坏一处(如 canvas 尺寸)确认对应规则命中。
反馈渠道:以 [QC] <一句话问题> 为标题提交到 https://github.com/lhg-plugs/lhg-motion-video/issues,正文写清"输入 → 错误输出 → 期望输出"。
出品:刘洪光
本 skill 由真人出镜 IP「刘洪光」(安徽合肥)出品,归属 lhg-skills。
- GitHub 主页:https://github.com/lhg-skills —— 全部 skill 开源在此,欢迎 star
- 视频号:搜「刘洪光实名上网」
- 微信:lhgsmsw