让公式动起来:Manim 三维物理动画从入门到完整场景的完整指南
2026/9/1 10:00:00 网站建设 项目流程

让公式动起来:Manim 三维物理动画从入门到完整场景的完整指南

【免费下载链接】manimA community-maintained Python framework for creating mathematical animations.项目地址: https://gitcode.com/GitHub_Trending/man/manim

如果课堂上的抛物线不再是一行印刷体公式,而是屏幕上真的有一颗球被"扔"出去、落地、弹起,学生的眼睛会先于耳朵理解物理。这正是Manim(Manim 3b1b,社区维护的 Python 数学动画框架)擅长的事:用几十行 Python 代码,把力学、电磁学这类抽象过程变成可播放、可暂停、可逐帧调参的三维动画。本文面向第一次接触 Manim 的读者,按"先跑起来 → 拆零件 → 装整机"的顺序,带你做出一个完整的阻尼振子场景。

Manim 是什么:先看清全貌

一句话概括:Manim 是一个声明式动画引擎——你描述"物体在某一时刻长什么样",它负责补间、渲染、编码成视频。它有三类核心能力,恰好覆盖物理动画的全部需求:

能力解决的问题对应源码
矢量图形与 3D 物体球、曲面、坐标系、轨迹线manim/mobject/three_d/three_dimensions.py
动画与更新器让物体按物理规律逐帧运动manim/animation/、manim/mobject/value_tracker.py
LaTeX 公式渲染把牛顿定律、洛伦兹力写进画面manim/utils/tex_templates.py

Manim 内部把几乎所有图形都拆成贝塞尔曲线段来存储与渲染(上图),这也是为什么"画一条电场线"和"画一个弹簧"在实现上没有本质区别——都是曲线,区别只在于你给的参数方程。

跑通第一个三维场景

环境准备与渲染命令

推荐用 conda 或 uv 安装(官方给了完整清单,见 docs/source/installation/conda.rst),随后任何脚本都用同一条命令渲染:

# -ql 低画质快速预览,-p 渲染完自动播放 manim -pql my_scene.py FirstBall

开发阶段永远加-ql,一遍渲染只要几秒;正式出片时去掉-q换回高清。

最小可运行示例:一颗转动的球

from manim import * class FirstBall(ThreeDScene): def construct(self): # phi/theta 是球面角:先"定机位",再放物体 self.set_camera_orientation(phi=70 * DEGREES, theta=45 * DEGREES) ball = Sphere(radius=1, resolution=(24, 24)) # resolution 控制表面细分,越大越圆越慢 ball.set_color(RED) self.add(ball) self.play(Rotate(ball, angle=TAU, axis=UP))

注意两个容易被忽略的点:

  • 相机角度在"放物体之前"设定ThreeDScene的机位决定之后所有物体的投影方式;
  • Sphere本质是一组参数曲面(继承自Surface),resolution=(24, 24)这类元组分别控制经度、纬度的采样密度,调它等于在"画质"和"速度"之间取平衡。

静态机位用默认渲染器就够了;如果之后想让相机环绕飞行,再切换到 OpenGL 渲染器(-r opengl,参见 docs/source/faq/opengl.md)。三维场景常用的现成素材(比如给场景当地图背景)可以直接放在example_scenes/assets/里参考用法:

三个工具让物体真正"动"起来

Manim 不是物理引擎,它不知道"质量"是什么。它提供的是一种更朴素的模式:你手动物理公式算出每一帧的位置,再喂给图形对象。实现这个模式的三件套如下。

3D 积木块:Sphere、Cube 与 Surface

三维几何件集中在 manim/mobject/three_d/three_dimensions.py,开箱可用的有SphereCubeCylinderConeTorusArrow3D等。构造参数以几何量(中心、半径、范围)为主,例如用u_range/v_range就能切出半个球:

half = Sphere(center=LEFT * 2, u_range=(0, PI)) # 只取半个方位角,得到一个球壳

配合 manim/mobject/three_d/three_d_utils.py 里的法向量工具函数,还可以做表面朝向、阴影之类的细节。

时间守门员:ValueTracker + updater

ValueTracker是物理动画里出现频率最高的角色:它把一个标量(通常是"时间 t")藏在一个 Mobject 里,通过animate语法随时间平滑变化,再让其他物体的 updater 每帧读取它(源码与官方示例见 manim/mobject/value_tracker.py)。

t = ValueTracker(0) # 存"时间" dot = Dot(color=BLUE) dot.add_updater(lambda m: m.move_to([1.5 * t.get_value(), 0, 0])) self.add(dot, t) # 关键:tracker 必须 add 进场景,updater 才会跑 self.play(t.animate.set_value(4), run_time=4)

这个"一个 tracker + 一群 updater"的结构,就是 Manim 里一切连续运动的底层协议——斜抛、轨道、振动全是它的变体。

把物理公式写进画面

MathTex默认就加载了physics宏包(预置模板定义在 manim/utils/tex_templates.py 中),所以\vec\ket\E\B这类物理符号可以直接用:

law = MathTex(r"\vec{F} = q(\vec{E} + \vec{v} \times \vec{B})") # 洛伦兹力,开箱即用 self.play(Write(law))

想自定义字体或加载别的宏包,可以自己构造TexTemplate传进tex_template参数,模板库里ctexsimple等变体都写在同一个文件里。

组装完整场景:一个会衰减的弹簧振子

把上面三件工具拼起来,就是完整的物理叙事。下面的阻尼振子没有调用任何"物理库",运动方程y = A·e^(−γt)·cos(ωt)是纯数学,动画只是它的逐帧求值:

import numpy as np from manim import * class DampedSpring(Scene): def construct(self): wall = Line(LEFT * 3, LEFT * 3 + UP * 2, color=GREY) # 固定端 mass = Square(side_length=0.6, color=BLUE) # 质量块 t = ValueTracker(0) A, omega, gamma = 1.2, 2.0, 0.35 # 振幅、角频率、阻尼系数 def follow_law(m): # 每帧:按阻尼简谐方程算出高度,写回物体位置 y = A * np.exp(-gamma * t.get_value()) * np.cos(omega * t.get_value()) m.move_to([0, 1 + y, 0]) mass.add_updater(follow_law) self.add(wall, mass, t) self.play(t.animate.set_value(3 * TAU), run_time=6) mass.clear_updaters() # 播完记得清掉 updater,避免残留更新

运行后可以立刻做"实验":把gamma改成 0 看等幅振荡,改大到 1.5 看欠阻尼变过阻尼式的快速归零——参数即剧情,这是纯代码做动画相对视频剪辑最大的优势。想要把画面与旁边的位移-时间曲线联动,思路也一样:再画一组Axes,用一个 updater 按同一公式描点即可。图片素材、表格、曲线混排的场景示例可在 example_scenes/basic.py 里对照阅读:

常见问题与调优

渲染器怎么选

场景建议原因
二维公式、静态三维截图默认 Cairo依赖少、速度快、画质稳定
相机环绕、实时窗口预览-r openglOpenGL 渲染器支持相机动态运动和交互窗口,见 docs/source/faq/opengl.md
正式出片-ql调好脚本,再去掉-q渲染高清渲染耗时与分辨率平方相关

渲染太慢怎么办

  • 降预览质量-ql(低画质)+-r 480,270(低分辨率)组合是调试期的默认姿势;
  • 用缓存:manim/utils/caching.py 提供基于哈希的缓存机制,未改动的中间结果(比如一张复杂Surface)会被复用,避免整段场景重复渲染;
  • 查热点:脚本卡在某一帧时,可用 snakeviz 这类火焰图工具定位是几何采样还是编码拖了后腿,官方性能指南里有完整的剖析流程:docs/source/guides/deep_dive.rst。

新手最容易踩的四个坑

  1. 给物体传massfriction参数——会直接报TypeError。Manim 的 Mobject 只关心几何,物理量请放进自己的变量里算,再由 updater 写回位置;
  2. 三维坐标系里 y 才是"上"[x, y, z]中 y 轴竖直向上,把高度写进第三个分量是新手最高频的错误;
  3. ValueTracker忘了self.add:tracker 不在场景里,挂在别人身上的 updater 不会更新,画面会"冻"在初始位置,这类 bug 没有任何报错,只能靠检查self.mobjects排查;
  4. 公式渲染失败MathTex编译依赖本机 LaTeX 环境,缺包时错误信息往往指向 tex 日志而非 Python 栈,装 conda 版或 Docker 版(docs/source/installation/docker.rst)可以一次性绕开。

资源入口与下一步

  • 官方文档总入口:docs/source/index.rst
  • 安装与依赖(conda / uv / Docker):docs/source/installation/
  • 可直接运行的场景范例:example_scenes/basic.py、example_scenes/opengl.py
  • 三维物体与曲面源码:manim/mobject/three_d/three_dimensions.py
  • 动画体系(Transform、Composition、Updaters):manim/animation/

从一颗转动的球到会衰减的弹簧振子,你已经走过了 Manim 物理动画的完整链路:定机位 → 摆积木 → 用ValueTracker注入时间 → 按方程求值。建议下一步把阻尼振子扩展成双摆,或者给点电荷画一组随时间转动的电场线——这两道题的每个零件,本文都已经给过了。想动手的话,先把仓库拉到本地,对着example_scenes/逐个运行:

git clone https://gitcode.com/GitHub_Trending/man/manim

【免费下载链接】manimA community-maintained Python framework for creating mathematical animations.项目地址: https://gitcode.com/GitHub_Trending/man/manim

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询