OpenMontage 技能拆解:ManimGL 颜色系统实战指南——内置常量、渐变插值、GLSL 着色与配色工程化
【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage
ManimGL(Grant Sanderson 3Blue1Brown 版的开源数学动画引擎)以强大的向量颜色渲染与交互式开发著称,其颜色能力贯穿物体创建、渐变绘制、GLSL 动态着色与三维材质表现。本文以 OpenMontage 仓库中的 ManimGL 最佳实践技能 · 颜色规则 为主体骨架,结合同技能包中的 styling 规则、SKILL.md 总览 及 examples 目录 中大量真实场景源码,系统讲解从BLUE_E~BLUE_A内置色阶、set_color_by_codeGLSL 着色,到可复用的工程化配色方案等全部技术要点。读完你将能直接编写、渲染并调试一套具有专业配色体系的 ManimGL 数学动画场景。
一、这份文档在 OpenMontage 技能体系中的位置
该文档位于仓库技能目录.agents/skills/manimgl-best-practices/rules/colors.md(并在.claude/skills/manimgl-best-practices/rules/下存在同名镜像),它并非孤立笔记,而是整个 ManimGL 技能包中的一条「样式与外观」规则。技能包总览 SKILL.md 将规则文件分为若干组,其中Styling & Appearance组就包含:
- colors.md——颜色常量、渐变、RGB/Hex、GLSL 着色;
- styling.md——填充、描边、透明度、backstroke、gloss、shadow。
两条规则在 API 上深度互补:colors.md回答「用什么颜色」,styling.md回答「颜色如何作用到 fill/stroke 与材质」;文末的set_gloss()、set_shadow()正是两条规则的接口衔接点。从仓库的 AGENT_GUIDE.md 与 skills/INDEX.md 可看到,manimgl-best-practices与manimce-best-practices、manim-composer一并被归类为**可视化 / 数学动画(Visualization / Math Animation)**技能,供 AI Agent 在编写数学向动画脚本时引用。
技能包同时强调了一个关键前置事实:本文档面向的是 ManimGL(manimgl,导入from manimlib import *),而非 Manim Community Edition(manim,导入from manim import *)。两者的颜色常量大体同源,但创建动画 API(ShowCreationvsCreate)、数学文本类(TexvsMathTex)、CLI(manimglvsmanim)均不相同,阅读本文代码示例时请勿混用两套引擎。
开源与许可提示:技能包内的示例代码改编自 Grant Sanderson 的 3Blue1Brown 视频仓库,遵循 CC BY-NC-SA 4.0(署名-非商业-相同方式共享),详见技能包内 LICENSE.txt。
二、颜色系统总览与内置颜色常量
ManimGL 的颜色体系可概括为「三层模型」:
- 内置命名常量(
RED、BLUE_C等)——写代码时的首选,可读性最高; - 数值颜色(RGB 0~1 数组、Hex 字符串)——来自设计稿或品牌色板时的转换入口;
- 程序化/着色器颜色(渐变、
interpolate_color、GLSL)——把「物理量」映射为颜色的高阶手段。
2.1 基础颜色
# Primary colors RED, GREEN, BLUE YELLOW, CYAN, MAGENTA # Grayscale WHITE, GREY, GRAY, BLACK # Common colors ORANGE, PURPLE, PINK, BROWN MAROON, TEAL, GOLD其中GREY与GRAY是等价的英/美拼写别名,均可直接使用。这些常量可作为color=、fill_color=、stroke_color=等任意着色参数的值。
2.2 带字母后缀的渐变色调(A~E 色阶)
ManimGL 为多数彩色提供了_E(最深)到_A(最浅)五个档位的明度变化,命名约定与 Adobe 系取色习惯相反——E 最深、A 最浅:
# Blue variations (darkest to lightest) BLUE_E # Darkest blue BLUE_D BLUE_C BLUE_B BLUE_A # Lightest blue # Similarly for other colors: RED_E, RED_D, RED_C, RED_B, RED_A GREEN_E, GREEN_D, GREEN_C, GREEN_B, GREEN_A YELLOW_E, YELLOW_D, YELLOW_C, YELLOW_B, YELLOW_A注意同系列常量中有一个常见混淆点:BLUE_C/RED_C等C 档并不代表「Cyan」,它只是蓝色家族里的中间明度档位,常被用作正文主题色。
2.3 基础用法示例
from manimlib import * class ColorExample(Scene): def construct(self): # Create circles with different color variations circles = VGroup(*[ Circle(radius=0.5, color=color) for color in [BLUE_E, BLUE_D, BLUE_C, BLUE_B, BLUE_A] ]) circles.arrange(RIGHT, buff=0.5) self.add(circles)运行方式(遵循 ManimGL CLI,详见技能包 cli.md 规则):
# 预览并写文件 manimgl scene.py ColorExample -w # 低质量快速测试 manimgl scene.py ColorExample -l在技能包 examples 中可找到大量「同族色阶表达数值梯度」的真实范例,例如 attention_softmax_masking.py 用set_stroke(GREY_B, 1)弱化网格描边以衬托前景数值——用深灰阶做背景、浅灰阶做边框,是低调信息层级的标准做法。
三、设置颜色:创建期、后置期与动画期
3.1 三种设置时机
# At creation circle = Circle(color=BLUE) # After creation square = Square() square.set_color(RED) # Multiple mobjects group = VGroup(Circle(), Square(), Triangle()) group.set_color(GREEN)要点:VGroup的set_color会递归作用于全部子对象,因此批量换色时不必遍历。三种写法在内部都归一为顶点颜色数据,最终渲染路径一致。
3.2 动画化颜色变化
class ColorAnimation(Scene): def construct(self): circle = Circle(color=BLUE) self.add(circle) # Animate color change self.play(circle.animate.set_color(RED)) self.wait() # Another change self.play(circle.animate.set_color(YELLOW)) self.wait()ManimGL 的.animate语法会自动将一次颜色赋值包装为逐帧插值动画,set_color从旧色平滑过渡到新色。想要精确控制节奏时,可为self.play(...)指定run_time(例如 0.5 秒的强调高亮、2 秒的缓慢渐变),这与仓库 manim-usage.md 中「Color highlight 0.5s、Equation transform 1.5s」的计时约定一致。
四、渐变与颜色插值
渐变是数学动画里表达「连续变化量」的首选手段,ManimGL 提供两条路径:面向子对象集合的set_submobject_colors_by_gradient,以及面向任意颜色计算的interpolate_color。
4.1 set_submobject_colors_by_gradient
该方法按子对象在VGroup中的顺序,把调色板平滑分配到每个子对象上:
# Apply gradient to submobjects text = Text("Gradient Text") text.set_submobject_colors_by_gradient(BLUE, GREEN, YELLOW) # Multiple objects with gradient squares = VGroup(*[Square() for _ in range(10)]) squares.arrange(RIGHT) squares.set_submobject_colors_by_gradient(RED, BLUE)它可以接受两个以上颜色作为断点(如RED, YELLOW, GREEN, BLUE),参数个数不限。对Text生效的机理在于:ManimGL 的文本对象按字形拆分成了多个子对象,因此渐变可以「逐字染色」。
4.2 颜色插值:interpolate_color
from manimlib.utils.color import interpolate_color # Create color between two colors mid_color = interpolate_color(RED, BLUE, 0.5) # Purple # Create gradient programmatically n_colors = 10 gradient = [ interpolate_color(RED, BLUE, alpha) for alpha in np.linspace(0, 1, n_colors) ]interpolate_color(color_a, color_b, alpha)在 RGB 空间内做线性插值,alpha=0得到color_a,alpha=1得到color_b。由于它接受任意数值 alpha,最适合把连续物理量映射为颜色——这是技能包 examples 中最常用的「数据可视化着色」模式。证据见 vector_fields.py:
- 在
SimpleVectorField场景中,箭头按向量模长着色:arrow.set_color(interpolate_color(BLUE, YELLOW, mag / 0.5))(见第 47~49 行附近),模长从 0 到 0.5 对应蓝到黄; - 在
GradientFieldDemo场景中,把标量场高度归一化到 [0,1] 后做interpolate_color(BLUE_E, RED, t)(第 95 行附近),深蓝→红的渐变让「势场峰谷」一目了然。
可见把「物理量归一化 +interpolate_color」封装成辅助函数,是保证多个场景配色一致的关键工程手段。
五、进阶着色:GLSL 片元着色与按函数着色
ManimGL 基于 OpenGL 渲染,因此支持把颜色计算下沉到 GPU 片元着色器,实现随坐标连续变化的复杂着色。
5.1 set_color_by_code(GLSL)
# Color based on position square = Square() square.set_color_by_code(""" color.r = x; color.g = y; color.b = 1.0; """)在传入的 GLSL 代码片段中,color是待写的vec4,x、y(以及三通道场景下的z)为片元本地坐标。典型应用是把 2D 平面按坐标直接映射成色场,用于绘制标量场背景、渐变遮罩或干涉/波动图案的底色。
5.2 set_color_by_xyz_func
# Color based on 3D position surface = Sphere(radius=2) surface.set_color_by_xyz_func( glsl_snippet="float value = sqrt(x*x + y*y + z*z); return value;", min_value=0, max_value=5, colormap='viridis' )它接受一个返回标量的 GLSL 函数、一个[min_value, max_value]的取值范围和一个colormap(如'viridis'),引擎会把函数返回值归一化后映射到所选 colormap,非常适合给 3D 曲面赋予「以距原点距离着色」这类连续物理意义。该 API 通常与技能包 3d.md 规则 中的Surface、Sphere、Torus等 3D 对象配合,examples 目录下的three_d_surfaces.py、bloch_sphere_3d.py等场景即属于此类用法。
六、文本与 LaTeX 公式着色
6.1 t2c:逐词着色 Text
# Color specific words text = Text( "Red, Green, and Blue", t2c={"Red": RED, "Green": GREEN, "Blue": BLUE} )t2c(text-to-color)字典把「原始字符串中的词」映射为颜色,属于创建期静态映射;技能包另有 t2c.md 与 tex.md 两条专门规则深入讲解。
6.2 着色 Tex 数学公式
ManimGL 使用Tex(注意与 ManimCE 的MathTex区分),且强烈建议配合大写R原始字符串书写 LaTeX:
# Color math symbols equation = Tex( R"E = mc^2", t2c={"E": BLUE, "m": GREEN, "c": YELLOW} ) # Color by tex substring formula = Tex(R"\int_0^1 x^2 dx") formula.set_color_by_tex("x", BLUE) formula.set_color_by_tex(R"\int", RED)t2c在创建期完成映射,适合一开始就固定语义色;set_color_by_tex可在动画中途按 LaTeX 子串动态改色——这正是「先写黑白公式,再逐个高亮变量」教学叙事的基础。
SKILL.md 快速参考里也用Tex(R"E = mc^2", t2c={"E": BLUE, "m": GREEN, "c": YELLOW})呼应了同样的规范。强调一点:ManimGL 中 LaTeX 颜色必须落在数学子对象上,若要给普通描述性文本着色请用上文t2c/Text,二者 API 名相同但面向对象不同。
七、RGB 与 Hex 颜色
当颜色来自品牌色板或设计稿时,需从manimlib.utils.color工具模块显式转换:
7.1 RGB 值(0~1 范围)
from manimlib.utils.color import rgb_to_color # RGB values (0-1 range) custom_color = rgb_to_color([0.5, 0.3, 0.8]) circle = Circle(color=custom_color) # RGB from 0-255 range (convert to 0-1) custom_color = rgb_to_color([128/255, 77/255, 204/255])rgb_to_color的入参是 0~1 浮点三元组;若习惯 0~255 表示法,必须先除以 255(引擎内部不接受 255 制原始值)。
7.2 Hex 颜色
from manimlib.utils.color import hex_to_rgb, rgb_to_color # Hex color hex_color = "#FF5733" rgb = hex_to_rgb(hex_color) color = rgb_to_color(rgb) circle = Circle(color=color)标准 Hex 字符串(含#前缀)经由hex_to_rgb拆成 0~1 RGB 三元组,再交给rgb_to_color得到引擎可用的颜色对象。由于项目里自定义配置通常以#1e1e1e这类 Hex 书写(见第八节配色方案),这两步转换是「设计稿 → 场景」的唯一入口。
八、透明度、填充与描边的配合
颜色在画布上的最终观感由 fill(填充色)、stroke(描边色)与各自 opacity(不透明度)共同决定:
# Transparent circle circle = Circle(color=BLUE, fill_opacity=0.5) # Change opacity circle.set_opacity(0.7) # Fill vs Stroke opacity square = Square() square.set_fill(BLUE, opacity=0.5) square.set_stroke(WHITE, width=4, opacity=1.0)set_opacity(v)同时作用于 fill 与 stroke,适合整体淡出/淡入;set_fill(color, opacity=...)只改填充,set_stroke(color, width=..., opacity=...)只改描边,能精细控制「实心填充 + 半透明白描边」等专业质感。
透明度是实现分层叠放(如半透明色块压在坐标系上、光晕叠加)的核心杠杆。更多描边细节(描边置底background=True、backstroke 文字描廓线、set_stroke_opacity等)参见同技能包 styling.md:
# 给文字加黑色背廓线,保证复杂背景上的可读性 text = Text("Clear and Readable", font_size=72, color=WHITE) text.set_backstroke(BLACK, width=10)九、颜色查询与样式匹配
调试与代码复用时,可从既有 mobject 反查颜色,或在 mobject 之间拷贝颜色:
9.1 读取颜色
circle = Circle(color=BLUE) # Get color color = circle.get_color() # Get fill color fill_color = circle.get_fill_color() # Get stroke color stroke_color = circle.get_stroke_color()get_color()返回综合(默认按 fill 优先)的颜色,而get_fill_color()/get_stroke_color()分别返回两个通道的独立值——当出现「描边与填充颜色不同却想只取其一」时务必区分使用。
9.2 颜色匹配
# Match color from another mobject circle = Circle(color=BLUE) square = Square() square.match_color(circle) # Match fill color square.match_fill(circle) # Match stroke square.match_stroke(circle)match_*系方法的工程价值在于:多个对象间保持颜色同步而不必记住具体色值。styling 规则进一步提供match_style(source)(拷贝全部样式属性),可作为批量统一外观的兜底。examples 中大量TracedPath(..., stroke_color=RED)、粒子Dot(color=YELLOW)与其轨迹stroke_color=BLUE的搭配(见 vector_fields.py),正是先定义「语义→颜色」再通过 color 参数分发的应用实例。
十、工程化配色方案
10.1 场景级 COLOR_SCHEME 字典
# Define color scheme COLOR_SCHEME = { "background": "#1e1e1e", "primary": BLUE_C, "secondary": GREEN_C, "accent": YELLOW_C, "text": WHITE, "highlight": RED_C } # Use in scene class StyledScene(Scene): def construct(self): title = Text("Title", color=COLOR_SCHEME["primary"]) subtitle = Text("Subtitle", color=COLOR_SCHEME["secondary"]) highlight = Circle(color=COLOR_SCHEME["accent"]) self.add(title, subtitle, highlight)把背景、主色、辅色、强调色、文字色集中到一个字典,是跨场景保持一致视觉的基础设施。注意示例将「背景」也纳入字典:一个深色背景(如#1e1e1e)需要在前景颜色选择上确保足够的对比度(见第十一节可访问性)。
10.2 3Blue1Brown 风格配色
# Grant's typical colors BLUE_3B1B = BLUE_C GREEN_3B1B = GREEN_C YELLOW_3B1B = YELLOW_C RED_3B1B = RED_C # Background BACKGROUND_COLOR = "#0a0a0a"通过别名变量把「品牌语义色」钉在_C档位、背景统一为接近纯黑的#0a0a0a,即复刻了 3b1b 视频标志性的深底高饱和观感——这也与技能包「3B1B 风格」定位一致。
10.3 仓库中的语义配色约定
仓库面向数学动画输出的 skills/creative/manim-usage.md 记录了一套「语义角色 → 颜色」约定,其所用常量(YELLOW、RED、TEAL、BLUE_C、GREEN、RED_E)在 ManimGL 中同样可用,可直接吸收为场景配色决策表:
- 正在求解的变量用黄色
YELLOW;矩阵/算子用红色RED; - 特征向量/结果用青色
TEAL;已知常量用蓝色BLUE_C; - 标注/标签用绿色
GREEN;弱化背景用GREY加opacity=0.5; - 错误/错误路径用深红
RED_E。
这种「颜色即语义」的纪律,能让观众无需读字即可建立「黄=待解、蓝=已知、红=算子」的认知通道。
十一、3D 材质:Gloss 与 Shadow
对于 3D 对象,颜色还需配合表面材质参数(gloss 高光、shadow 阴影)才能呈现立体感:
# Add glossy appearance sphere = Sphere(radius=2, color=BLUE) sphere.set_gloss(0.8) # 0 to 1 # Get gloss value gloss = sphere.get_gloss()# Add shadow (for 3D) cube = Cube(color=RED) cube.set_shadow(0.5) # 0 to 1 # Get shadow value shadow = cube.get_shadow()set_gloss(v):控制表面镜面高光强度,0 为完全哑光、1 为镜面质感,常与「玻璃/塑料材质」风格函数组合使用;set_shadow(v):控制对象投射阴影强度,0 无阴影、1 阴影最强。
styling 规则中的glass_style预设即为三者的典型组合:set_fill(color, opacity=0.3)+ 白色细描边 +set_gloss(0.9),可让 3D 球体呈现半透明玻璃质感。这些 API 服务于技能包 examples 中bloch_sphere_3d.py、three_d_surfaces.py等三维科学可视化场景,若要实现「概念本身就是空间关系」的可视化(球面、体积、轨道),应优先选用三维场景并配合本节的材质参数。
十二、综合示例:一屏展示颜色全部技法
colors.md 提供了一个把「色阶展示 + 渐变 + 自定义 RGB + 着色文本 + 动画换色」融合到单个场景的完整范例,可直接运行:
class ComprehensiveColorExample(Scene): def construct(self): # Color variations showcase blue_shades = VGroup(*[ Circle(radius=0.4, color=color) for color in [BLUE_E, BLUE_D, BLUE_C, BLUE_B, BLUE_A] ]) blue_shades.arrange(RIGHT, buff=0.3) blue_shades.to_edge(UP, buff=1) # Gradient squares = VGroup(*[Square(side_length=0.6) for _ in range(8)]) squares.arrange(RIGHT, buff=0.2) squares.set_submobject_colors_by_gradient(RED, YELLOW, GREEN, BLUE) # Custom RGB color custom_circle = Circle( radius=1, color=rgb_to_color([0.8, 0.2, 0.6]), fill_opacity=0.7 ) custom_circle.shift(DOWN * 2) # Colored text text = Text( "Colorful Text", font_size=48, t2c={"Colorful": BLUE, "Text": GREEN} ) text.next_to(custom_circle, UP, buff=0.5) # Add everything self.play( FadeIn(blue_shades, lag_ratio=0.1), FadeIn(squares, lag_ratio=0.1), ShowCreation(custom_circle), Write(text) ) self.wait() # Animate color changes self.play( squares.animate.set_submobject_colors_by_gradient(PURPLE, ORANGE), custom_circle.animate.set_color(TEAL) ) self.wait()它同时示范了两个工程习惯:一是ShowCreation/Write/FadeIn等 ManimGL 特有创建动画与配色叠加使用;二是渐变也可以在动画中二次改写——animate.set_submobject_colors_by_gradient让整组方块平滑重染新色板。
十三、最佳实践清单与常见模式
13.1 官方推荐的七条纪律
- 优先使用命名常量:可读性优先于 RGB 值,除非颜色来自外部设计稿;
- 维持一致的配色方案:先定义调色板再动手画场景,保证整套视频色彩连贯;
- 用渐变表达递进与关联:渐变天然适合呈现「连续量、趋势、关系」;
- 用透明度做分层:半透明是呈现重叠元素遮挡关系的最轻量手段;
- 关注颜色可访问性:确保前后景足够对比度;避免仅靠红绿区分信息(可叠加明度
_A~_E变化); - 用 t2c 高亮公式:对数学表达式按子串着色以突出重点;
- 克制用色:颜色过多反而分散注意力,一屏建议 3~4 个主色以内。
其中第 5 条「对比度与色弱友好」与仓库 skills/creative/manim-usage.md 中「避免只用红绿区分、用_A到_E明度差补足」的建议互相印证,也是 tests/contracts/test_theme_text_contrast_contract.py 等文本对比度契约测试所守护的同类原则。
13.2 三个高频复用模式
彩虹渐变——把一组 mobject 依次铺满可见光谱:
def rainbow_gradient(mobjects): colors = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE] VGroup(*mobjects).set_submobject_colors_by_gradient(*colors)渐隐换色(Fade to color)——用run_time控制颜色过渡节奏:
self.play( circle.animate.set_color(RED), run_time=2 )颜色轮播(Color cycling)——在讲解「状态的连续跳变」时逐色切换并短暂停留:
colors = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE] for color in colors: self.play(circle.animate.set_color(color), run_time=0.5) self.wait(0.2)这三个模式分别覆盖「批量铺色」「强调换色」「状态闪烁」三种最常见叙事需求,可作为自定义着色工具函数的起点,放入你自己的场景库中持续复用。
十四、在 OpenMontage 项目中的落地建议
若在 OpenMontage 中编写/调用 ManimGL 数学动画,可遵循以下落地路径:
- 先读技能包总览:确认目标引擎后,对照 SKILL.md 的「ManimGL vs ManimCE 差异表」规避
ShowCreation/Create、Tex/MathTex、self.frame等混用陷阱; - 按 colors 规则搭配色:从本文第二节常量表起步,用第八节
COLOR_SCHEME字典固定整套场景色板,必要时用第七节 Hex 转换接入品牌色; - 向 examples 取经:仓库 examples 下有 70+ 个可直接运行的场景文件(
vector_fields.py、three_d_surfaces.py、attention_softmax_masking.py等),其中的interpolate_color物理量着色、GREY_B弱化描边等写法,均已在真实场景中验证; - 渲染与调试:遵循技能包 CLI 规范——
manimgl 文件.py 场景类 -w成片、-l低清快测、-se 行号进入交互式开发(配合checkpoint_paste()即时回放,见 interactive.md),从而把配色调整从「渲染-看片-改」的慢循环中解放出来。
仓库为只读资源,以上技能包文件供查阅与本地运行,请勿在仓库内直接修改。
总结
ManimGL 的颜色系统是一条从「命名常量 → 数值颜色 → GPU 着色」逐步下探的完整链路:内置常量与_A~_E色阶覆盖 90% 的日常书写;set_submobject_colors_by_gradient与interpolate_color承担连续量映射;set_color_by_code/set_color_by_xyz_func把着色交给 GPU;而COLOR_SCHEME字典与 3B1B 色板则把「美」工程化为可复制的约定。掌握本文全部代码块与仓库 examples 中的真实用法,你便能写出色彩克制、语义清晰、可直接渲染的 ManimGL 数学动画——这正是 colors.md 想要交付给 Agent 与开发者的核心能力。
【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考