
本文将介绍如何使用ManimCE框架实现镜面反射特效,让你的动画更加生动有趣。
1. 实现原理
镜面成像最主要的问题还是需要借助Python,借助Python实现镜面成像,需要引入函数库文件
1 2
| from manim import * import numpy as np
|
1.1. 对称点计算
实现镜面反射的核心是计算点关于直线的对称点。代码中的symmetry_point函数通过向量投影的方法计算对称点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def symmetry_point(p1, p2, p): """计算点p关于由两点p1和p2确定的直线的对称点""" # 转换为numpy数组确保计算正确 p1 = np.array(p1) p2 = np.array(p2) p = np.array(p) # 计算直线的方向向量 direction = p2 - p1 # 计算从p1到p的向量 vec_p1p = p - p1 # 计算投影长度(点p在直线上的投影点到p1的距离) proj_length = np.dot(vec_p1p, direction) / np.dot(direction, direction) # 计算点p在直线上的投影点 projection = p1 + proj_length * direction # 对称点 = 2 * 投影点 - 原点 symmetric_point = 2 * projection - p return np.array([symmetric_point[0], symmetric_point[1], 0])
|
这个函数使用了向量投影的数学原理,先找到点在直线上的投影,然后根据投影点计算对称点。具体步骤是:
- 计算直线的方向向量
- 计算从直线上一点到目标点的向量
- 计算该向量在直线方向上的投影长度
- 找到投影点坐标
- 通过公式
2 * 投影点 - 原点 计算对称点
1.2. 反射动画类设计
MirrorReflection 类继承自 Manim 的 Animation 类,用于创建和管理镜面反射效果:
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
| class MirrorReflection: """ 镜面反射管理器 参数: mobject: 需要反射的原始对象 mirror: 镜面直线(默认为水平线) reflect_opacity: 反射的填充不透明度因子 (0-1) reflect_stroke_opacity: 反射的描边不透明度因子 (0-1) """ def __init__( self, mobject, mirror: Line = None, reflect_opacity=0.3, reflect_stroke_opacity=0.5, ): # 初始化参数 self.original = mobject self.mirror = mirror if mirror else Line(LEFT, RIGHT) self.reflect_opacity = reflect_opacity self.reflect_stroke_opacity = reflect_stroke_opacity # 创建反射对象并设置初始属性 self.reflection = mobject.copy() self._update_reflection_position() self.reflection.set_fill(opacity=self.reflect_opacity * mobject.get_fill_opacity()) self.reflection.set_stroke(opacity=self.reflect_stroke_opacity * mobject.get_stroke_opacity()) # 添加更新器以实现动态跟随 self.reflection.add_updater(lambda m: self._update_reflection(m)) super().__init__(mobject, **kwargs)
|
1.3. 动态更新机制
为了确保反射效果能够跟随原始对象的变化而实时更新,代码实现了几个关键方法:
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
| def _update_reflection_position(self): """更新反射对象的位置(计算每个点的对称点)""" points = self.original.get_points() new_points = [] p1, p2 = self.mirror.get_start(), self.mirror.get_end() for p in points: # 使用对称点计算函数计算每个点的反射位置 new_points.append(symmetry_point(p1, p2, p)) # 应用新位置 self.reflection.set_points(new_points)
def _update_reflection(self, reflection_mobject): """更新反射对象的属性(位置、透明度)""" temp_reflection = self.original.copy() reflection_mobject.become(temp_reflection) # 更新反射对象位置和透明度 self._update_reflection_position() reflection_mobject.set_fill( opacity=self.reflect_opacity * self.original.get_fill_opacity() ) reflection_mobject.set_stroke( opacity=self.reflect_stroke_opacity * self.original.get_stroke_opacity() )
def get_reflection(self): """获取反射对象,用于添加到场景""" return self.reflection
|
这些方法确保了无论原始对象如何移动、缩放或旋转,反射效果都会相应地更新,保持视觉上的一致性。
2. 使用示例
让我们看看如何在实际场景中使用这个镜面反射特效:
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 35 36 37 38 39 40 41
| class MirrorReflectionDemo(Scene): def construct(self): # 1. 创建对象和镜面 triangle = Triangle(color=BLUE, fill_opacity=0.8).shift(LEFT * 2) mirror = Line(UP * 3, DOWN * 3, color=WHITE, stroke_width=4) # 2. 创建反射效果 mirror_effect = MirrorReflection( triangle, mirror=mirror, reflect_opacity=0.4, # 调整反射透明度 reflect_stroke_opacity=0.6 ) reflection = mirror_effect.get_reflection() # 3. 添加到场景 self.add(mirror, triangle, reflection) self.wait() # 4. 演示动画 - 反射会实时更新! self.play(triangle.animate.shift(UP)) self.wait(0.5) self.play(triangle.animate.scale(1.5)) self.wait(0.5) self.play(triangle.animate.shift(RIGHT * 3)) self.wait(0.5) self.play(Rotate(triangle, angle=PI/2)) self.wait(0.5) # 5. 复杂形状演示 square = Square(color=RED, fill_opacity=0.8).shift(RIGHT * 2 + DOWN) square_reflection = MirrorReflection(square, mirror=mirror).get_reflection() self.play(FadeIn(square), FadeIn(square_reflection)) self.wait() self.play(square.animate.rotate(PI/3).shift(UP)) self.wait(2)
|
使用步骤非常简单:
- 创建需要添加反射效果的原始对象(这里是一个蓝色三角形)
- 创建镜面(这里是一条白色垂直线)
- 创建
MirrorReflection实例,并传入原始对象和镜面
- 通过
create_reflection_mobject()方法获取反射对象
- 将原始对象、镜面和反射对象添加到场景中
- 对原始对象执行各种动画操作,观察反射效果的实时更新
在这个例子中,我们演示了对象的上移、缩放、下移和旋转四种操作,反射效果都会实时跟随更新,保持与原始对象的对称关系。
3. 内容总结
从上面的视频演示效果来看,镜面成像的代码还是比较实用的,代码可以实现我们的制作需求,并有如下特点
3.1. 特效特点
这个镜面反射特效具有以下特点:
- 实时更新:无论原始对象如何变换,反射效果都会实时更新,保持视觉一致性
- 高度可定制:可以调整反射对象的填充透明度和描边透明度,创建不同的视觉效果
- 灵活的镜面设置:可以自定义镜面的位置、方向和样式,适应不同场景需求
- 易用性:封装成了独立的动画类,使用简单,只需几行代码就能添加专业的反射效果
3.2. 使用场景
镜面反射特效适用于多种场景:
- 数学教学:用于几何对称、坐标系变换等概念的可视化教学
- 物理模拟:模拟光的反射、镜像对称等物理现象
- 艺术效果:为动画添加美感和层次感,提升视觉吸引力
- 交互演示:用于展示对称关系、变换过程等
- Logo和品牌展示:创建镜像效果的动态Logo动画
通过这个简单而强大的镜面反射特效,可以为你的Manim动画增添更多的视觉魅力和专业感。本文转自 https://www.cnblogs.com/wang_yb/p/19109427,如有侵权,请联系删除。附带我修改之后的代码,仅供朋友们参考:
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| from manim import * import numpy as np
def symmetry_point(p1, p2, p): """计算点p关于由两点p1和p2确定的直线的对称点""" p1 = np.array(p1) p2 = np.array(p2) p = np.array(p) direction = p2 - p1 vec_p1p = p - p1 proj_length = np.dot(vec_p1p, direction) / np.dot(direction, direction) projection = p1 + proj_length * direction symmetric_point = 2 * projection - p return np.array([symmetric_point[0], symmetric_point[1], 0])
class MirrorReflection: """ 镜面反射管理器 参数: mobject: 需要反射的原始对象 mirror: 镜面直线(默认为水平线) reflect_opacity: 反射的填充不透明度因子 (0-1) reflect_stroke_opacity: 反射的描边不透明度因子 (0-1) """ def __init__( self, mobject, mirror: Line = None, reflect_opacity=0.3, reflect_stroke_opacity=0.5, ): self.original = mobject self.mirror = mirror if mirror else Line(LEFT, RIGHT) self.reflect_opacity = reflect_opacity self.reflect_stroke_opacity = reflect_stroke_opacity # 创建反射对象 self.reflection = mobject.copy() self._update_reflection_position() self.reflection.set_fill(opacity=self.reflect_opacity * mobject.get_fill_opacity()) self.reflection.set_stroke(opacity=self.reflect_stroke_opacity * mobject.get_stroke_opacity()) # 添加更新器以实现动态跟随 self.reflection.add_updater(lambda m: self._update_reflection(m)) def _update_reflection_position(self): """更新反射对象的位置(计算每个点的对称点)""" points = self.original.get_points() new_points = [] p1, p2 = self.mirror.get_start(), self.mirror.get_end() for p in points: new_points.append(symmetry_point(p1, p2, p)) self.reflection.set_points(new_points) def _update_reflection(self, reflection_mobject): """更新反射对象的属性(位置、透明度)""" temp_reflection = self.original.copy() reflection_mobject.become(temp_reflection) self._update_reflection_position() reflection_mobject.set_fill( opacity=self.reflect_opacity * self.original.get_fill_opacity() ) reflection_mobject.set_stroke( opacity=self.reflect_stroke_opacity * self.original.get_stroke_opacity() ) def get_reflection(self): """获取反射对象,用于添加到场景""" return self.reflection class MirrorReflectionDemo(Scene): def construct(self): # 1. 创建对象和镜面 triangle = Triangle(color=BLUE, fill_opacity=0.8).shift(LEFT * 2) mirror = Line(UP * 3, DOWN * 3, color=WHITE, stroke_width=4) # 2. 创建反射效果 mirror_effect = MirrorReflection( triangle, mirror=mirror, reflect_opacity=0.4, # 调整反射透明度 reflect_stroke_opacity=0.6 ) reflection = mirror_effect.get_reflection() # 3. 添加到场景 self.add(mirror, triangle, reflection) self.wait() # 4. 演示动画 - 反射会实时更新! self.play(triangle.animate.shift(UP)) self.wait(0.5) self.play(triangle.animate.scale(1.5)) self.wait(0.5) self.play(triangle.animate.shift(RIGHT * 3)) self.wait(0.5) self.play(Rotate(triangle, angle=PI/2)) self.wait(0.5) # 5. 复杂形状演示 square = Square(color=RED, fill_opacity=0.8).shift(RIGHT * 2 + DOWN) square_reflection = MirrorReflection(square, mirror=mirror).get_reflection() self.play(FadeIn(square), FadeIn(square_reflection)) self.wait() self.play(square.animate.rotate(PI/3).shift(UP)) self.wait(2)
|
好了,感谢大家来到老刘博客,希望今天分享的内容对您有所帮助。