3D Rendering with Blender | Computational Mechanics Visualization | Skill-Lync Resources

50% OFF - Ends Soon!

Lesson 10 of 11 15 min

3D Rendering with Blender

Blender is a free, open-source 3D creation suite. Its Python API (bpy) allows you to create stunning photorealistic renders of FEA results — perfect for presentations, publications, and marketing materials.

Why Blender for CAE?

ToolStrengthWeakness
ParaViewFast, scientificBasic graphics
MatplotlibFamiliar, scriptable2D-focused
BlenderPhotorealisticSteeper learning curve

For executive presentations or marketing materials, Blender renders stand out.

Example Output

Here's what you'll create in this lesson — a deformed tensile specimen with stress coloring:

Sponsored

Ranjith switched from IT to core automotive industry

His inspiring career transition story with video

See His Journey
Blender Deformed Specimen

The render shows:

  • Necking deformation at the center
  • Stress colormap (blue = low, red = high)
  • Professional three-point lighting

Installation

  • Download Blender from [blender.org](https://www.blender.org/download/)
  • Blender includes Python — no separate installation needed
  • Run scripts from Blender's Text Editor or command line

Running Python in Blender

Method 1: Blender Text Editor

  • Open Blender
  • Switch to "Scripting" workspace
  • Click "New" in the Text Editor
  • Paste your script and click "Run Script"

Method 2: Command Line

blender --background --python myscript.py

Basic bpy Concepts

import bpy

# Delete all objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

# Create a mesh
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
cube = bpy.context.active_object
cube.name = "FEA_Element"

# Apply material
mat = bpy.data.materials.new(name="StressMaterial")
mat.use_nodes = True
mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (1, 0, 0, 1)
cube.data.materials.append(mat)

Full Example: Deformed Tensile Specimen

This script creates a deformed tensile specimen with stress coloring:

"""
Deformed Tensile Specimen Visualization
Automotive Application: Seat belt anchor stress analysis
"""
import bpy
import numpy as np

# =============================================================================
# CONFIGURATION
# =============================================================================

LENGTH = 4.0          # Specimen length
WIDTH = 1.0           # Specimen width
THICKNESS = 0.2       # Specimen thickness
ELONGATION = 0.3      # 30% stretch
NECK_FACTOR = 0.5     # Necking intensity
RESOLUTION = 20       # Mesh resolution

OUTPUT_PATH = "/tmp/deformed_specimen.png"

# =============================================================================
# SETUP
# =============================================================================

def clear_scene():
    """Delete all objects in the scene."""
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete()

def setup_camera():
    """Position camera for good view of specimen."""
    bpy.ops.object.camera_add(location=(6, -4, 3))
    camera = bpy.context.active_object
    camera.rotation_euler = (np.radians(70), 0, np.radians(50))
    bpy.context.scene.camera = camera

def setup_lighting():
    """Add three-point lighting for professional look."""
    # Key light
    bpy.ops.object.light_add(type='AREA', location=(3, -3, 5))
    key = bpy.context.active_object
    key.data.energy = 500
    key.data.size = 3

    # Fill light
    bpy.ops.object.light_add(type='AREA', location=(-3, -2, 3))
    fill = bpy.context.active_object
    fill.data.energy = 200
    fill.data.size = 2

    # Rim light
    bpy.ops.object.light_add(type='AREA', location=(0, 4, 4))
    rim = bpy.context.active_object
    rim.data.energy = 300
    rim.data.size = 2

# =============================================================================
# GEOMETRY
# =============================================================================

def create_deformed_specimen():
    """Create tensile specimen with necking deformation."""
    # Create base mesh
    bpy.ops.mesh.primitive_cube_add(
        size=1,
        location=(0, 0, 0),
        scale=(LENGTH, WIDTH, THICKNESS)
    )
    specimen = bpy.context.active_object
    specimen.name = "TensileSpecimen"

    # Subdivide for smooth deformation
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.subdivide(number_cuts=RESOLUTION)
    bpy.ops.object.mode_set(mode='OBJECT')

    # Apply deformation to vertices
    mesh = specimen.data
    for vert in mesh.vertices:
        x, y, z = vert.co

        # Normalize x position (0 to 1 along length)
        x_norm = (x / LENGTH + 0.5)

        # Axial stretch (uniform elongation)
        new_x = x * (1 + ELONGATION)

        # Necking: reduce width at center
        # Gaussian-like profile centered at x=0
        neck_profile = np.exp(-8 * x**2 / LENGTH**2)
        width_factor = 1 - NECK_FACTOR * neck_profile * ELONGATION

        new_y = y * width_factor
        new_z = z * width_factor

        vert.co = (new_x, new_y, new_z)

    return specimen

def apply_stress_material(obj):
    """Apply stress-colored material based on vertex position."""
    mat = bpy.data.materials.new(name="StressMaterial")
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    links = mat.node_tree.links

    # Clear default nodes
    for node in nodes:
        nodes.remove(node)

    # Create nodes
    output = nodes.new('ShaderNodeOutputMaterial')
    output.location = (400, 0)

    principled = nodes.new('ShaderNodeBsdfPrincipled')
    principled.location = (100, 0)
    principled.inputs['Metallic'].default_value = 0.3
    principled.inputs['Roughness'].default_value = 0.4

    # Color ramp for stress visualization
    ramp = nodes.new('ShaderNodeValToRGB')
    ramp.location = (-200, 0)
    ramp.color_ramp.elements[0].color = (0, 0, 1, 1)  # Blue (low stress)
    ramp.color_ramp.elements[1].color = (1, 0, 0, 1)  # Red (high stress)

    # Add intermediate colors
    elem = ramp.color_ramp.elements.new(0.25)
    elem.color = (0, 1, 1, 1)  # Cyan

    elem = ramp.color_ramp.elements.new(0.5)
    elem.color = (0, 1, 0, 1)  # Green

    elem = ramp.color_ramp.elements.new(0.75)
    elem.color = (1, 1, 0, 1)  # Yellow

    # Geometry node for position
    geometry = nodes.new('ShaderNodeNewGeometry')
    geometry.location = (-600, 0)

    # Separate XYZ
    separate = nodes.new('ShaderNodeSeparateXYZ')
    separate.location = (-400, 0)

    # Map range to normalize position
    map_range = nodes.new('ShaderNodeMapRange')
    map_range.location = (-200, -150)
    map_range.inputs['From Min'].default_value = -LENGTH/2 * (1 + ELONGATION)
    map_range.inputs['From Max'].default_value = LENGTH/2 * (1 + ELONGATION)
    map_range.inputs['To Min'].default_value = 0
    map_range.inputs['To Max'].default_value = 1

    # Connect nodes
    links.new(geometry.outputs['Position'], separate.inputs['Vector'])
    links.new(separate.outputs['X'], map_range.inputs['Value'])
    links.new(map_range.outputs['Result'], ramp.inputs['Fac'])
    links.new(ramp.outputs['Color'], principled.inputs['Base Color'])
    links.new(principled.outputs['BSDF'], output.inputs['Surface'])

    obj.data.materials.append(mat)

# =============================================================================
# RENDERING
# =============================================================================

def setup_render():
    """Configure render settings."""
    scene = bpy.context.scene

    # Use Cycles for realistic rendering
    scene.render.engine = 'CYCLES'
    scene.cycles.samples = 128
    scene.cycles.use_denoising = True

    # Output settings
    scene.render.resolution_x = 1920
    scene.render.resolution_y = 1080
    scene.render.filepath = OUTPUT_PATH

    # Background
    world = bpy.data.worlds['World']
    world.use_nodes = True
    bg = world.node_tree.nodes['Background']
    bg.inputs['Color'].default_value = (0.05, 0.05, 0.08, 1)  # Dark blue-gray

def render():
    """Render the scene."""
    bpy.ops.render.render(write_still=True)
    print(f"Rendered to {OUTPUT_PATH}")

# =============================================================================
# MAIN
# =============================================================================

def main():
    clear_scene()
    setup_camera()
    setup_lighting()

    specimen = create_deformed_specimen()
    apply_stress_material(specimen)

    setup_render()
    render()

if __name__ == "__main__":
    main()

Run with:

Sponsored

3,000+ engineers placed at Mahindra, Bosch, TATA ELXSI

Including Continental, Capgemini, Ola Electric & 500+ more companies

See Where They Work
blender --background --python deformed_specimen.py
🎯 3,000+ Engineers Placed
Sponsored
Harshal Sukenkar

Harshal

Fiat Chrysler

Abhishek

Abhishek

TATA ELXSI

Srinithin

Srinithin

Xitadel

Ranjith

Ranjith

Core Automotive

Gaurav Jadhav

Gaurav

Automotive Company

Bino K Biju

Bino

Design Firm

Aseem Shrivastava

Aseem

EV Company

Puneet

Puneet

Automotive Company

Vishal Kumar

Vishal

EV Startup

Importing FEA Results

For real FEA post-processing, you can import mesh data from VTK or OBJ files:

import bpy
import numpy as np

def import_vtk_mesh(vtk_file):
    """Import mesh from VTK file (simplified example)."""
    # In practice, use pyvista or meshio to read VTK
    # Then create mesh in Blender

    vertices = []  # Read from VTK
    faces = []     # Read from VTK
    stress = []    # Read from VTK

    # Create mesh
    mesh = bpy.data.meshes.new("FEA_Result")
    mesh.from_pydata(vertices, [], faces)
    mesh.update()

    obj = bpy.data.objects.new("FEA_Result", mesh)
    bpy.context.collection.objects.link(obj)

    # Apply vertex colors based on stress
    if not mesh.vertex_colors:
        mesh.vertex_colors.new()

    color_layer = mesh.vertex_colors.active

    # Map stress to color (simplified)
    stress_norm = (stress - stress.min()) / (stress.max() - stress.min())
    # ... apply to vertex colors

Animation: Deformation Sequence

Create an animation by keyframing deformation over time:

import bpy

def animate_deformation(obj, frames=60):
    """Animate deformation from reference to current config."""
    mesh = obj.data
    original_coords = [v.co.copy() for v in mesh.vertices]

    for frame in range(frames + 1):
        bpy.context.scene.frame_set(frame)
        t = frame / frames  # 0 to 1

        for i, vert in enumerate(mesh.vertices):
            # Interpolate from original to deformed
            orig = original_coords[i]
            # Apply time-dependent deformation
            vert.co.x = orig.x * (1 + 0.3 * t)  # Stretch
            vert.co.y = orig.y * (1 - 0.15 * t)  # Contract
            vert.co.z = orig.z * (1 - 0.15 * t)

        # Insert keyframe for all vertices
        mesh.update()
        obj.keyframe_insert(data_path="location", frame=frame)

Exercises

Exercise 1: Add Reference Configuration

Modify the script to show both reference (wireframe) and current (solid) configurations side by side.

Sponsored

Get an IIT Jammu PG certification

Recognized by Mahindra, Bosch, TATA ELXSI & 500+ companies

See Program Details

Exercise 2: Stress Colormap

Implement a proper von Mises stress colormap using vertex colors instead of position-based coloring.

Exercise 3: Camera Animation

Create a 360-degree turntable animation around the specimen.

Render Quality Settings

SettingPreviewFinal
Samples32256+
Resolution960x5401920x1080
DenoisingOnOn
EngineEEVEECycles

Key Takeaways

  • Blender's Python API (bpy) enables scripted CAE visualization
  • Run scripts with blender --background --python script.py
  • Three-point lighting creates professional renders
  • Cycles engine produces photorealistic results
  • Export animations as MP4 or image sequences

What's Next

You've completed all the tutorial lessons. Test your knowledge in the Quiz to earn your course completion badge.

3,000+ Engineers Placed in Top Companies
Career Growth

3,000+ Engineers Placed in Top Companies

Join the ranks of successful engineers at Bosch, Tata, L&T, and 500+ hiring partners.