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, edge softening, 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. Edge Softening: "To transform generic blocks into a polished, toy-like aesthetic, the script applies a Bevel modifier with multiple segments to smoothly round off the sharp corners of the body segments."
  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."

Bevel

An operation or modifier that slants or rounds off the sharp edges of a 3D object. In blocky modeling styles, this captures light reflections nicely and gives models a physical, tangible quality.

Context: "Adding a Bevel modifier removes the razor-sharp edges from the cube primitives."

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():
    """Creates a Voronoi material that won't stretch."""
    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):
    """Creates a mesh part and links it to the specified collection."""
    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)

    # Link to collection if provided
    if collection:
        for col in obj.users_collection:
            col.objects.unlink(obj)
        collection.objects.link(obj)
        
    return obj

# --- Main Execution ---
# Clean scene and setup collection
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()

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

# Main Body Parts
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)

neck_angle, neck_l, neck_loc = math.radians(-25), 3.5, (0, 1.15, 5.2)
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)

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
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)

# Legs Setup
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 
    create_part(f"Joint_{name}", (0.35, 0.35, 0.35), (x, y, joint_z), material=pattern_mat, shape='SPHERE', collection=giraffe_col)
    create_part(name, (0.2, 0.2, h), (x, y, joint_z - (h/2)), material=pattern_mat, bevel_width=0.02, collection=giraffe_col)

# Detail Parts
create_part("Ear_L", (0.3, 0.1, 0.2), (-0.32, h_y - 0.15, h_z + 0.3), (0, 0, math.radians(30)), color=COLOR_BROWN, bevel_width=0.02, collection=giraffe_col)
create_part("Ear_R", (0.3, 0.1, 0.2), (0.32, h_y - 0.15, h_z + 0.3), (0, 0, math.radians(-30)), color=COLOR_BROWN, bevel_width=0.02, collection=giraffe_col)

create_part("Ossicone_L", (0.1, 0.1, 0.4), (-0.15, h_y - 0.1, h_z + 0.4), color=COLOR_DARK, bevel_width=0.01, collection=giraffe_col)
create_part("Ossicone_R", (0.1, 0.1, 0.4), (0.15, h_y - 0.1, h_z + 0.4), color=COLOR_DARK, bevel_width=0.01, collection=giraffe_col)
create_part("Eye_L", (0.1, 0.1, 0.1), (-0.28, h_y + 0.2, h_z + 0.1), color=COLOR_DARK, bevel_width=0.005, collection=giraffe_col)
create_part("Eye_R", (0.1, 0.1, 0.1), (0.28, h_y + 0.2, h_z + 0.1), color=COLOR_DARK, bevel_width=0.005, collection=giraffe_col)
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)

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

Comments

Popular posts from this blog

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

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

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