
做Manim动画久了,你是否厌倦了那万年不变的黑色虚空?很多初学者(甚至老手)都想给动画加个背景图,但往往会遇到两个问题:
- 怎么加 是把图片放进去,还是设置相机?
- 看不清 背景花里胡哨,前面的文字公式瞬间“隐身”了。
今天,我们就来揭开Manim动画中一个简单却强大的技巧–为动画添加背景。通过几个小示例,分别演示两种完全不同的背景处理思路。
1. 舞台布景法
使用ImageMobject类,这是最直观、最常用的方法。它的逻辑是:背景图片只是舞台上的一个普通演员,只是它长得特别大,而且站得特别靠后。
这种方式的特点是:
下面的第一个示例中,实现一个简单的动态背景,操作背景和操作一般的Mobject是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Method1ImageMobject(Scene): def construct(self): # 1. 加载图片(确保这个图片是存在的) bg = ImageMobject("./assets/紫色梦幻星空.jpg")
# 2. 核心操作:撑满屏幕并允许超出 # 我们把高度设为屏幕高度的 2 倍,这样才有移动的空间 bg.scale_to_fit_height(config.frame_height * 2)
# 3. 核心操作:放到最底层 bg.set_z_index(-100)
self.add(bg)
# 前景物体 text = Text("方法一:ImageMobject", font_size=40, color=WHITE) sub = Text("背景可以动!", font_size=24, color=YELLOW).next_to(text, DOWN)
self.play(Write(text), FadeIn(sub))
# 4. 演示优势:让背景缓慢移动 self.play(bg.animate.shift(LEFT * 2), rate_func=linear) self.play(bg.animate.shift(UP * 2), rate_func=linear) self.play(bg.animate.shift(RIGHT * 2), rate_func=linear) self.play(bg.animate.shift(DOWN * 2), rate_func=linear) self.wait()
|
下面的示例也很简单,就是显示一段简单的公式。不过,大家可以比较看看,相比于默认的黑色背景,加一个学校的黑板背景,是不是更有亲和力?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Method2ImageMobject(Scene): def construct(self): # 1. 加载图片 bg = ImageMobject("./assets/黑板.jpg")
# 2. 调整大小铺满屏幕 bg.scale_to_fit_height(config.frame_height) bg.scale_to_fit_width(config.frame_width)
# 3. 核心操作:放到最底层 bg.set_z_index(-100) self.add(bg)
# 前景物体 math = MathTex(r"\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}") self.play(Write(math))
|
2. 主角光环法
除了上面两种,还有一种比较特殊的方式,也就是BackgroundColoredVMobjectDisplayer类。它是一个负责渲染的类,专门处理一种特殊情况:当物体拥有“背景色描边”时,如何遮挡住它背后的东西。我们通过 set_background_stroke() 来调用这个机制。严格来说不是 “设置全屏背景”,而是 “给物体加一个局部背景(Matte)”,这是在花哨背景下生存的必备技能。它的特点是:
- 局部遮挡:在文字或公式周围生成一圈不透明的轮廓。
- 增强对比:专门用于解决“背景太花,文字看不清”的问题。
下面的示例故意构造了一个混乱的背景,然后比较看看,这两个一样的公式,是不是加了局部背景的公式更加清晰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class ReadableFormulaOnImage(Scene): def construct(self): # 1. 先设置一个很花的背景(这里为了演示,我们用随机噪点模拟一张复杂的图) # 实际使用中,请换成你的 image.jpg noise = Rectangle(width=16, height=10) noise.set_fill(color=[BLUE, RED, GREEN, YELLOW], opacity=0.5) # 把它搞得乱一点 for _ in range(20): line = Line( start=[np.random.uniform(-7, 7), np.random.uniform(-4, 4), 0], end=[np.random.uniform(-7, 7), np.random.uniform(-4, 4), 0], color=random_color(), stroke_width=5, ) self.add(line) self.add(noise)
# 2. 普通的文字(在花背景下很难看清) bad_text = MathTex(r"\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}") bad_text.shift(UP)
# 3. 【核心技巧】使用 BackgroundColoredVMobject 机制 # set_background_stroke 会给文字加一层“描边” # 这层描边是不透明的,会利用 Displayer 类遮挡住背景! good_text = MathTex(r"\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}") good_text.set_background_stroke(color=BLACK, width=8) # 黑色描边,宽度设大一点 good_text.shift(DOWN)
# 动画演示对比 self.play(Write(bad_text))
self.play(Write(good_text)) # 清晰可见! self.wait()
|
3. 两种方式比较
两种方式各有自己的应用场景,对比如下:
| 特性 |
舞台布景法 |
主角光环法 |
| 本质 |
场景中的一个巨大物体 |
物体自身的描边属性 |
| 主要用途 |
动态背景、视差滚动、多层背景 |
高亮主体、对抗花哨背景 |
| 动画能力 |
⭐⭐⭐⭐⭐ (极强) |
⭐⭐⭐ (跟随物体运动) |
| 代码复杂度 |
中 (需手动调大小/层级) |
低 (一行设置) |
| 文档对应 |
Mobject 类 |
BackgroundColoredVMobjectDisplayer |
| 最佳场景 |
漂浮的云、移动的星空 |
字幕、复杂背景下的公式 |
4. 总结
简单来说,如果你想做一段电影感的片头,背景需要缓慢推移,请用 舞台布景法;如果你发现文字看不清了,请使用 主角光环法 (set_background_stroke) 给文字加个 “光环”。好了,今天的教程就分享到这里,我也是一名Manim的学习新手,转载这篇文章主要是方便我自己学习Manim,感谢大家来到老刘博客。