If we directly ask Gemini to create a Blender file, it says it can't, but it can generate a python script which can be run inside Blender to create a scene. This is useful if we need to generate repetitive visuals or something which can be programmatically generated. Example,
, is below.
Here is the complete Python script to generate your exact fulldome scene.
It handles the math for perfectly spacing the rows along the hemisphere without
overlapping, scales the circular cutouts to exactly 38 degrees (leaving
a 2-degree gap so they don't touch), sets up the sequential 1-second fade-ins,
and auto-links your seq/seqXX.jpg files.
How to use it:
1. Open a new Blender file.
2. Go to the Scripting tab.
3. Click New, paste the code below, and click the Run Script (Play) button.
4. Set your 3D Viewport to Rendered View to see the fade-in math and image
textures working perfectly.
import bpy
import math
# ==========================================
# 1. CLEANUP PREVIOUS SCENE
# ==========================================
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
# ==========================================
# 2. SCENE & ANIMATION SETTINGS
# ==========================================
FPS = 24
TOTAL_SCREENS = 14
FADE_DURATION = FPS * 1 # 1 second fade in per screen
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
scene.render.resolution_x = 2048
scene.render.resolution_y = 2048
scene.render.fps = FPS
# Total duration = 1 second per screen, plus 1 second at the end
TOTAL_FRAMES = (TOTAL_SCREENS * FADE_DURATION) + FADE_DURATION
scene.frame_start = 1
scene.frame_end = TOTAL_FRAMES
# ==========================================
# 3. FULLDOME CAMERA (Equidistant)
# ==========================================
cam_data = bpy.data.cameras.new("Fulldome_Camera")
cam_data.type = 'PANO'
cam_data.panorama_type = 'FISHEYE_EQUIDISTANT'
cam_data.fisheye_fov = math.radians(180)
cam_obj = bpy.data.objects.new("Fulldome_Camera", cam_data)
bpy.context.collection.objects.link(cam_obj)
scene.camera = cam_obj
# Camera at center (0,0,0) pointing straight UP (Zenith)
cam_obj.location = (0, 0, 0)
cam_obj.rotation_euler = (math.radians(180), 0, 0)
# Dark Background
world = scene.world
world.use_nodes = True
bg_node = world.node_tree.nodes.get("Background")
if bg_node:
bg_node.inputs[0].default_value = (0.01, 0.01, 0.02, 1)
# ==========================================
# 4. MATH: SCREEN PLACEMENTS
# ==========================================
# Dome Radius
R = 10.0
# To subtend exactly 38 degrees (leaving a gap under 40deg),
# the physical radius of the plane at distance R is R * tan(19 degrees)
screen_radius = R * math.tan(math.radians(19))
# Calculate positions (Elevation and Azimuth)
# Row 1: 9 screens at 20 degrees elevation.
# (Bottom edge is 20 - 19 = 1 degree above horizon. Perfect!)
# Row 2: 5 screens at 60 degrees elevation.
# (Bottom edge is 60 - 19 = 41 degrees. Top of Row 1 is 39. Gap of 2 degrees!)
screen_data = []
# Row 1 (9 Screens, lowest row)
for i in range(9):
azimuth = i * (360.0 / 9.0)
screen_data.append({'el': 20.0, 'az': azimuth})
# Row 2 (5 Screens, upper row)
# Offset azimuth slightly so they interlock beautifully
for i in range(5):
azimuth = i * (360.0 / 5.0) + 36.0
screen_data.append({'el': 60.0, 'az': azimuth})
# ==========================================
# 5. MATERIAL GENERATOR
# ==========================================
def create_fade_circle_material(index, start_frame, end_frame):
mat_name = f"Mat_Screen_{index:02d}"
mat = bpy.data.materials.new(name=mat_name)
mat.use_nodes = True
mat.blend_method = 'CLIP' # Helps viewport transparency
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()
# Base Nodes
output = nodes.new(type='ShaderNodeOutputMaterial')
output.location = (1000, 0)
# Fade Setup (Mix Shader 2)
fade_mix = nodes.new(type='ShaderNodeMixShader')
fade_mix.location = (800, 0)
fade_trans = nodes.new(type='ShaderNodeBsdfTransparent')
fade_trans.location = (600, 100)
# Cutout Setup (Mix Shader 1)
cutout_mix = nodes.new(type='ShaderNodeMixShader')
cutout_mix.location = (600, -100)
cutout_trans = nodes.new(type='ShaderNodeBsdfTransparent')
cutout_trans.location = (400, 0)
# Video/Image Setup
emission = nodes.new(type='ShaderNodeEmission')
emission.location = (400, -200)
emission.inputs['Strength'].default_value = 1.5
img_tex = nodes.new(type='ShaderNodeTexImage')
img_tex.location = (100, -200)
# Assuming relative path in a "seq" folder next to your .blend file
img_path = f"//seq/seq{index:02d}.jpg"
# Try to load the image if it exists, otherwise leave placeholder
try:
img = bpy.data.images.load(img_path)
img_tex.image = img
except:
print(f"Warning: Could not load {img_path}. Placeholder created.")
# Math for the Circle Mask
tex_coord = nodes.new(type='ShaderNodeTexCoord')
tex_coord.location = (-300, 0)
distance = nodes.new(type='ShaderNodeVectorMath')
distance.operation = 'DISTANCE'
distance.location = (-100, 0)
distance.inputs[1].default_value = (0.5, 0.5, 0.0)
less_than = nodes.new(type='ShaderNodeMath')
less_than.operation = 'LESS_THAN'
less_than.location = (100, 0)
less_than.inputs[1].default_value = 0.5
# Wire it all up
links.new(tex_coord.outputs['UV'], distance.inputs[0])
links.new(distance.outputs['Value'], less_than.inputs[0])
links.new(tex_coord.outputs['UV'], img_tex.inputs['Vector'])
links.new(img_tex.outputs['Color'], emission.inputs['Color'])
links.new(less_than.outputs['Value'], cutout_mix.inputs['Fac'])
links.new(cutout_trans.outputs['BSDF'], cutout_mix.inputs[1])
links.new(emission.outputs['Emission'], cutout_mix.inputs[2])
links.new(fade_trans.outputs['BSDF'], fade_mix.inputs[1])
links.new(cutout_mix.outputs['Shader'], fade_mix.inputs[2])
links.new(fade_mix.outputs['Shader'], output.inputs['Surface'])
# --- ANIMATE THE FADE ---
# At start of animation: completely transparent
fade_mix.inputs['Fac'].default_value = 0.0
fade_mix.inputs['Fac'].keyframe_insert(data_path="default_value", frame=1)
# Just before its turn: still transparent
fade_mix.inputs['Fac'].default_value = 0.0
fade_mix.inputs['Fac'].keyframe_insert(data_path="default_value", frame=start_frame)
# 1 second later: fully visible
fade_mix.inputs['Fac'].default_value = 1.0
fade_mix.inputs['Fac'].keyframe_insert(data_path="default_value", frame=end_frame)
return mat
# ==========================================
# 6. SPAWN SCREENS
# ==========================================
for i, data in enumerate(screen_data):
el_rad = math.radians(data['el'])
az_rad = math.radians(data['az'])
# Convert polar to Cartesian XYZ
z = R * math.sin(el_rad)
r_xy = R * math.cos(el_rad)
x = r_xy * math.cos(az_rad)
y = r_xy * math.sin(az_rad)
# 1. Create the Plane
bpy.ops.mesh.primitive_plane_add(size=1.0, location=(x, y, z))
plane = bpy.context.active_object
# Index is i+1 (so it starts at 1)
idx = i + 1
plane.name = f"Screen_{idx:02d}"
# 2. Scale to exact angular size (38 degrees)
# We multiply by 2 because screen_radius is from the center to edge.
plane.scale = (screen_radius * 2, screen_radius * 2, 1.0)
# 3. Add Track To Constraint (Always face camera)
track = plane.constraints.new(type='TRACK_TO')
track.target = cam_obj
track.track_axis = 'TRACK_Z'
track.up_axis = 'UP_Y'
# 4. Create and assign animated material
start_f = (i * FADE_DURATION) + 1
end_f = start_f + FADE_DURATION
mat = create_fade_circle_material(idx, start_f, end_f)
plane.data.materials.append(mat)
print(f"Fulldome scene generated! Total screens: {TOTAL_SCREENS}. Animation frames: {TOTAL_FRAMES}")
LLMs can even give us alternate ideas. For example, this conversation,
Prompt for Gemini,
I'd like to show a montage of videos in Blender to be rendered for fulldome (180
degree fisheye equisolid camera). The script is "We constantly seek happiness in
the form of pleasure, peace, or the satisfaction of curiosity." My current idea
is to depict a pyramid with video textures on the four sides depicting
"pleasure, peace, or the satisfaction of curiosity." Any other ideas?
Which resulted in this reply -
Designing for a fulldome (180° fisheye) is a completely different beast than
designing for a flat screen.
Your pyramid idea is conceptually strong, but in a fulldome environment, a
single object floating in the center of the screen can sometimes feel a bit
"small," leaving the vast periphery of the dome empty. In fulldome, you want to
surround the viewer or give them a sense of scale and immersion.
Here are a few alternative concepts specifically tailored to make the most of
the 180° fisheye camera in Blender, incorporating your video montages and
script.
1. The Canopy of Spheres (Focus on "Looking Up")
- The Visual: The camera sits at the base of a massive, glowing, abstract tree
or DNA-like structure that stretches up to the "zenith" (the dead center of
your fisheye circle, which is the top of the dome). Instead of leaves,
giant, slow-moving translucent bubbles or glass orbs hang above the viewer.
- The Montage: Inside these orbs are your videos.
- Pleasure videos can be in warm, rapidly pulsing orange/red orbs.
- Peace videos in slow-spinning, tranquil blue/teal orbs.
- Curiosity videos in geometric, bright white/purple orbs.
- Why it works for Fulldome: Audiences in planetariums naturally look up. By
placing the anchor of the structure at the bottom edge of the fisheye (the
springline) and having it branch out overhead, you create a massive sense of
scale and immersion.
2. The Monolith Portals (Surrounding the Viewer)
- The Visual: Instead of looking at a pyramid from the outside, place the
camera inside a massive, open space (like a serene desert, a starry void, or
a calm ocean). Standing out of the ground, surrounding the camera in
a 360-degree radius (or semi-circle), are three monolithic, towering
screens/portals.
- The Montage: As the narrator speaks, the camera slowly rotates.
- "Pleasure" lights up the monolith to the left, reflecting its video off
the glossy floor.
- "Peace" lights up the center monolith.
- "Curiosity" lights up the right monolith.
- Why it works for Fulldome: It places the viewer inside the geometry rather
than watching it from afar. Wide, towering objects that wrap around the
peripheral vision (the edges of the fisheye circle) look incredibly imposing
and immersive in a dome.
3. The Tunnel of Seeking (Forward Motion)
- The Visual: "Constantly seek" implies forward momentum. The camera is moving
slowly and continuously forward through a massive, abstract, hexagonal or
circular tunnel.
- The Montage: The walls of the tunnel are made of floating video panels.
- As the word "Pleasure" is spoken, panels whiz by showing vibrant,
sensory clips.
- For "Peace," the tunnel widens, the movement slows slightly, and the
panels show calming nature or meditative clips.
- For "Curiosity," the tunnel breaks apart into a constellation of
floating screens showing science, exploration, or complex data.
- Why it works for Fulldome: Forward motion (called vection) is the single
most powerful optical illusion in a fulldome. A slow, steady forward push
through a tunnel makes the audience feel like they are physically moving
through space.
4. The Fluid Kaleidoscope (Abstract Blending)
- The Visual: Instead of harsh geometric shapes (like a pyramid), map your
videos onto fluid, undulating mesh surfaces—like ribbons of silk or waves of
water—that drift right over the camera lens.
- The Montage:
- A vibrant ribbon of video (Pleasure) swoops across the dome.
- It is intercepted by a slow-moving, massive, calm wave of video (Peace)
that washes over the whole screen.
- Finally, a sharply focused, fractal-like geometric burst of video
screens (Curiosity) shatters through the peaceful wave.
- Why it works for Fulldome: Sharp edges and straight lines (like a pyramid)
are heavily curved and distorted by fisheye lenses. Organic, fluid shapes
hide this distortion and feel incredibly natural and dreamlike in a dome.
5. If You Stick With the Pyramid: The Interior Pyramid
If you really love the pyramid motif, flip the perspective. Place the camera
inside a colossal pyramid.
- The Visual: The camera looks up toward the apex of the pyramid (which sits
in the center of your fisheye render). The walls of the pyramid surround the
viewer.
- The Montage: The three visible interior walls of the pyramid become massive
projection mapping surfaces. One wall illuminates with "Pleasure," the next
with "Peace," and the third with "Curiosity." As the script concludes, the
apex of the pyramid opens up, revealing a starry sky or a bright light
(representing the overarching "Happiness").
💡 Quick Blender Fulldome Tips:
1. Camera Setup: Set your camera to Panoramic -> Fisheye Equisolid. Field of
View: 180 degrees. Render resolution must be perfectly square (e.g., 4096
x 4096).
2. The Sweet Spot: In a dome, the audience is usually looking about 30 to 45
degrees up from the bottom edge. Don't put your most important video exactly
in the dead center (zenith), as it forces the audience to crane their necks
straight up.
3. Avoid Fast Camera Pans: Panning left or right in a dome causes severe motion
sickness. Keep camera movements to slow forward/backward tracking, or very
slow rotations. Let the objects (the video panels) move around the camera
instead.