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

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

The Story

The giraffe is instantly recognizable for its towering height, elongated neck, and beautiful, distinct camouflage spots. Translating these organic features into a stylized, blocky 3D figurine involves balancing geometric simplicity with precise procedural texturing. By using a Python script in Blender, we can build a charming low-poly version of a giraffe that utilizes mathematical principles to align its long limbs and a continuous Voronoi texture to generate its iconic pattern. This project offers an excellent path to master the English vocabulary associated with programmatic coordinates, hierarchical parenting, and spatial proportions within a 3D pipeline.

Reference photo of a giraffe in the savanna
Reference Image
3D giraffe model created with Python script
3D Model Render

AI's Explanation

  1. Seamless Patterning: "To prevent the giraffe's spots from stretching over the elongated neck and legs, I linked the 'Object' output of the Texture Coordinate node to the Voronoi texture, ensuring an even distribution across all dimensions."
  2. Hierarchical Parenting: "To ensure natural movement, the script establishes a chained Parenting system. The Torso acts as the root parent, which drives the Neck, while the Neck drives the Head, and the Head controls the facial features."
  3. Anatomical Proportions: "Establishing realistic Proportions required calculated rotations. I tilted the torso and neck using precise mathematical radians to capture the giraffe's majestic, upward-reaching stance."

Key Words and Phrases

Voronoi

A type of procedural noise function that divides space into cell-like regions based on distance to specific points. It is ideal for generating biological patterns like giraffe spots, scales, or cracked mud.

Context: "I used a Voronoi node to generate the organic spot pattern programmatically."

Parenting

The process of establishing a hierarchical relationship between 3D objects, where one object (the child) inherits the transformations of another object (the parent). Chain parenting links multiple objects sequentially to mimic structural systems like skeletal joints.

Context: "Implementing chain Parenting allows the head and features to follow the movement of the neck naturally."

Proportions

The comparative relation between things or magnitudes as to size, quantity, or ratio. Keeping the relative scale of the limbs and neck realistic ensures the animal looks convincing even when stylized.

Context: "Adjusting the neck length was essential to capture the correct Proportions of the giraffe."

Script Preview

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

Python
import bpy
import math

# --- Configuration Constants ---
GIRAFFE_COLLECTION_NAME = "Giraffe_Model"
MATERIAL_NAME = "Giraffe_Pattern"
COLOR_BROWN = (0.6, 0.3, 0.1, 1)
COLOR_CREAM = (0.9, 0.8, 0.7, 1)
COLOR_DARK = (0.05, 0.02, 0.01, 1)

def create_giraffe_material():
    mat = bpy.data.materials.new(name=MATERIAL_NAME)
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    links = mat.node_tree.links
    
    for n in nodes: nodes.remove(n)
    
    output = nodes.new(type='ShaderNodeOutputMaterial')
    bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
    ramp = nodes.new(type='ShaderNodeValToRGB')
    voronoi = nodes.new(type='ShaderNodeTexVoronoi')
    coord = nodes.new(type='ShaderNodeTexCoord')

    links.new(coord.outputs['Object'], voronoi.inputs['Vector'])

    voronoi.voronoi_dimensions = '3D'
    voronoi.feature = 'F1'
    if hasattr(voronoi, "distance"):
        voronoi.distance = 'EUCLIDEAN'
    else:
        voronoi.distance_metric = 'EUCLIDEAN'
        
    voronoi.inputs['Scale'].default_value = 4.5 
    voronoi.inputs['Randomness'].default_value = 0.8
    
    ramp.color_ramp.interpolation = 'CONSTANT'
    ramp.color_ramp.elements[0].position = 0.55
    ramp.color_ramp.elements[0].color = (0.32, 0.15, 0.05, 1)
    ramp.color_ramp.elements[1].position = 0.60
    ramp.color_ramp.elements[1].color = (0.9, 0.8, 0.7, 1)

    links.new(voronoi.outputs['Distance'], ramp.inputs['Fac'])
    links.new(ramp.outputs['Color'], bsdf.inputs['Base Color'])
    
    bsdf.inputs['Roughness'].default_value = 0.3
    links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
    return mat

def create_part(name, scale, location, rotation=(0, 0, 0), color=None, material=None, bevel_width=0.04, shape='CUBE', collection=None):
    if shape == 'SPHERE':
        bpy.ops.mesh.primitive_uv_sphere_add(radius=0.5, segments=32, ring_count=16)
    else:
        bpy.ops.mesh.primitive_cube_add(size=1)
        
    obj = bpy.context.active_object
    obj.name = name
    obj.scale = scale
    obj.location = location
    obj.rotation_euler = rotation
    
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    
    if shape == 'CUBE' and bevel_width > 0:
        bevel_mod = obj.modifiers.new(name="Bevel", type='BEVEL')
        bevel_mod.width = bevel_width
        bevel_mod.segments = 4
        bpy.ops.object.shade_smooth()
    elif shape == 'SPHERE':
        bpy.ops.object.shade_smooth()
    
    if material:
        obj.data.materials.append(material)
    elif color:
        mat = bpy.data.materials.new(name=f"Mat_{name}")
        mat.use_nodes = True
        bsdf = mat.node_tree.nodes["Principled BSDF"]
        bsdf.inputs['Base Color'].default_value = color
        bsdf.inputs['Roughness'].default_value = 0.3
        obj.data.materials.append(mat)

    if collection:
        for col in obj.users_collection:
            col.objects.unlink(obj)
        collection.objects.link(obj)
        
    return obj

def establish_parent(child, parent):
    if child and parent:
        old_matrix = child.matrix_world.copy()
        child.parent = parent
        child.matrix_parent_inverse = parent.matrix_world.inverted()

# --- Main Execution ---
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

giraffe_col = bpy.data.collections.new(GIRAFFE_COLLECTION_NAME)
bpy.context.scene.collection.children.link(giraffe_col)

pattern_mat = create_giraffe_material()

torso_rot = math.radians(12)
torso_loc = (0, 0, 3.2)

# 1. Torso (The Root Parent)
torso = create_part("Torso", (1.0, 1.8, 1.2), torso_loc, (torso_rot, 0, 0), material=pattern_mat, bevel_width=0.08, collection=giraffe_col)

# 2. Neck & Head Chain (Torso -> Neck -> Head)
neck_angle, neck_l, neck_loc = math.radians(-25), 3.5, (0, 1.15, 5.2)
neck = create_part("Neck", (0.4, 0.4, neck_l), neck_loc, (neck_angle, 0, 0), material=pattern_mat, bevel_width=0.04, collection=giraffe_col)
establish_parent(neck, torso)

h_y = neck_loc[1] + (math.sin(-neck_angle) * (neck_l/2)) + 0.3
h_z = neck_loc[2] + (math.cos(-neck_angle) * (neck_l/2)) + 0.1
head = create_part("Head", (0.5, 1.0, 0.5), (0, h_y, h_z), (math.radians(10), 0, 0), material=pattern_mat, bevel_width=0.05, collection=giraffe_col)
establish_parent(head, neck)

# 3. Legs & Joints (Parented to Torso)
f_y, f_z = torso_loc[1] + (math.cos(torso_rot) * 0.8), torso_loc[2] + (math.sin(torso_rot) * 0.8)
b_y, b_z = torso_loc[1] - (math.cos(torso_rot) * 0.8), torso_loc[2] - (math.sin(torso_rot) * 0.8)

leg_data = [("FL", -0.4, f_y, f_z, 3.35), ("FR", 0.4, f_y, f_z, 3.35),
            ("BL", -0.4, b_y, b_z, 3.0), ("BR", 0.4, b_y, b_z, 3.0)]

for name, x, y, az, h in leg_data:
    joint_z = az - 0.1 
    joint = create_part(f"Joint_{name}", (0.35, 0.35, 0.35), (x, y, joint_z), material=pattern_mat, shape='SPHERE', collection=giraffe_col)
    establish_parent(joint, torso)
    
    leg = create_part(name, (0.2, 0.2, h), (x, y, joint_z - (h/2)), material=pattern_mat, bevel_width=0.02, collection=giraffe_col)
    establish_parent(leg, torso)

# 4. Tail (Parented to Torso)
tail = create_part("Tail", (0.1, 0.1, 1.2), (0, b_y, b_z), (math.radians(-45), 0, 0), color=COLOR_DARK, bevel_width=0.01, collection=giraffe_col)
establish_parent(tail, torso)

# 5. Head Detail Parts (Parented to Head)
head_details = [
    ("Ear_L", (0.3, 0.1, 0.2), (-0.32, h_y - 0.15, h_z + 0.3), (0, 0, math.radians(30)), COLOR_BROWN, None, 0.02, 'CUBE'),
    ("Ear_R", (0.3, 0.1, 0.2), (0.32, h_y - 0.15, h_z + 0.3), (0, 0, math.radians(-30)), COLOR_BROWN, None, 0.02, 'CUBE'),
    ("Ossicone_L", (0.1, 0.1, 0.4), (-0.15, h_y - 0.1, h_z + 0.4), (0, 0, 0), COLOR_DARK, None, 0.01, 'CUBE'),
    ("Ossicone_R", (0.1, 0.1, 0.4), (0.15, h_y - 0.1, h_z + 0.4), (0, 0, 0), COLOR_DARK, None, 0.01, 'CUBE'),
    ("Eye_L", (0.1, 0.1, 0.1), (-0.28, h_y + 0.2, h_z + 0.1), (0, 0, 0), COLOR_DARK, None, 0.005, 'CUBE'),
    ("Eye_R", (0.1, 0.1, 0.1), (0.28, h_y + 0.2, h_z + 0.1), (0, 0, 0), COLOR_DARK, None, 0.005, 'CUBE')
]

for name, scale, loc, rot, col, mat, bevel, shape in head_details:
    hd_obj = create_part(name, scale, loc, rot, color=col, material=mat, bevel_width=bevel, shape=shape, collection=giraffe_col)
    establish_parent(hd_obj, head)

bpy.ops.object.select_all(action='DESELECT')

Comments

Popular posts from this blog

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

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

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