Saturday, April 25, 2026

two or three ways to make LLMs help with Blender scene creation

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, CirclesVideos.blend - the prompt for Gemini 3, via the free tier of aistudio.google.com, is below.

Can you now generate a python script to create a blender scene with

  1. Fisheye 180 degree equidistant camera in the centre, pointing straight up

  2. equidistant circular screens subtending just less than 40 degrees so that they do not touch, having a small gap between them, 9 circular screens nearest the equator, and more rows of similarly sized circular screens, but fewer as we go up the hemisphere since the circumference of higher latitudes would be lower

  3. The lowest row of circular screens must be completely above the horizon for the viewport of the 180 degree fisheye equidistant camera

  4. The screens should fade in one by one starting from the lowest row, left to right, sequentially, one after the other, with a one second fade in time. So, circle one fades in by the 00:00:01 mark, then circle two fades in and finishes fading in by the 00:00:02 mark, and so on. The total duration of the animation can be one second greater than the time required to fade in all the circles.

  5. Each circular screen should have an image texture mapped to a unique file - named seq01.jpg for circle one, seq02.jpg for circle two and so on, with the jpg textures located in a subdirectory of the project directory called seq. The image texture would be square in shape, but should be zoomed in so that the entire circle is filled with the texture.


This resulted in the following:

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.
This had some problems - Actually, the camera should be Equidistant, not Equisolid. My prompt was wrong, and the LLM did not correct me. But I still went with the first suggestion. But just asking Gemini to create a scene with the first suggestion did not go well. The "orbs" were distorting the video texture too much, and other issues. So, I asked the LLM to tell me how I could accomplish specific tasks using the Blender UI, and that gave useful answers. That brings us to another way in which LLMs can help - acting like a step-by-step tutor.

Prompt was:
I would like the orbs to be just flat circles, always oriented towards the camera, on which if I texture videos, the middle section of the video would be cropped and seen, like a circular cutout. Possible?Instead of writing another script, you can just let me know which properties I need to change.

Which resulted in this useful answer:

Yes, this is completely possible and very common in motion graphics! It involves
creating a mathematical Alpha Mask in the shader to cut a perfect circle out of
a square plane.

Here are the exact manual steps to achieve this "circular billboard" look
without running any new scripts.

Step 1: Remove the Outer Bubbles

Since you just want flat circles, we no longer need the 3D glass shells.

1.  In your Outliner (top right), search for Bubble.
2.  Select all the Bubble_... objects and delete them. You should now only see
    your flat video screens.

Step 2: Make the Screens Square

Right now, the screens are shaped like 16:9 rectangles. If we cut a circle out
of a rectangle, it becomes an oval. We need to make them perfect squares.

1.  Select your Screen_... objects.
2.  Press N to open the side panel in the 3D viewport.
3.  Under Item, change their Scale to X: 1.0, Y: 1.0, and Z: 1.0.

Step 3: Create the Circular Cutout (The Shader)

Now we will tell the material to make the corners transparent, leaving only a
perfect circle in the middle. Select one of your screens (e.g., a Pleasure
screen) and go to the Shader Editor:

1.  Enable Transparency: Go to the Material Properties tab (the red sphere icon
    on the right). Scroll down to Settings and change Blend Mode from Opaque to
    Alpha Clip. (This ensures the corners actually disappear in your viewport).
2.  Add a Mix Shader and place it right before your Material Output node.
3.  Add a Transparent BSDF node. Plug it into the Top socket of the Mix Shader.
4.  Plug your Emission node (which has your video) into the Bottom socket of the
    Mix Shader.
5.  Make the Circle Math:
      - Add a Vector Math node and change the mode to Distance.
      - Add a standard Math node and change the mode to Less Than (Set the
        threshold value to 0.5).
6.  Connect the mask:
      - Plug UV (from your Texture Coordinate node) into the Top vector of the
        Distance node.
      - Type 0.5, 0.5, 0.0 into the Bottom vector of the Distance node.
      - Plug the Distance output into the Less Than node.
      - Plug the Less Than output into the Fac (factor) of your Mix Shader.

Your screen is now a perfect, sharp circle!

Step 4: Fix the "Squished" Video Aspect Ratio

Because we forced the screen to be a 1:1 square in Step 2, your 16:9 video is
now squished horizontally. We fix this by "zooming in" on the video texture so
the left and right sides get cropped off.

1.  Find the Mapping node attached to your Video Texture.
2.  Change Scale X to 0.562 (This is 9 divided by 16, which un-squishes the
    video).
3.  Change Location X to 0.219 (This perfectly re-centers the video inside your
    circle).

Repeat Steps 3 & 4 for your other two materials (Peace and Curiosity), and you
will have exactly what you are looking for: flat, perfect circular cutouts of
your videos floating in space, always tracking the camera!

Can I change the scale to 1.0 for all of them at once? Or do I have to select
each one and make the change to each one?

You can absolutely do them all at once! You do not need to do them one by one.

In Blender, there are two very fast ways to do this.

Method 1: The Fastest Way (Keyboard Shortcut)

Since you want the scale to be exactly 1.0, you can just use Blender's "Clear
Scale" command.

1.  In your Outliner (top right), type Screen in the search bar to filter your
    objects.
2.  Click the first screen, hold Shift, and click the last screen so all 15 of
    them are selected.
3.  Move your mouse into the main 3D Viewport.
4.  Press Alt + S on your keyboard.

Boom! Every selected object instantly resets its scale to 1.0, 1.0, 1.0.

Method 2: The "Magic Alt Key" Way (Good to know for the future)

If you ever want to change any setting for multiple objects at once in Blender
(not just resetting to 1.0), you use the Alt key.

1.  Select all your screens.
2.  Press N to open the side panel and look at the Scale X, Y, Z.
3.  Hold down the Alt key, click on the X scale, type 1.0, and hit Enter.
4.  Keep holding Alt and do the same for Y.

Holding Alt while changing a value in Blender tells the program: "Apply this
exact change to everything I currently have selected, not just the active
object."

To create the circular cutout, under Settings of Material Properties, I don't
see a Blend Mode option.

Ah, I see from your screenshot exactly why that is missing!

You are currently using the Cycles render engine (which the script set up
because it is required for the 180-degree Fulldome fisheye lens). The "Blend
Mode" dropdown only exists for the Eevee render engine.

Because Cycles is a physically-based raytracer, it calculates transparency
automatically. You do not need to flip any switches in the settings panel at
all!

You can completely skip Step 1.

Just go straight into the Shader Editor and build the node setup (Mix Shader +
Transparent BSDF + the Distance/Math nodes). As long as your 3D viewport is set
to Rendered View (the right-most shading sphere icon at the top right of your 3D
view), Cycles will automatically calculate the math and make the corners
invisible.

These suggestions were what I used to create the precursor to CirclesVideos.blend at the top of the post.

Edit: Coincidentally, I see now that Ron Proctor has released a video about using Blender Buddy (local LLM) - Blender Buddy Test Drive + ChatGPT Comparison

No comments:

Post a Comment