Learning English through AI-Driven 3D Modeling: Crafting a Rabbit

Learning English through AI-Driven 3D Modeling: Crafting a Rabbit

The Story

The stylized white rabbit, often inspired by traditional snow rabbits, is characterized by its compact, rounded body, long ears with delicate pink inner shading, and striking ruby-red eyes. Translating these soft, biological shapes into a clean 3D figurine requires structured spatial organization and smooth surface topology. By using a Python script in Blender, we can programmatically construct this charming character from geometric spheres, applying subdivision modifiers to achieve a pristine finish. This project serves as an ideal practical exercise for mastering English vocabulary related to safe scene initialization, programmatic material assignment, and structural model management within an automated pipeline.

Reference photo of a white rabbit with red eyes
Reference Image
3D rabbit model created with Python script
3D Model Render

AI's Explanation

  1. Model Management: "To ensure a clean outliner hierarchy, the script automatically generates a dedicated Collection named 'Collection_Rabbit', containing all individual mesh components rather than cluttering the root scene."
  2. Hierarchical Parenting: "To maintain safe transform offsets during future animations, we implement a robust Parenting function that mathematically inverts the parent's world matrix when attaching the limbs and head to the primary body root."
  3. Surface Refinement: "To transform low-poly primitive UV spheres into an organic, smooth shape, the script programmatically assigns a Subdivision modifier combined with native smooth shading to melt low-poly corners into a soft, organic surface form."

Key Words and Phrases

Collection

An organizational container in Blender used to group related objects together. Automating collection setup prevents clutter in the Outliner and simplifies visibility toggles and asset linking.

Context: "Creating a custom Collection helps isolate the rabbit components from the rest of the scene environment."

Parenting

The process of establishing a hierarchical relationship where child objects inherit the spatial transformations (location, rotation, scale) of their designated parent object, vital for clean rigging and motion.

Context: "Proper Parenting guarantees that the eyes, ears, and nose remain perfectly attached when moving the head."

Subdivision

A modeling modifier that splits existing geometry into smaller polygons to generate a smoother surface finish. It allows low-resolution topology to appear highly polished and curved.

Context: "Applying a Subdivision surface modifier gives the rabbit body its soft, clay-like appearance."

Script Preview

Copy the code below into Blender's Text Editor to generate your own Toy Rabbit figurine.

Python
import bpy
import math

def setup_clear_scene():
    """Clears all mesh objects from the initial scene safely."""
    if bpy.context.object and bpy.context.object.mode != 'OBJECT':
        bpy.ops.object.mode_set(mode='OBJECT')
    
    # Select and delete all mesh objects
    bpy.ops.object.select_all(action='DESELECT')
    for obj in bpy.context.scene.objects:
        if obj.type == 'MESH':
            obj.select_set(True)
    bpy.ops.object.delete()

def get_or_create_collection(collection_name):
    """Creates a custom collection if it doesn't exist and links it to the scene."""
    if collection_name in bpy.data.collections:
        return bpy.data.collections[collection_name]
    
    new_collection = bpy.data.collections.new(collection_name)
    bpy.context.scene.collection.children.link(new_collection)
    return new_collection

def create_advanced_material(name, color, roughness=0.5, specular=0.5):
    """Creates a standard Principled BSDF material with specified parameters."""
    mat = bpy.data.materials.new(name=name)
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    
    # Get the Principled BSDF node
    bsdf = nodes.get("Principled BSDF")
    if bsdf:
        bsdf.inputs['Base Color'].default_value = color
        bsdf.inputs['Roughness'].default_value = roughness
        if 'Specular' in bsdf.inputs:
            bsdf.inputs['Specular'].default_value = specular
        elif 'Specular IOR Level' in bsdf.inputs:
            bsdf.inputs['Specular IOR Level'].default_value = specular
            
    return mat

def apply_subdivision_and_smooth(obj, levels=2):
    """Applies Subdivision Surface modifier and enables smooth shading natively."""
    if hasattr(obj.data, "polygons"):
        for poly in obj.data.polygons:
            poly.use_smooth = True
            
    subsurf = obj.modifiers.new(name="Subdivision", type='SUBSURF')
    subsurf.levels = levels
    subsurf.render_levels = levels

def set_parent_keep_transform(child, parent):
    """Establishes a parent-child relationship while safely preserving world transformation."""
    child.parent = parent
    child.matrix_parent_inverse = parent.matrix_world.inverted()

def create_rabbit_model():
    """Generates a high-quality stylized rabbit inside a dedicated managing collection."""
    setup_clear_scene()

    # --- Collection Setup for Better Model Management ---
    rabbit_collection = get_or_create_collection("Collection_Rabbit")
    
    # Safely backup and set active layer collection using proper Blender API properties
    original_layer_collection = bpy.context.view_layer.active_layer_collection
    rabbit_layer_collection = bpy.context.view_layer.layer_collection.children.get(rabbit_collection.name)
    if rabbit_layer_collection:
        bpy.context.view_layer.active_layer_collection = rabbit_layer_collection

    # --- Material Palette Definition ---
    white_fur_mat = create_advanced_material("Mat_WhiteFur", (0.95, 0.95, 0.95, 1.0), roughness=0.8, specular=0.2)
    ruby_eye_mat = create_advanced_material("Mat_RubyEye", (0.6, 0.01, 0.03, 1.0), roughness=0.05, specular=1.0)
    pink_nose_mat = create_advanced_material("Mat_PinkNose", (1.0, 0.5, 0.55, 1.0), roughness=0.4, specular=0.4)
    inner_ear_mat = create_advanced_material("Mat_InnerEar", (1.0, 0.75, 0.8, 1.0), roughness=0.6, specular=0.3)

    # --- 1. Main Parent Body ---
    bpy.ops.mesh.primitive_uv_sphere_add(segments=32, ring_count=16, radius=1.0, location=(0.0, 0.0, 0.0))
    body = bpy.context.active_object
    body.name = "Rabbit_Body"
    body.scale = (1.1, 1.4, 0.95)
    body.data.materials.append(white_fur_mat)
    apply_subdivision_and_smooth(body, levels=2)
    
    bpy.context.view_layer.update()

    # --- 2. Head ---
    bpy.ops.mesh.primitive_uv_sphere_add(segments=32, ring_count=16, radius=0.7, location=(0.0, -1.1, 0.75))
    head = bpy.context.active_object
    head.name = "Rabbit_Head"
    head.scale = (1.0, 0.95, 0.95)
    head.data.materials.append(white_fur_mat)
    apply_subdivision_and_smooth(head, levels=2)
    
    set_parent_keep_transform(head, body)
    bpy.context.view_layer.update()

    # --- 3. Ears ---
    for side in ["R", "L"]:
        sign = 1.0 if (side == "R") else -1.0
        
        bpy.ops.mesh.primitive_uv_sphere_add(segments=32, ring_count=16, radius=0.22, location=(0.28 * sign, -1.05, 1.45))
        ear = bpy.context.active_object
        ear.name = f"Rabbit_Ear_{side}"
        ear.scale = (0.75, 0.45, 2.8)
        ear.rotation_euler = (0.15, 0.18 * sign, -0.05 * sign)
        
        ear.data.materials.append(white_fur_mat)
        ear.data.materials.append(inner_ear_mat)
        
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.object.mode_set(mode='OBJECT')
        
        for poly in ear.data.polygons:
            if poly.normal.y < -0.35 and abs(poly.normal.x) < 0.75 and poly.center.z < 0.4:
                poly.material_index = 1
                
        bpy.ops.object.mode_set(mode='OBJECT')
        apply_subdivision_and_smooth(ear, levels=2)
        
        set_parent_keep_transform(ear, head)

    # --- 4. Eyes ---
    for side in ["R", "L"]:
        sign = 1.0 if side == "R" else -1.0
        bpy.ops.mesh.primitive_uv_sphere_add(segments=16, ring_count=16, radius=0.1, location=(0.44 * sign, -1.54, 0.92))
        eye = bpy.context.active_object
        eye.name = f"Rabbit_Eye_{side}"
        eye.scale = (0.9, 0.6, 0.9)
        eye.rotation_euler = (0.1, 0.1 * sign, 0.45 * sign)
        eye.data.materials.append(ruby_eye_mat)
        apply_subdivision_and_smooth(eye, levels=2)
        
        set_parent_keep_transform(eye, head)

    # --- 5. Nose ---
    bpy.ops.mesh.primitive_uv_sphere_add(segments=16, ring_count=16, radius=0.08, location=(0.0, -1.75, 0.7))
    nose = bpy.context.active_object
    nose.name = "Rabbit_Nose"
    nose.scale = (1.2, 0.8, 0.7)
    nose.data.materials.append(pink_nose_mat)
    apply_subdivision_and_smooth(nose, levels=2)
    
    set_parent_keep_transform(nose, head)

    # --- 6. Front Legs ---
    for side in ["R", "L"]:
        sign = 1.0 if side == "R" else -1.0
        bpy.ops.mesh.primitive_uv_sphere_add(segments=16, ring_count=16, radius=0.18, location=(0.4 * sign, -0.9, -0.65))
        leg_f = bpy.context.active_object
        leg_f.name = f"Rabbit_Leg_Front_{side}"
        leg_f.scale = (1.0, 1.3, 0.8)
        leg_f.data.materials.append(white_fur_mat)
        apply_subdivision_and_smooth(leg_f, levels=2)
        
        set_parent_keep_transform(leg_f, body)

    # --- 7. Back Legs ---
    for side in ["R", "L"]:
        sign = 1.0 if side == "R" else -1.0
        bpy.ops.mesh.primitive_uv_sphere_add(segments=16, ring_count=16, radius=0.32, location=(0.7 * sign, 0.4, -0.5))
        leg_b = bpy.context.active_object
        leg_b.name = f"Rabbit_Leg_Back_{side}"
        leg_b.scale = (0.9, 1.5, 0.9)
        leg_b.data.materials.append(white_fur_mat)
        apply_subdivision_and_smooth(leg_b, levels=2)
        
        set_parent_keep_transform(leg_b, body)

    # --- 8. Tail ---
    bpy.ops.mesh.primitive_uv_sphere_add(segments=16, ring_count=16, radius=0.25, location=(0.0, 1.3, 0.3))
    tail = bpy.context.active_object
    tail.name = "Rabbit_Tail"
    tail.scale = (1.0, 0.9, 1.0)
    tail.data.materials.append(white_fur_mat)
    apply_subdivision_and_smooth(tail, levels=2)
    
    set_parent_keep_transform(tail, body)

    # Restore the original active layer collection context safely
    bpy.context.view_layer.active_layer_collection = original_layer_collection

if __name__ == "__main__":
    create_rabbit_model()

Comments

Popular posts from this blog

シャキシャキ美味しい!スナップエンドウ、英語でどう言う?

[AI & Seasonal English]: 半夏生編 -「半夏生」で日本の美しい暦と英語を学ぼう!

[AI & Seasonal English] 【スパイス香る】ドイツのクリスマスパン「シュトーレン(Stollen)」の歴史と「しっとり感」を語る英語