Creating Mushroom Animation
3D Voxel Mushroom Animation Tutorial
This guide walks you through how to generate a looping 3D voxel animation of mushrooms using SpatialStudio.
The script creates a magical mushroom grove with caps that glow, stems that sway gently, and spores floating in the air, all inside a cubic 3D space, then saves the animation to a .splv
file.
What this script does
-
Creates a 3D scene of size 128×128×128
-
Spawns 6 mushrooms of varying sizes, each with:
- A textured cap with natural color variations
- A sturdy stem that sways subtly in the breeze
- Glowing spots and highlights for a magical effect
-
Adds floating spores that drift through the air
-
Animates the grove for 8 seconds at 30 FPS with gentle swaying motion
-
Outputs the file
mushroom.splv
that you can play in your viewer
How it works (simplified)
-
Voxel volume Each frame is a 3D grid filled with RGBA values (
SIZE × SIZE × SIZE × 4
). -
Mushroom cap Caps are drawn as flattened spheres with realistic color gradients and spotted patterns.
-
Mushroom stem Each mushroom gets a cylindrical stem that gently sways with wind-like motion.
-
Glow effects Bright spots are added to mushroom caps to create a bioluminescent appearance.
-
Floating spores Small particles drift through the scene with natural Brownian motion.
-
Animation loop A normalized time variable
t
cycles from0 → 2π
, ensuring the motion loops smoothly. -
Encoding Frames are passed into
splv.Encoder
, which writes them into the.splv
video file.
Try it yourself
Install requirements first:
pip install spatialstudio numpy tqdm
Then copy this script into mushroom.py
and run:
python mushroom.py
Full Script
import numpy as np
from spatialstudio import splv
from tqdm import tqdm
# Scene setup
SIZE, FPS, SECONDS = 128, 30, 8
FRAMES = FPS * SECONDS
CENTER_X = CENTER_Y = CENTER_Z = SIZE // 2
OUT_PATH = "../outputs/mushroom.splv"
# Mushroom settings
MUSHROOM_COUNT = 6
CAP_RADIUS_BASE = 12
STEM_HEIGHT = 20
SPORE_COUNT = 25
def add_voxel(volume, x, y, z, color):
if 0 <= x < SIZE and 0 <= y < SIZE and 0 <= z < SIZE:
volume[x, y, z, :3] = color
volume[x, y, z, 3] = 255
def generate_mushroom_cap(volume, cx, cy, cz, color, radius, t, mushroom_id):
for dx in range(-radius, radius+1):
for dy in range(-3, 4):
for dz in range(-radius, radius+1):
# Create flattened sphere for cap
dist_xz = np.sqrt(dx*dx + dz*dz)
if dist_xz <= radius and abs(dy) <= 3:
# Add natural texture variation
texture = np.sin(dx*0.4 + dz*0.3 + t*0.2) * 0.15
brightness = 0.8 + texture
# Add spots pattern
spot_pattern = np.sin(dx*0.8) * np.sin(dz*0.8) * np.sin(t*0.1 + mushroom_id)
if spot_pattern > 0.6:
brightness += 0.4
final_color = tuple(min(255, int(c * brightness)) for c in color)
add_voxel(volume, cx+dx, cy+dy, cz+dz, final_color)
def generate_mushroom_stem(volume, cx, cy, cz, t, mushroom_id):
stem_color = (222, 184, 135) # Burlywood
sway_x = np.sin(t * 1.2 + mushroom_id * 0.5) * 2
sway_z = np.cos(t * 0.8 + mushroom_id * 0.3) * 1
for i in range(STEM_HEIGHT):
height_ratio = i / STEM_HEIGHT
stem_x = cx + int(sway_x * height_ratio)
stem_y = cy - 4 - i
stem_z = cz + int(sway_z * height_ratio)
# Create cylindrical stem with slight thickness variation
thickness = 2 + int(np.sin(i * 0.3) * 0.5)
for dx in range(-thickness, thickness+1):
for dz in range(-thickness, thickness+1):
if dx*dx + dz*dz <= thickness*thickness:
# Add stem texture
brightness = 0.9 + np.sin(i*0.2 + dx*0.1)*0.1
final_color = tuple(int(c * brightness) for c in stem_color)
add_voxel(volume, stem_x+dx, stem_y, stem_z+dz, final_color)
def generate_mushroom_glow(volume, cx, cy, cz, radius, t, mushroom_id):
glow_color = (255, 255, 200) # Soft yellow glow
intensity = 0.7 + 0.3 * np.sin(t * 2.0 + mushroom_id * 0.7)
for i in range(8):
angle = (i / 8.0) * 2 * np.pi + t * 0.5
gx = cx + int((radius * 0.7) * np.cos(angle))
gz = cz + int((radius * 0.7) * np.sin(angle))
gy = cy + 1
if intensity > 0.5: # Only show glow when intensity is high enough
for dx in range(-1, 2):
for dy in range(-1, 2):
for dz in range(-1, 2):
if dx*dx + dy*dy + dz*dz <= 2:
add_voxel(volume, gx+dx, gy+dy, gz+dz, glow_color)
def generate_floating_spores(volume, t):
spore_color = (255, 255, 150) # Light yellow
np.random.seed(42) # For consistent spore positions
for i in range(SPORE_COUNT):
# Each spore follows a unique path
base_x = np.random.randint(20, SIZE-20)
base_y = np.random.randint(40, SIZE-20)
base_z = np.random.randint(20, SIZE-20)
# Add floating motion
offset_x = np.sin(t * 0.8 + i * 0.3) * 15
offset_y = np.cos(t * 0.6 + i * 0.5) * 8
offset_z = np.sin(t * 1.1 + i * 0.7) * 12
spore_x = int(base_x + offset_x)
spore_y = int(base_y + offset_y)
spore_z = int(base_z + offset_z)
# Fade spores in and out
fade = 0.5 + 0.5 * np.sin(t * 1.5 + i * 0.4)
if fade > 0.3:
add_voxel(volume, spore_x, spore_y, spore_z, spore_color)
def generate_mushroom_grove(volume, t):
mushroom_colors = [
(220, 20, 60), # Crimson
(255, 69, 0), # Red Orange
(255, 140, 0), # Dark Orange
(184, 134, 11), # Dark Goldenrod
(128, 0, 128), # Purple
(75, 0, 130), # Indigo
]
for i in range(MUSHROOM_COUNT):
# Arrange mushrooms in a natural cluster
angle = (i / MUSHROOM_COUNT) * 2 * np.pi
distance = 20 + 10 * np.sin(i * 0.8)
mx = CENTER_X + int(distance * np.cos(angle))
my = CENTER_Y + 10 + int(3 * np.sin(t * 0.5 + i * 0.2)) # Slight bobbing
mz = CENTER_Z + int(distance * np.sin(angle))
# Vary mushroom sizes
radius = CAP_RADIUS_BASE + int(4 * np.sin(i * 0.6))
color = mushroom_colors[i % len(mushroom_colors)]
generate_mushroom_cap(volume, mx, my, mz, color, radius, t, i)
generate_mushroom_stem(volume, mx, my, mz, t, i)
generate_mushroom_glow(volume, mx, my, mz, radius, t, i)
def generate_scene(volume, t):
generate_mushroom_grove(volume, t)
generate_floating_spores(volume, t)
enc = splv.Encoder(SIZE, SIZE, SIZE, framerate=FPS, outputPath=OUT_PATH, motionVectors="off")
for frame in tqdm(range(FRAMES), desc="Growing mushroom grove"):
volume = np.zeros((SIZE, SIZE, SIZE, 4), dtype=np.uint8)
t = (frame / FRAMES) * 2*np.pi
generate_scene(volume, t)
enc.encode(splv.Frame(volume, lrAxis="x", udAxis="y", fbAxis="z"))
enc.finish()
print(f"Created {OUT_PATH}")
Next steps
- Change
MUSHROOM_COUNT
to create a larger or smaller grove. - Modify
mushroom_colors
to experiment with different mushroom varieties. - Adjust
SPORE_COUNT
to make the scene more or less magical. - Add ground terrain by creating a textured base layer.
- Experiment with different glow colors for bioluminescent effects.