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

Gaurav Jadhav is now a CAE Engineer

Practical projects and mock interviews made the difference

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

Harshal got placed at Fiat Chrysler as Design Engineer

Watch his video testimonial on how the program helped him

See His Journey
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

April batch closing soon — only 42 seats remaining

Join 3,000+ engineers who got placed at top companies

Reserve Your Seat

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.