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

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

The Story

The canary, a vibrant songbird known for its bright plumage and compact, agile frame, presents an excellent subject for procedural 3D modeling. Translating this delicate biological form into a digital model requires precise control over primitive shapes and their spatial relationships. By utilizing a Python script in Blender, we can procedurally construct the bird's structure, from its slender beak and expressive eyes to the detailed hierarchy of its wings and toes. This exercise provides a valuable opportunity to practice technical English vocabulary related to geometry manipulation, scene organization, and programmatic material definition within a 3D modeling pipeline.

Reference photo of a canary
Reference Image
3D canary model created with Python script
3D Model Render

AI's Explanation

  1. Configuration: "We define a dictionary to house the Configuration of every part, allowing us to specify mesh types, scaling factors, and material assignments in a centralized, readable format."
  2. Parenting: "To ensure the bird maintains its integrity during animation, we establish Parenting hierarchies, such as linking toes to the legs and the legs to the body, ensuring all child objects follow their parent's transformations."
  3. Roughness: "By manipulating the Roughness values within our material registry, we can differentiate between the soft, matte look of feathers and the polished, low-roughness quality of the bird's eyes."

Key Words and Phrases

Configuration

The arrangement of parameters that defines a system's state. In this script, it allows us to systematically build the canary by defining the unique properties of each body segment.

Context: "The Configuration dictionary serves as the blueprint for generating the complex anatomy of the bird."

Parenting

Establishing a parent-child relationship where objects inherit transformation data. This is crucial for keeping the bird's limbs and appendages organized.

Context: "Defining precise Parenting relationships ensures the wings move correctly relative to the body."

Roughness

A shader setting that controls light diffusion. Adjusting this parameter helps achieve the desired surface realism for different parts of the canary model.

Context: "Setting low Roughness values for the eyes makes them appear reflective and lifelike."

Script Preview

Copy the code below into Blender's Text Editor to generate your own 3D Canary figurine.

Python
import bpy
import mathutils

def create_bird_model():
    # 1. Configuration Data
    materials_config = {
        "RedFactor": {"color": [1.0, 0.25, 0.0, 1.0], "roughness": 0.5},
        "Beak": {"color": [0.9, 0.7, 0.6, 1.0], "roughness": 0.5},
        "Leg": {"color": [0.7, 0.5, 0.4, 1.0], "roughness": 0.2},
        "Eye": {"color": [0.01, 0.01, 0.01, 1.0], "roughness": 0.1}
    }

    parts_config = {
        "Body": {"type": "uv_sphere", "params": {"radius": 1.0}, "scale": [1.2, 0.8, 0.9], "mat": "RedFactor", "subdiv": "CATMULL_CLARK"},
        "Head": {"type": "uv_sphere", "params": {"radius": 0.6}, "scale": [1.1, 1.0, 1.0], "mat": "RedFactor", "subdiv": "CATMULL_CLARK"},
        "Beak": {"type": "cone", "params": {"radius1": 0.2, "depth": 0.5}, "mat": "Beak", "subdiv": "SIMPLE"},
        "Eye_L": {"type": "uv_sphere", "params": {"radius": 0.08}, "mat": "Eye", "subdiv": "CATMULL_CLARK"},
        "Eye_R": {"type": "uv_sphere", "params": {"radius": 0.08}, "mat": "Eye", "subdiv": "CATMULL_CLARK"},
        "Wing_L": {"type": "uv_sphere", "params": {"radius": 1.0}, "scale": [0.817, 0.201, 0.463], "mat": "RedFactor", "subdiv": "CATMULL_CLARK"},
        "Wing_R": {"type": "uv_sphere", "params": {"radius": 1.0}, "scale": [0.817, 0.201, 0.463], "mat": "RedFactor", "subdiv": "CATMULL_CLARK"},
        "Tail": {"type": "uv_sphere", "params": {"radius": 1.0}, "scale": [0.45, 0.45, 0.45], "mat": "RedFactor", "subdiv": "CATMULL_CLARK"},
        "Feather1": {"type": "uv_sphere", "params": {"radius": 1.0}, "scale": [1.5, 0.45, 0.1], "mat": "RedFactor", "subdiv": "CATMULL_CLARK"},
        "Feather2": {"type": "uv_sphere", "params": {"radius": 1.0}, "scale": [1.5, 0.45, 0.1], "mat": "RedFactor", "subdiv": "CATMULL_CLARK"},
        "Leg_L": {"type": "cylinder", "params": {"radius": 0.03, "depth": 1.0}, "mat": "Leg", "subdiv": "SIMPLE"},
        "Leg_R": {"type": "cylinder", "params": {"radius": 0.03, "depth": 1.0}, "mat": "Leg", "subdiv": "SIMPLE"},
        "M_Toe_L": {"type": "cube", "params": {}, "scale": [0.4, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"},
        "M_Toe_R": {"type": "cube", "params": {}, "scale": [0.4, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"},
        "B_Toe_L": {"type": "cube", "params": {}, "scale": [0.3, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"},
        "B_Toe_R": {"type": "cube", "params": {}, "scale": [0.3, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"},
        "F_Toe_1_L": {"type": "cube", "params": {}, "scale": [0.4, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"},
        "F_Toe_1_R": {"type": "cube", "params": {}, "scale": [0.4, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"},
        "F_Toe_2_L": {"type": "cube", "params": {}, "scale": [0.4, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"},
        "F_Toe_2_R": {"type": "cube", "params": {}, "scale": [0.4, 0.03, 0.02], "mat": "Leg", "subdiv": "SIMPLE"}
    }

    hierarchy = [
        {"name": "Body", "parent": None, "loc": [0.0, 0.0, 0.0], "rot": [0.0, 0.0, 0.0]},
        {"name": "Head", "parent": "Body", "loc": [0.8, 0.0, 0.6], "rot": [0.0, 0.0, 0.0]},
        {"name": "Beak", "parent": "Head", "loc": [1.45, 0.0, 0.6], "rot": [0.0, 1.57, 0.0]},
        {"name": "Eye_L", "parent": "Head", "loc": [1.1, 0.45, 0.8], "rot": [0.0, 0.0, 0.0]},
        {"name": "Eye_R", "parent": "Head", "loc": [1.1, -0.45, 0.8], "rot": [0.0, 0.0, 0.0]},
        {"name": "Wing_L", "parent": "Body", "loc": [-0.382, 0.718, 0.130], "rot": [0.0, 0.0, 0.0]},
        {"name": "Wing_R", "parent": "Body", "loc": [-0.382, -0.718, 0.130], "rot": [0.0, 0.0, 0.0]},
        {"name": "Tail", "parent": "Body", "loc": [-1.0, 0.0, 0.1], "rot": [0.0, 0.0, 0.0]},
        {"name": "Feather1", "parent": "Tail", "loc": [-1.5, 0.0, 0.3], "rot": [0.26, -0.26, -0.26]},
        {"name": "Feather2", "parent": "Tail", "loc": [-1.5, 0.0, 0.3], "rot": [-0.26, -0.26, 0.26]},
        {"name": "Leg_L", "parent": "Body", "loc": [-0.35, 0.25, -1.0], "rot": [0.0, -0.21, 0.0]},
        {"name": "Leg_R", "parent": "Body", "loc": [-0.35, -0.25, -1.0], "rot": [0.0, -0.21, 0.0]},
        {"name": "M_Toe_L", "parent": "Leg_L", "loc": [-0.05, 0.25, -1.499], "rot": [0.0, 0.12, 0.0]},
        {"name": "M_Toe_R", "parent": "Leg_R", "loc": [-0.05, -0.25, -1.499], "rot": [0.0, 0.12, 0.0]},
        {"name": "B_Toe_L", "parent": "Leg_L", "loc": [-0.392, 0.25, -1.502], "rot": [0.0, -0.21, 0.0]},
        {"name": "B_Toe_R", "parent": "Leg_R", "loc": [-0.392, -0.25, -1.502], "rot": [0.0, -0.21, 0.0]},
        {"name": "F_Toe_1_L", "parent": "Leg_L", "loc": [-0.068, 0.334, -1.5], "rot": [0.038, 0.128, 0.43]},
        {"name": "F_Toe_1_R", "parent": "Leg_R", "loc": [-0.068, -0.334, -1.5], "rot": [0.038, 0.128, -0.43]},
        {"name": "F_Toe_2_L", "parent": "Leg_L", "loc": [-0.068, 0.165, -1.5], "rot": [-0.038, 0.128, -0.43]},
        {"name": "F_Toe_2_R", "parent": "Leg_R", "loc": [-0.068, -0.165, -1.5], "rot": [-0.038, 0.128, 0.43]}
    ]

    col_name = "Collection_Canary"
    if col_name in bpy.data.collections:
        for obj in bpy.data.collections[col_name].objects:
            bpy.data.objects.remove(obj, do_unlink=True)
    else:
        new_col = bpy.data.collections.new(col_name)
        bpy.context.scene.collection.children.link(new_col)
    target_col = bpy.data.collections[col_name]

    mat_registry = {}
    for name, cfg in materials_config.items():
        mat = bpy.data.materials.new(name=name)
        mat.use_nodes = True
        bsdf = mat.node_tree.nodes["Principled BSDF"]
        bsdf.inputs['Base Color'].default_value = cfg["color"]
        bsdf.inputs['Roughness'].default_value = cfg["roughness"]
        mat_registry[name] = mat

    for name, cfg in parts_config.items():
        if cfg["type"] == "uv_sphere": bpy.ops.mesh.primitive_uv_sphere_add(**cfg["params"])
        elif cfg["type"] == "cone": bpy.ops.mesh.primitive_cone_add(**cfg["params"])
        elif cfg["type"] == "cylinder": bpy.ops.mesh.primitive_cylinder_add(**cfg["params"])
        elif cfg["type"] == "cube": bpy.ops.mesh.primitive_cube_add(size=1.0)
        
        obj = bpy.context.active_object
        obj.name = name
        subsurf = obj.modifiers.new(name="Subdivision", type='SUBSURF')
        subsurf.subdivision_type = cfg.get("subdiv", "CATMULL_CLARK")
        subsurf.levels = 2
        subsurf.render_levels = 2
        bpy.context.view_layer.objects.active = obj
        bpy.ops.object.modifier_apply(modifier=subsurf.name)
        bpy.ops.object.shade_smooth()
        
        if "scale" in cfg: obj.scale = cfg["scale"]
        obj.data.materials.append(mat_registry[cfg["mat"]])
        for col in obj.users_collection: col.objects.unlink(obj)
        target_col.objects.link(obj)

    for item in hierarchy:
        obj = bpy.data.objects.get(item["name"])
        if item["parent"]:
            parent_obj = bpy.data.objects.get(item["parent"])
            if parent_obj:
                obj.parent = parent_obj
                obj.matrix_parent_inverse = parent_obj.matrix_world.inverted()

    for item in hierarchy:
        obj = bpy.data.objects.get(item["name"])
        loc = mathutils.Vector(item["loc"])
        rot = mathutils.Euler(item["rot"]).to_matrix().to_4x4()
        scale_vec = mathutils.Vector(parts_config[item["name"]].get("scale", [1.0, 1.0, 1.0]))
        scale = mathutils.Matrix.Scale(scale_vec[0], 4, (1, 0, 0)) @ \
                mathutils.Matrix.Scale(scale_vec[1], 4, (0, 1, 0)) @ \
                mathutils.Matrix.Scale(scale_vec[2], 4, (0, 0, 1))
        obj.matrix_world = mathutils.Matrix.Translation(loc) @ rot @ scale

create_bird_model()

Comments

Popular posts from this blog

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

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

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