你好,我是 Maneshwar,我正在构建 LiveReview —— 一个感知爆炸半径的 AI 代码审查工具,专为你的业务关键系统打造。给我们点个 Star帮助开发者发现这个项目,试用一下,并分享你的反馈来帮助改进产品。
是的,那是 Miles Morales。
他荡着蛛丝落下,摆出超级英雄的蹲姿,向屏幕外射出一道蛛丝,把一块巨大的 DEV 板拽进画面。
那一帧我都没有手动制作动画。
而且在这周之前我从未打开过 Blender。
这篇文章讲述的就是这一切是如何发生的,以及一个“简单”的 3D 角色背后隐藏着多么惊人的计算机科学。
为什么偏偏是 Blender
我当时想做一段视频。
不是屏幕录制,而是一段真正有东西在动的视频。
我的第一站是HyperFrames,它可以让你用 HTML 编写视频并将其渲染出来。
它在动态图形方面确实非常出色。
滑入的文字、弹出的标志、自己绘制出来的图表,所有这些都很棒。
但当我想要一个角色去做点什么的时候,它就撞墙了。
HTML 可以很漂亮地移动矩形。
它没法让一个人荡着蛛丝飞。
所以下一个问题就很明显了,虽然有点吓人:真正的动画师用什么?
答案,就免费开源而言,是Blender。
而 Blender 有一个 MCP 服务器,这意味着我不会独自学习它。
驱动这一切的 MCP
让这一切对一个完全的新手成为可能的东西是blender-mcp。
它把 Blender 连接到像 Claude Code 这样的 LLM 客户端,所以你不必在 400 个菜单里翻找,只需说“加载这个模型并重新链接它的纹理”,它就会完成。
它的架构值得理解,因为它比听起来要简单。
flowchart LR
ME[Me typing a prompt] --> CC[Claude Code]
CC -->|MCP over stdio| SRV[blender-mcp server]
SRV -->|JSON over TCP socket| ADDON[Blender add-on]
ADDON -->|runs Python| BPY[bpy API]
BPY --> SCENE[Live Blender scene]
SCENE -->|viewport screenshot| CC
classDef human fill:#e9ecef,stroke:#6c757d,color:#1a1a1a
classDef agent fill:#9d8cff,stroke:#5b4bcc,color:#1a1a1a
classDef bridge fill:#6ea8ff,stroke:#2f62c4,color:#1a1a1a
classDef blender fill:#ff9a5c,stroke:#c4602a,color:#1a1a1a
class ME human
class CC agent
class SRV,ADDON bridge
class BPY,SCENE blender
它分为两半。
Blender 内部的一个插件会打开一个小型 socket 服务器并等待命令。
Blender 外部的一个 MCP 服务器一侧用 Model Context Protocol 与 LLM 对话,另一侧则通过那个 socket 向下转发 JSON 命令。
它暴露的最强大的工具是execute_blender_code,它通过bpy(Blender 的 Python API)在 Blender 内运行任意 Python 代码。
Blender 里的每个按钮都只是bpy之上的一层薄薄的皮。
所以一个能写bpy的智能体,原则上就能按下每一个按钮。
让它运转起来的循环,是反向回来的截图工具。
智能体改变某些东西,抓取视口,查看它,然后决定接下来修复什么。
这和你对代码使用的编辑、运行、检查循环是一样的,只是指向了一个 3D 场景。
一个诚实的警告:那个工具在设计上就是任意代码执行。像对待任何带 shell 的本地自动化一样对待它,不要把它指向一台满是机密的机器。
第一步:获取一个角色
我不是 3D 建模师,也不打算在周五之前变成一个。
所以我抓来了一个现成的 Miles Morales 模型,也就是 Spider-Verse 里的 Spider-Man,来自Sketchfab。
即便是“只是加载它”也包含了不少教训。
这个文件里带了四个排成一排的 Miles 副本:戴面具的和不戴面具的,摆好姿势的和 T 字姿势的。
纹理指向了原作者 Windows 文件夹里的路径,比如C:\Users\someone\Desktop\...,所以智能体不得不把它们重新链接到 zip 里的文件。
而且这个 zip 缺少法线贴图和镜面反射贴图。
在 Blender 中,指向缺失文件的纹理节点会渲染成亮粉色。
这相当于 3D 里的 404,而且非常、非常显眼。
修复方法是断开那些插槽,代价是战衣表面损失了一些细节。
然后我打开了不戴面具的 Miles,他的脸看起来像恐怖片,布满白线和白点。
结果那张脸其实没问题。
那个模型自带一个143 根骨骼的面部绑定(眼睑、眉毛、嘴唇、下颌、脸颊,甚至还有舌头),而骨架被设置为“在前面绘制”,所以那些骨骼全都被画在了他的皮肤上面。
勾选一个复选框之后,他又变帅了。
解锁一切的概念:绑定
我在这里学到了这篇文章其余部分都依赖的那个词。
一个 3D 角色,其核心是一个网格:一大串顶点(Miles 大约有 27k 个面)粘合成三角形。
网格就是一尊雕像。
你没法让一尊雕像挥手。
要让它动起来,你还需要三样东西。
一个骨架,也就是一副骨骼。
它是一棵由骨骼组成的树,每一根都是下一根的父亲:髋部到脊柱到胸部到肩部到上臂到前臂到手到手指。
旋转肩部,树中它下面的一切都会跟着动,就像任何游戏引擎里的变换层级,或者嵌套的<div>配一个 CSStransform。
蒙皮权重,它们把网格绑定到骨架上。
每个顶点都存储一个小表,说明每根骨骼对它的影响有多大。
前臂上的一个顶点可能是 100%forearm。
正好在肘部的一个顶点可能是 50%upper_arm和 50%forearm,这正是让肘部能平滑弯曲、而不是像纸吸管那样折起来的原因。
这套数学叫做线性混合蒙皮,它其实就是矩阵的加权平均:
final_position(v) = Σ weight(v, bone) × bone_matrix × rest_position(v)
bones
一个静止姿势,也就是一切测量的参考基准。
这就是为什么你下载的每个角色都站在一个T-pose,双臂笔直伸出。
这不是什么舞蹈动作。
它是零点,是身体的git init,而每个动画都以旋转的形式存储,相对于它。
把这些组合起来,你就有了一个绑定。
然后这个天真的计划就崩了,因为模型的绑定有骨骼,却没有控制器。
正向运动学 vs 反向运动学
我看过一些 YouTube 视频,动画师抓住角色的胯部往下拖,膝盖就弯了,而脚还牢牢粘在地板上。
当我拖动 Miles 的胯部时,他整个身体像幽灵一样穿过地板飘了下去。
这其中的差别是整个领域里最妙的想法之一。
正向运动学(FK)就是原始骨架所做的事。
你设定每个关节的角度,手最终落在旋转链把它放到的任何位置。
计算容易,做动画却很痛苦。想让手放在门把手上?祝你好运,去猜出能把它放到那里的肩、肘、腕角度吧。
反向运动学(IK)把问题反了过来。
你说出手(或脚)应该在哪里,求解器就反向推算出能把它放到那里的关节角度。
这字面上就是一个每帧都在求解的优化问题,而 Blender 自带一个 IK 约束可以替你完成这件事。
用 IK 目标把脚钉住,把胯部往下拖,求解器就不得不弯曲膝盖,因为脚不允许移动。
这就是那些 YouTube 视频里的魔法。
这不是魔法,这是一个约束求解器。
不过,手工搭建一套完整的 IK 控制绑定是一项真正的技能。
而我想让 Miles 这周就会做后空翻,不是下个季度。
Mixamo:绑定即服务
登场的是Mixamo,Adobe 的免费自动绑定器和动画库。
流程是:把你的角色以 T-pose 导出为 FBX,上传它,Mixamo 就会为你构建骨架和蒙皮权重。
它唯一要求你做的,就是放置几个标记点。
下巴、手腕、手肘、膝盖、腹股沟。
五个标记点,开启对称后,实际上每侧只需点三次。
根据这些点,它会推断出整个骨架,这很好地提醒了我们人体有多少结构。如果你知道关节在哪里,剩下的主要就是比例问题。
位置的重要性超出你的想象。
我第一遍把手腕放在了前臂上,这会导致他每次射蛛丝时手都在错误的位置弯曲。
我选了Standard Skeleton (65),它包含手指骨骼,因为一个没有手指的双手拉拽动作看起来就像戴着手套的人。
然后它就……成功了。
绑定好的角色可以直接导入 Mixamo 的动作捕捉动画库。
这些是真人穿着动捕服表演、录制并清理过的,所以它们有手 K 动画难以伪造的重心转移和细微晃动。
跳跃、攀爬、悬挂、投掷、跳舞、摔倒,应有尽有。
有一个代价,而且有点让人心疼。
Mixamo 构建的是它自己的身体骨架。
它不知道也不关心模型自带的那个漂亮的 143 骨骼面部绑定。
"With skin" 和 "without skin"
当你下载动画时,Mixamo 会问你是要with skin还是without skin。
这是关于分离数据与呈现的一个很好的小教训。
With skin会给你网格、骨架和动作。
Without skin只给你骨架和动作,几百 KB 的骨骼旋转随时间变化的数据。
因为每个片段都针对同一个 65 骨骼骨架、使用相同的骨骼名称,所以你只需要 skin一次。
其余的都只是按骨骼名称记录的 keyframe 数据,Blender 会很乐意把它应用到任何骨骼匹配的 armature 上。
所以我下载了第一个片段带 skin,其余的不带。
而且我下载了很多。
七十八个文件。
一个一个点的,因为 Mixamo 没有多选功能。
有些文件内部其实是不同的动画却共享同一个名字,因为网站只是在你上次下载的文件名后面追加(1)。
它们不是同一张图。
智能体最终扫描了每一个 FBX,看看里面到底是什么,才敢相信任何一个文件名。
从一堆片段到一场表演
以下是整个流程,从头到尾:
flowchart TD
A[Sketchfab model] --> B[Clean up in Blender<br/>relink textures, drop pink slots]
B --> C[Export T-pose FBX]
C --> D[Mixamo auto-rig<br/>5 markers]
D --> E[Download clips<br/>1 with skin, rest without]
E --> F[Import as Blender actions]
F --> G{Clip looks right<br/>from this angle?}
G -->|no| E
G -->|yes| H[Chain clips on NLA<br/>crossfades + root motion]
H --> I[Webs, board, camera]
I --> J[360p preview render]
J --> K{Good enough?}
K -->|no| H
K -->|yes| L[Final render]
classDef decision fill:#f4d35e,stroke:#b8991f,color:#1a1a1a
classDef source fill:#e9ecef,stroke:#6c757d,color:#1a1a1a
classDef rig fill:#5ee6c8,stroke:#1f9c86,color:#1a1a1a
classDef scene fill:#9d8cff,stroke:#5b4bcc,color:#1a1a1a
classDef out fill:#ff9a5c,stroke:#c4602a,color:#1a1a1a
class G,K decision
class A,B,C source
class D,E,F rig
class H,I scene
class J,L out
每个 Mixamo 片段在 Blender 中都会变成一个动作,即一组命名的动画曲线(每根骨骼每个旋转通道对应一条 f-curve)。
单独来看,一个动作就是一个单一的动作。
一段表演是若干个动作连在一起,而这正是 Blender 的非线性动画编辑器的用途。
NLA 本质上就是动画的视频编辑器时间线。
每个动作变成一个片段,片段放在轨道上,重叠的片段可以混合到一起。
正是这种混合,才让从“悬挂”到“落地”的切换不会像坏掉的 GIF 那样生硬跳变。
智能体把这一切封装进了一个小小的序列器,于是镜头被描述成一串动作,而不是一堆帧号:
perf = Performer(rig)
perf.then("Hanging Idle", length=40)
perf.then("Hard Landing", to=50, speed=1.3, blend=6)
perf.then("Standing 1H Magic Attack 02", frm=16, to=40, speed=1.25, blend=6)
perf.then("Pull Heavy Object Stop", to=30, speed=1.1, blend=5, face=65)
perf.then("Victory Idle", blend=8, face=10, length=51)
perf.build() # NLA 片段、交叉淡化、拼接的根运动
blend=6是六帧的交叉淡化,过渡到下一个片段。
face=65把他转了 65 度,让拉扯的方向指向滑板。
而那个“蛛网发射”,我保证,是一个字面意义上名为Magic Attack的片段。Mixamo 并没有“thwip”这个动作。
隐藏在build()中的有趣问题是根运动。
有些片段会让角色在空间中移动(落地会让他下坠,拉扯会让他后仰),而每个片段都从自己的原点开始。
天真地把其中两个背靠背播放,Miles 就会在动作之间瞬移回起点。
所以序列器会读取一个片段中臀部结束的位置,并偏移下一个片段,让它从那里开始。
这和把 GPS 轨迹拼接起来是同一个思路:每一段都是相对的,所以你保持一个累加和。
因为所有时序都源自那个列表,替换一个片段就会自动重新调整蛛网、滑板和摄像机的时序。
蛛网丝、滑板和摄像机完全不是 Mixamo 的,它们是普通的 Blender 对象,由脚本打关键帧,在他伸手的那一帧触发。
用 360p 迭代,像个正常人一样
第一次完整渲染是 720p 下 16 秒,在我的 GTX 1650 上花了大约八分钟。
一次还行。
当反馈同时是“让它更利落”、“把板子做大”和“他的西装太暗了”时,情况就不妙了。
所以预览降到了 360p,大约快了 4 倍,而 720p 成了只有决赛才用的东西。
你在代码里熟知的那条规则同样适用:先让反馈循环变快,再让输出变好。
还有两件事耗费了真正的时间:
西装渲染成了一团扁平的黑色块。
模型用的是卡通着色器,它把光照压缩成几道生硬的色带,这在卡通里看着很棒,但当你想看到肌肉时就糟透了。
换成基于物理的着色,再加上一盏专门照他的灯以及红蓝轮廓光,他的形体就回来了。
浅灰色还是深色影棚?
近乎黑色的西装配上近乎黑色的背景,就是一个什么都没有的剪影。
浅色背景更易读。深色背景更酷。顶部的 GIF 是深色的那个,因为我有优先级。
逃离下载按钮
在点击了 78 次“Download”之后,我问了那个显而易见的工程师问题:我能不能就在本地做这件事?
Mixamo 的绑定器和素材库只存在于 Adobe 的服务器上。
但动作捕捉数据并不是 Adobe 发明的。
TheCMU Graphics Lab Motion Capture Database大约有 2,500 个免费片段,而且 GitHub 上有一个BVH 镜像还带有一个文本索引,说明每个片段是什么。
侧手翻、萨尔萨舞、攀爬、武术、“拉绳子”,全都有。
问题在于 CMU 的骨架不是 Mixamo 的骨架。
把动作从一个骨架转移到另一个骨架叫做重定向,而这是一个很可爱的小线性代数问题。
你不能直接把旋转复制过去,因为两副骨架有不同的静止姿势。从“手臂略微下垂”旋转 30 度,和从“手臂完全伸直”旋转 30 度,并不是同一个姿势。
所以重定向器会把每根骨骼的旋转从源静止姿势中转换出来,进入一个共享的世界坐标系,再转换回目标的静止姿势。
它还会按腿长缩放髋部的位移,这样一位高个表演者的步幅就不会让 Miles 走太空步。
第一次尝试渲染出来……什么都没有。
Miles 不在画面里。他哪儿都不在。
BVH 的静止姿势把髋部正好放在原点,所以“静止髋部高度”是零,而缩放系数就是除以它。
Miles 因为一次除以零被送上了轨道。
改为从片段中最低的那只脚测量地面后,问题解决了,突然之间他就能从多年前在 Carnegie Mellon 录制的数据集中走路、跳舞和翻跟头了。
我实际学到的东西
我本想让一个家伙荡蛛丝。
结果带回来一串我没想到会从一个美术工具里学到的概念:
- 角色是一个网格由一棵骨骼树通过逐顶点权重变形而来。
- 动画是相对于静止姿态的旋转,这就是为什么所有东西都摆成 T-pose。
- IK是一个约束求解器,它是木偶和尸体之间的区别。
- 动作片段是以骨骼名称为键的数据,所以一张皮肤可以穿上一千个动画。
- NLA 片段是可复用动作的时间线,把它们串起来就意味着拼接根运动。
- 重定向是一次基变换,如果你放任不管,它就会除以零。
- Blender 是一个上面套了 UI 的 Python 程序,这就是为什么一个MCP 桥接能够驱动它。
对 CS 背景的人来说里面还有一大堆东西:着色器节点图是数据流程序,Cycles 是路径追踪器,物理模拟是数值积分器,几何节点基本上就是一种针对网格的函数式语言。
但一步一步来。
清单上的下一项是我在那个泳池里残忍抛弃的面部绑定,也许还有让 Miles 真正开口说话。
如果你玩过 Blender、绑定或 MCP,告诉我接下来我该学什么。
你的团队注意力有限,而 AI 生成代码的洪流让在不拖慢你的前提下保持生产环境安全可靠变得更难。
我正在构建LiveReview,一个为你业务关键系统打造的、感知爆炸半径的 AI 代码审查工具。
LiveReview 不会对每个 diff 给予同等强调,而是按爆炸半径——即其影响通过你的调用图能波及多远——为每个变更打分,这样你就能把注意力集中在真正重要的地方。
把代码审查的精力花在业务风险最高的地方——而不是均匀分摊到每个 diff 上。
⭐ 在 GitHub 上给它点星:
LiveReview:面向业务关键系统的爆炸半径感知 AI 代码审查
LiveReview 是一个 AI 代码审查器,它按爆炸半径:一个改动在你的调用图中能波及多远、触及多少持久化状态,以及它的测试覆盖有多好。对共享鉴权检查的 3 行改动,优先级可能高于 300 行的 UI 微调。你团队的注意力会先投向风险最高的代码,而不是平均分散到每个 diff 上。
blast-radius-demo.mp4LiveReview 的 Blast Radius 与 Review Priority 评分,直接在 diff 查看器中呈现。
Blast Radius 评分是如何工作的?(更技术性的解释)
目标如下:
- 对一个被其他 40 个文件调用的函数做 3 行修复,而且它还会写入数据库,应该得到高分。
- 单个文件里 300 行的 UI 改动,完全被……覆盖
点击下方,用你的代码库试用 LiveReview:












