Skip to content
Back to skills

Blender Materials Shaders

ASecurity

Shader nodes, PBR materials, and procedural textures in Blender. Use for material creation, shader setups, PBR workflows, cross-engine compatibility (EEVEE_NEXT/Cycles), or when user mentions "material," "shader," "PBR," "texture," "node tree," or "Principled BSDF."

  • 6 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 3, 2026
ai-agentspythonrustnodeapiperformance

Works with

  • api
  • mcp

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 20, 2026

npx -y skills add sideshowroberto/vfx-agent-toolkit --skill blender-materials-shaders --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Blender Materials Shaders?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Blender Materials Shaders
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sideshowroberto-blender-materials-shaders/badge)](https://www.skillsdirectory.com/skills/sideshowroberto-blender-materials-shaders)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: blender-materials-shaders
description: Shader nodes, PBR materials, and procedural textures in Blender. Use for material creation, shader setups, PBR workflows, cross-engine compatibility (EEVEE_NEXT/Cycles), or when user mentions "material," "shader," "PBR," "texture," "node tree," or "Principled BSDF."
allowed-tools: Read,Write
---

# Blender Materials & Shaders Skill

**Version:** 2.1.0
**Last Updated:** 2026-09-10
**Dependencies:** Blender 5.1+

---

## Breaking Changes (4.5+ / 5.1+)

```python
import bpy

# Render engine (5.1+)
bpy.context.scene.render.engine = 'BLENDER_EEVEE_NEXT'  # Not 'BLENDER_EEVEE'

# Principled BSDF input names changed in 4.5.0 - use names not indices
bsdf.inputs['Transmission Weight'].default_value = 1.0  # was 'Transmission'
bsdf.inputs['Subsurface Weight'].default_value = 0.2    # was 'Subsurface'
bsdf.inputs['Emission Color'].default_value = (1,1,1,1) # was 'Emission'
bsdf.inputs['Base Color'].default_value = (0.8, 0.2, 0.2, 1.0)
```

---

## Procedural texturing on parametric geometry (loop-gauntlet lessons, 2026-09-10)

- **Use Texture Coordinate > Object, not the default Generated
  coordinates, for procedural textures on parametric architecture.** A
  Noise texture on Generated coordinates stretched into streaks on
  120-segment arc rings and long boxes; Object coordinates are metric and
  isotropic because all objects share the world origin.
- **Brick texture reads the X/Y of its input vector - it is planar in that
  plane, not aligned to the surface it is applied to.** On a wall lying in
  the YZ plane a Brick texture produces blotches instead of coursing. Build
  a Z-based course mask instead (SeparateXYZ > multiply > fract >
  less-than > bump) - it works on every wall orientation and lines up
  scene-wide.
- **A texture that shares a shader with an already-accepted plate must
  share its histogram.** Before assigning a new image plate to a shader
  slot that an accepted plate already uses, match the new plate's
  luminance mean and standard deviation to the accepted one (a two-line
  PIL script: read both images, scale/offset the new one to match mean and
  std). Two extra relief plates with greyscale means of 126 and 172 against
  an accepted plate's 181 rendered as dark grey stone instead of matching
  stone, costing a full iteration before the mismatch was found.
- **A photo's surface grain becomes an unwanted bump map at close range.**
  A photo-derived plate used as colour-plus-bump reads as rough bark
  texture when the nearest matched camera is close (about 2 m); reserve
  photo-derived plates for bands whose nearest camera is several metres
  away, and use the smoothest (e.g. generated) plate on close-up bands.

---

## QUICK START

### Create PBR Material

```python
import bpy

mat = bpy.data.materials.new("PBR_Material")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()

bsdf = nodes.new('ShaderNodeBsdfPrincipled')
bsdf.inputs['Base Color'].default_value = (0.8, 0.2, 0.2, 1.0)
bsdf.inputs['Roughness'].default_value = 0.5
bsdf.inputs['Metallic'].default_value = 0.0

output = nodes.new('ShaderNodeOutputMaterial')
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])

# Assign to active object
obj = bpy.context.active_object
if obj and obj.type == 'MESH':
    obj.data.materials.append(mat)
    print(f"Material '{mat.name}' assigned to '{obj.name}'")
```

---

## STANDARD WORKFLOWS

### Workflow 1: Procedural Texture (No Image Files)

**Use When:** Natural materials (stone, wood, rust) without image textures

```python
import bpy

mat = bpy.data.materials.new("Procedural_Stone")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()

texcoord = nodes.new('ShaderNodeTexCoord')
noise = nodes.new('ShaderNodeTexNoise')
noise.inputs['Scale'].default_value = 5.0
noise.inputs['Detail'].default_value = 8.0
noise.inputs['Roughness'].default_value = 0.6

ramp = nodes.new('ShaderNodeValToRGB')
ramp.color_ramp.elements[0].color = (0.2, 0.2, 0.2, 1.0)  # dark stone
ramp.color_ramp.elements[1].color = (0.7, 0.7, 0.7, 1.0)  # light stone

bsdf = nodes.new('ShaderNodeBsdfPrincipled')
bsdf.inputs['Roughness'].default_value = 0.85

output = nodes.new('ShaderNodeOutputMaterial')

links.new(texcoord.outputs['Object'], noise.inputs['Vector'])
links.new(noise.outputs['Fac'], ramp.inputs['Fac'])
links.new(ramp.outputs['Color'], bsdf.inputs['Base Color'])
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])

print("Procedural stone material created")
```

---

### Workflow 2: Glass / Transparent Material

```python
import bpy

mat = bpy.data.materials.new("Glass")
mat.use_nodes = True
mat.blend_method = 'BLEND'
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()

bsdf = nodes.new('ShaderNodeBsdfPrincipled')
bsdf.inputs['Transmission Weight'].default_value = 1.0   # 4.5+ name
bsdf.inputs['IOR'].default_value = 1.45
bsdf.inputs['Roughness'].default_value = 0.0

output = nodes.new('ShaderNodeOutputMaterial')
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])

# EEVEE_NEXT: enable ray tracing for refractions
bpy.context.scene.eevee.use_raytracing = True

print("Glass material created")
```

---

### Workflow 3: Material Layering with Mix Shader

**Use When:** Combining materials (metal + rust, clean + dirty, worn + fresh)

```python
import bpy

mat = bpy.data.materials.new("Layered_Metal")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()

# Clean metal
bsdf_metal = nodes.new('ShaderNodeBsdfPrincipled')
bsdf_metal.inputs['Metallic'].default_value = 1.0
bsdf_metal.inputs['Roughness'].default_value = 0.2
bsdf_metal.inputs['Base Color'].default_value = (0.7, 0.7, 0.7, 1.0)

# Rust / weathering layer
bsdf_rust = nodes.new('ShaderNodeBsdfPrincipled')
bsdf_rust.inputs['Base Color'].default_value = (0.5, 0.2, 0.1, 1.0)
bsdf_rust.inputs['Roughness'].default_value = 0.9

# Noise-based mask
noise = nodes.new('ShaderNodeTexNoise')
noise.inputs['Scale'].default_value = 10.0

mix = nodes.new('ShaderNodeMixShader')

output = nodes.new('ShaderNodeOutputMaterial')

links.new(noise.outputs['Fac'], mix.inputs['Fac'])
links.new(bsdf_metal.outputs['BSDF'], mix.inputs[1])
links.new(bsdf_rust.outputs['BSDF'], mix.inputs[2])
links.new(mix.outputs['Shader'], output.inputs['Surface'])

print("Layered metal/rust material created")
```

---

### Workflow 4: Reusable Node Group

```python
import bpy

# Create a reusable weathering node group
group = bpy.data.node_groups.new("Weathering", 'ShaderNodeTree')
group_in = group.nodes.new('NodeGroupInput')
group_out = group.nodes.new('NodeGroupOutput')

# Interface (5.1+ uses interface API)
iface = group.interface
iface.new_socket("Scale", in_out='INPUT', socket_type='NodeSocketFloat')
iface.new_socket("Mask", in_out='OUTPUT', socket_type='NodeSocketColor')

noise = group.nodes.new('ShaderNodeTexNoise')
group.links.new(group_in.outputs['Scale'], noise.inputs['Scale'])
group.links.new(noise.outputs['Color'], group_out.inputs['Mask'])

# Use the group in a material
mat = bpy.data.materials.new("Weathered")
mat.use_nodes = True
nodes = mat.node_tree.nodes
node_group = nodes.new('ShaderNodeGroup')
node_group.node_tree = group
node_group.inputs['Scale'].default_value = 5.0

print(f"Node group '{group.name}' created and referenced")
```

---

## ADVANCED TECHNIQUES

### Cross-Engine Optimization

```python
import bpy

engine = bpy.context.scene.render.engine

if engine == 'BLENDER_EEVEE_NEXT':
    # EEVEE: performance-conscious, keep it simple
    mat.refraction_depth = 0.1
    # Real-time: avoid expensive SSS and volumetrics unless needed
else:  # CYCLES
    # Full ray tracing - enable SSS, caustics, etc.
    bsdf.inputs['Subsurface Weight'].default_value = 0.1
    bsdf.inputs['Subsurface Radius'].default_value = (0.1, 0.05, 0.03)
```

---

## TROUBLESHOOTING

### Material Not Appearing in Viewport

```python
import bpy

# Set viewport to Material Preview or Rendered shading
for area in bpy.context.screen.areas:
    if area.type == 'VIEW_3D':
        for space in area.spaces:
            if space.type == 'VIEW_3D':
                space.shading.type = 'MATERIAL'  # NOT 'MATERIAL_PREVIEW' (4.5+)
```

### Shader Socket AttributeError

```python
# 'NodeSocketShader' has no default_value - it only accepts links
# [FAIL] bsdf.outputs['BSDF'].default_value = ...
# [OK] Just connect it:
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
```

### Material Renders Black

```python
import bpy

nodes = mat.node_tree.nodes
links = mat.node_tree.links

# Verify output node exists
output = next((n for n in nodes if n.type == 'OUTPUT_MATERIAL'), None)
if not output:
    output = nodes.new('ShaderNodeOutputMaterial')

# Ensure BSDF is connected to surface
if not output.inputs['Surface'].is_linked:
    bsdf = next((n for n in nodes if n.type == 'BSDF_PRINCIPLED'), None)
    if bsdf:
        links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
        print("Fixed: reconnected BSDF to output")
```

---

## VALIDATION CHECKLIST

- [ ] Engine set (`BLENDER_EEVEE_NEXT` or `CYCLES`)
- [ ] BSDF inputs use 4.5+ naming convention
- [ ] Material Output node connected
- [ ] Viewport shading = `MATERIAL` (not `MATERIAL_PREVIEW`)
- [ ] Test render validates appearance

---

## VERSION HISTORY

**v2.1.0** (2026-09-10) - Loop-gauntlet lessons: procedural textures need
Object coordinates (not Generated) on parametric geometry, Brick texture is
planar-XY (use a Z-based course mask for walls), and image plates sharing a
shader with an accepted plate must be histogram-matched, with photo-derived
plates reserved for bands far from the nearest camera.

**v2.0.0** (2026-06-10) - MCP migration
- Removed HTTP Bridge requirements and curl steps
- Removed `import requests` / `requests.post()` wrappers
- Added `import bpy` to all code blocks
- Updated target: Blender 5.1+
- Added node group workflow with 5.1+ interface API

**v1.0.0** (2025-10-24) - Initial release

---

**Status:** Production Ready
**Maintainer:** VFX Pipeline Team
**Dependencies:** Blender 5.1+, Python 3.11+

Files in this skill

  • SKILL.md7.9 KB
  • reference/ADVANCED_SHADER_NODES.md32.1 KB
  • reference/PBR_WORKFLOWS.md30.3 KB
  • reference/PROCEDURAL_TEXTURES.md39.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.