3D 几何

Manim 的 3D 几何体全部可以用 Cairo 渲染器绘制(伪 3D 投影 + 着色),无需 OpenGL。它们分两个家族:由 Surface 参数化生成的曲面家族,以及由面片拼成的多面体家族(多面体在下一节专门介绍)。

先看最常用的六个实体:

六个基本实体一字排开:Cube、Sphere、Cone、Cylinder、Torus、Prism。默认角度下它们看起来像压扁的 2D 图形,配合 set_camera_orientation 才有立体感。

查看源码 three_d_solids.py
from manim import *


class ThreeDSolids(ThreeDScene):
    def construct(self):
        solids = VGroup(
            Cube(),
            Sphere(),
            Cone(),
            Cylinder(),
            Torus(major_radius=1, minor_radius=0.35),
            Prism(dimensions=[1.5, 1.0, 0.8]),
        )
        solids.arrange(RIGHT, buff=0.8)
        self.set_camera_orientation(phi=70 * DEGREES, theta=-60 * DEGREES)
        self.play(FadeIn(solids), run_time=2)
        self.begin_ambient_camera_rotation(rate=0.4)
        self.wait(2)
        self.stop_ambient_camera_rotation()

Cone

Cone(base_radius=1, height=1, direction=array([0., 0., 1.]), show_base=False, v_range=(0, 6.283185307179586), u_min=0, checkerboard_colors=False, **kwargs) 官方文档 ↗

圆锥面(侧面为参数化曲面,默认无底面)。

Cone(
    base_radius: float = 1,
    height: float = 1,
    direction: Vector3DLike = Z_AXIS,
    show_base: bool = False,
    v_range: tuple[float, float] = (0, 2*PI),
    u_min: float = 0,
    checkerboard_colors=False,
    **kwargs,
)

name

type

default

desc

base_radius

float

1

底面半径

height

float

1

高

direction

Vector3DLike

[0,0,1]

锥体朝向,默认沿 Z 轴

show_base

bool

False

是否显示底面圆盘

Cube

Cube(side_length=2, fill_opacity=0.75, fill_color=ManimColor('#58C4DD'), stroke_width=0, **kwargs) 官方文档 ↗

立方体,由 6 个正方形面片组成(因此是 VGroup,见下文 Prism)。

Cube(
    side_length: float = 2,
    fill_opacity: float = 0.75,
    fill_color: ParsableManimColor = "#58C4DD",
    stroke_width: float = 0,
    **kwargs,
)

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Cube" -> "VGroup"; "VGroup" -> "VMobject"; "VMobject" -> "Mobject"; }

Cylinder

Cylinder(radius=1, height=2, direction=array([0., 0., 1.]), v_range=(0, 6.283185307179586), show_ends=True, resolution=(24, 24), **kwargs) 官方文档 ↗

圆柱面。

Cylinder(
    radius: float = 1,
    height: float = 2,
    direction: Vector3DLike = Z_AXIS,
    v_range: tuple[float, float] = (0, 2*PI),
    show_ends: bool = True,
    resolution: int | tuple[int, int] = (24, 24),
    **kwargs,
)

name

type

default

desc

radius

float

1

半径

height

float

2

高

direction

Vector3DLike

[0,0,1]

轴向

show_ends

bool

True

是否封闭上下底

resolution

int | tuple

(24, 24)

网格分辨率

Sphere

Sphere(center=array([0., 0., 0.]), radius=1, resolution=None, u_range=(0, 6.283185307179586), v_range=(0, 3.141592653589793), **kwargs) 官方文档 ↗

球面。

Sphere(
    center: Point3DLike = ORIGIN,
    radius: float = 1,
    resolution: int | Sequence[int] | None = None,
    u_range: tuple[float, float] = (0, 2*PI),
    v_range: tuple[float, float] = (0, PI),
    **kwargs,
)

name

type

default

desc

center

Point3DLike

ORIGIN

球心

radius

float

1

半径

resolution

int | Sequence | None

None

网格分辨率,None 时取配置默认值

Torus

Torus(major_radius=3, minor_radius=1, u_range=(0, 6.283185307179586), v_range=(0, 6.283185307179586), resolution=None, **kwargs) 官方文档 ↗

圆环面(救生圈形状)。

Torus(
    major_radius: float = 3,
    minor_radius: float = 1,
    u_range: tuple[float, float] = (0, 2*PI),
    v_range: tuple[float, float] = (0, 2*PI),
    resolution: int | tuple[int, int] | None = None,
    **kwargs,
)

name

type

default

desc

major_radius

float

3

中心到管中心的距离

minor_radius

float

1

管半径

提示

Torus 默认 major_radius=3,在小场景中显得很大,演示时通常显式传 Torus(major_radius=1, minor_radius=0.4) 之类的值。

Prism

Prism(dimensions=[3, 2, 1], **kwargs) 官方文档 ↗

长方体(矩形棱柱),继承 Cube。

Prism(dimensions: Vector3DLike = [3, 2, 1], **kwargs)

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Prism" -> "Cube"; "Cube" -> "VGroup"; "VGroup" -> "VMobject"; }

dimensions 是 [宽, 高, 深] 三个方向的长度。Prism([2, 2, 2]) 等价于 Cube(side_length=2)。

Dot3D

Dot3D(point=array([0., 0., 0.]), radius=0.08, color=ManimColor('#FFFFFF'), resolution=(8, 8), **kwargs) 官方文档 ↗

3D 点:一个极小的球。

Dot3D(
    point: Point3D = ORIGIN,
    radius: float = 0.08,
    color: ParsableManimColor = WHITE,
    resolution: int | tuple[int, int] | None = (8, 8),
    **kwargs,
)

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Dot3D" -> "Sphere"; "Sphere" -> "Surface"; "Surface" -> "VGroup"; }

Line3D

Line3D(start=array([-1.,  0.,  0.]), end=array([1., 0., 0.]), thickness=0.02, color=None, resolution=24, **kwargs) 官方文档 ↗

3D 线段:一根很细的圆柱。

Line3D(
    start: Point3DLike = [-1, 0, 0],
    end: Point3DLike = [1, 0, 0],
    thickness: float = 0.02,
    color: ParsableManimColor | None = None,
    resolution: int | tuple[int, int] = 24,
    **kwargs,
)

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Line3D" -> "Cylinder"; "Cylinder" -> "Surface"; "Surface" -> "VGroup"; }

Arrow3D

Arrow3D(start=array([-1.,  0.,  0.]), end=array([1., 0., 0.]), thickness=0.02, height=0.3, base_radius=0.08, color=ManimColor('#FFFFFF'), resolution=24, **kwargs) 官方文档 ↗

3D 箭头:细圆柱 + 圆锥头。

Arrow3D(
    start: Point3DLike = [-1, 0, 0],
    end: Point3DLike = [1, 0, 0],
    thickness: float = 0.02,
    height: float = 0.3,
    base_radius: float = 0.08,
    color: ParsableManimColor = WHITE,
    resolution: int | tuple[int, int] = 24,
    **kwargs,
)

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Arrow3D" -> "Line3D"; "Line3D" -> "Cylinder"; "Cylinder" -> "Surface"; }

下面这个示例把 Line3D、Arrow3D、Dot3D 和自定义 Surface 放在一起:

saddle(u, v) 返回 [u, v, 0.5*u*v] 定义一张马鞍面;Line3D/Arrow3D 本质上是细圆柱,所以它们继承 Cylinder 的参数。

查看源码 three_d_lines_surface.py
from manim import *


class ThreeDLinesAndSurface(ThreeDScene):
    def construct(self):
        line = Line3D(ORIGIN, [2, 2, 2])
        arrow = Arrow3D([-2.5, 0, 0], [-2.5, 0, 2])
        dot = Dot3D([2, -1.5, 1], color=YELLOW)

        def saddle(u, v):
            return np.array([u, v, 0.5 * u * v])

        surface = Surface(
            saddle,
            u_range=(-2, 2),
            v_range=(-2, 2),
            resolution=(16, 16),
        )
        self.set_camera_orientation(phi=60 * DEGREES, theta=-45 * DEGREES)
        self.play(Create(line), Create(arrow), FadeIn(dot), run_time=1)
        self.play(Create(surface), run_time=2)
        self.wait(0.5)
        self.move_camera(theta=20 * DEGREES, run_time=1)

Surface

Surface(func, u_range=(0, 1), v_range=(0, 1), resolution=32, surface_piece_config={}, fill_color=ManimColor('#29ABCA'), fill_opacity=1.0, checkerboard_colors=[ManimColor('#29ABCA'…, stroke_color=ManimColor('#BBBBBB'), stroke_width=0.5, should_make_jagged=False, pre_function_handle_to_anchor_scale_factor=1e-05, **kwargs) 官方文档 ↗

一切参数化 3D 曲面的基类:给一个 (u, v) -> [x, y, z] 函数,再指定参数范围和网格分辨率,Manim 把它切成小面片拼出来。

Surface(
    func: Callable[[float, float], np.ndarray],
    u_range: tuple[float, float] = (0, 1),
    v_range: tuple[float, float] = (0, 1),
    resolution: int | Sequence[int] = 32,
    surface_piece_config: dict = {},
    fill_color: ParsableManimColor = BLUE_D,
    fill_opacity: float = 1.0,
    checkerboard_colors=[BLUE_D, BLUE_E],
    stroke_color: ParsableManimColor = LIGHT_GREY,
    stroke_width: float = 0.5,
    should_make_jagged: bool = False,
    pre_function_handle_to_anchor_scale_factor: float = 1e-5,
    **kwargs,
)

name

type

default

desc

func

Callable

—

参数化函数 (u, v) -> np.ndarray([x, y, z])

u_range

tuple[float, float]

(0, 1)

u 参数范围

v_range

tuple[float, float]

(0, 1)

v 参数范围

resolution

int | Sequence

32

每维网格份数,越高越平滑越慢

checkerboard_colors

Iterable | False

[BLUE_D, BLUE_E]

棋盘格双色;传 False 关闭

should_make_jagged

bool

False

是否让面片边缘锯齿化(着色更硬)

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Surface" -> "VGroup"; "VGroup" -> "VMobject"; "VMobject" -> "Mobject"; }

注意继承关系:Surface 不是 VMobject 的直接子类,而是 VGroup——每个小面片是 ThreeDVMobject,整个曲面是面片的组合。因此可以用 VGroup 的所有方法(arrange、scale 等)操作曲面。

ThreeDVMobject

ThreeDVMobject(shade_in_3d=True, **kwargs) 官方文档 ↗

单个 3D 面片(Surface 的内部组成单元),也可单独用于自定义 3D 图形。

ThreeDVMobject(shade_in_3d: bool = True, **kwargs)

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "ThreeDVMobject" -> "VMobject"; "VMobject" -> "Mobject"; }

shade_in_3d=True 时按 3D 法线方向参与光照着色;设 False 则按普通 2D 图形平涂。

常用类对比

类

继承

一句话用途

关键参数

Sphere

Surface → VGroup

球面

center、radius、resolution

Cone

Surface → VGroup

圆锥

base_radius、height、show_base

Cylinder

Surface → VGroup

圆柱

radius、height、show_ends

Torus

Surface → VGroup

圆环

major_radius、minor_radius

Cube

VGroup

立方体

side_length

Prism

Cube → VGroup

长方体

dimensions=[x,y,z]

Dot3D

Sphere → Surface

3D 点

point、radius

Line3D

Cylinder → Surface

3D 线段

start、end、thickness

Arrow3D

Line3D → Cylinder

3D 箭头

height、base_radius(箭头头部)

常见错误与建议

常见错误

不要用 2D 的 Line/Arrow 在 3D 场景里连接两个 z 坐标不同的点——它们只画 XY 平面的投影。跨深度的连线请用 Line3D/Arrow3D。

常见错误

Surface 的 func 必须返回长度为 3 的 np.ndarray(如 np.array([u, v, u*v]))。返回 list 或形状不对会在构造时直接报错。

提示

resolution 是渲染速度的主要开关。-ql 下调低分辨率(如 (8, 8))调试,出正式片再恢复 (32, 32) 或更高。

版本说明

本节签名与默认值均基于 manim 0.21.0 本机 inspect 核实(如 Sphere 有 center 参数、Surface 直接继承 VGroup)。

自测

✏️ 练习

想画一张高度为 z = sin(x) + cos(y)、范围 x,y∈[-2,2] 的曲面,该怎么写?

✅ 参考答案

用 Surface 显式写出参数化函数:

def func(u, v):
    return np.array([u, v, np.sin(u) + np.cos(v)])

surface = Surface(func, u_range=(-2, 2), v_range=(-2, 2), resolution=(24, 24))
✏️ 练习

Cube 是 VMobject 吗?solids.arrange(RIGHT) 为什么能直接用在六个不同类型的实体组合上?

✅ 参考答案

Cube 直接继承 VGroup(再由 VGroup → VMobject),而 Cone/Sphere 等经 Surface 也继承 VGroup。因此所有 3D 实体都具有 VGroup/Mobject 的变换与布局方法,可以放进同一个 VGroup 后统一 arrange。

下一步

基本 3D 几何体已经齐备。下一节看多面体家族:正多面体、任意自定义多面体,以及从点集自动求凸包的 ConvexHull3D。