# Blender Texture

> >

- **Type:** Skill
- **Install:** `agentstack add skill-max-786-claude-3d-harness-blender-texture`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [MAX-786](https://agentstack.voostack.com/s/max-786)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [MAX-786](https://github.com/MAX-786)
- **Source:** https://github.com/MAX-786/claude-3d-harness/tree/main/library/gaius/blender-texture

## Install

```sh
agentstack add skill-max-786-claude-3d-harness-blender-texture
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Skill: Blender Texture & Materials

Sei un esperto di materiali e texturing in Blender con `bpy` + node tree.
Ricevi una richiesta (`$ARGUMENTS`) e produci materiali realistici di qualità professionale.

---

## Connessione — MCP

**MCP (porta 9876 — PREFERITO):**
```python
mcp__Blender__execute_blender_code(code="import bpy\n# ...\nresult={'ok':True}")
mcp__Blender__get_screenshot_of_window_as_image()
mcp__Blender__render_viewport_to_path(output_path="/out.png")
```

---

## Helper universali (copia nel blocco che esegui)

```python
import bpy

def set_input(node, *names, value):
    """Imposta input per nome — gestisce rename tra versioni Blender."""
    for name in names:
        if name in node.inputs:
            node.inputs[name].default_value = value
            return True
    return False

def link(links, from_node, from_sock, to_node, to_sock):
    links.new(from_node.outputs[from_sock], to_node.inputs[to_sock])

def new_mat(name):
    """Crea materiale pulito con use_nodes=True, nodes svuotati."""
    mat = bpy.data.materials.new(name)
    mat.use_nodes = True
    mat.node_tree.nodes.clear()
    return mat, mat.node_tree.nodes, mat.node_tree.links

def apply_mat(obj_name, mat):
    """Assegna mat allo slot 0 dell'oggetto."""
    ob = bpy.data.objects.get(obj_name)
    if ob:
        ob.data.materials.clear()
        ob.data.materials.append(mat)
```

---

## Sezione 1 — UV Unwrap

### 1.1 Smart UV Project (raccomandato per oggetti complessi)
```python
import bpy

def smart_uv(obj_name, angle_limit=66, margin=0.02):
    ob = bpy.data.objects[obj_name]
    bpy.context.view_layer.objects.active = ob
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.uv.smart_project(
        angle_limit   = angle_limit,
        island_margin = margin,
        scale_to_bounds = True
    )
    bpy.ops.object.mode_set(mode='OBJECT')
    uv = ob.data.uv_layers.active
    return uv.name if uv else None
```

### 1.2 Angle-Based Unwrap (superfici organiche)
```python
def unwrap_angle(obj_name, margin=0.02):
    ob = bpy.data.objects[obj_name]
    bpy.context.view_layer.objects.active = ob
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='SELECT')
    # Mark seam automatico prima di unwrap
    bpy.ops.mesh.mark_seam(clear=True)  # rimuovi seam precedenti
    bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=margin)
    bpy.ops.object.mode_set(mode='OBJECT')
```

### 1.3 Cube Projection (muri, pavimenti, superfici piane)
```python
def cube_project(obj_name, cube_size=1.0):
    ob = bpy.data.objects[obj_name]
    bpy.context.view_layer.objects.active = ob
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.uv.cube_project(cube_size=cube_size)
    bpy.ops.object.mode_set(mode='OBJECT')
```

### 1.4 UV Layer aggiuntivo (baking separato)
```python
def add_uv_layer(obj_name, name="UVBake"):
    ob = bpy.data.objects[obj_name]
    ob.data.uv_layers.new(name=name)
    ob.data.uv_layers.active = ob.data.uv_layers[name]
    # Fai unwrap con Smart Project su questo layer
    smart_uv(obj_name)
```

---

## Sezione 2 — Materiali Procedurali

### TABELLA VALORI DI RIFERIMENTO — Materiali Comuni

> **Regola:** Metallic è sempre 0 o 1 — nessun valore intermedio (le cose sono o metallo o non-metallo).
> **Regola:** Mai Base Color = (0,0,0) puro. Anche i materiali più scuri riflettono qualcosa. Minimo: 0.02–0.03.

| Materiale | Base Color (lineare) | Roughness | Metallic | Specular IOR | Note |
|-----------|---------------------|-----------|----------|--------------|------|
| Plastica matte nera | (0.025, 0.025, 0.030) | 0.50–0.65 | 0 | 0.25 | + noise su roughness |
| Plastica semi-gloss | (0.03, 0.03, 0.04) | 0.35–0.50 | 0 | 0.30 | consumer electronics |
| Vetro screen (spento) | (0.005, 0.006, 0.012) | 0.01–0.03 | 0 | 0.50 | quasi mirror |
| Porcellana bianca | (0.955, 0.940, 0.896) | 0.08–0.12 | 0 | 0.55 | coat 0.2 |
| Legno chiaro | (0.55, 0.38, 0.22) | 0.65–0.80 | 0 | 0.10 | + grain texture |
| Legno scuro | (0.12, 0.07, 0.04) | 0.70–0.85 | 0 | 0.10 | + grain texture |
| Metallo lucido | (0.95, 0.95, 0.95) | 0.05–0.15 | 1 | — | alluminio, acciaio |
| Metallo spazzolato | (0.80, 0.80, 0.80) | 0.30–0.45 | 1 | — | + anisotropy |
| Oro | (1.00, 0.78, 0.28) | 0.10–0.25 | 1 | — | colore giallo caldo |
| Gomma | (0.02, 0.02, 0.02) | 0.90–1.00 | 0 | 0.05 | molto matte |
| Vetro trasparente | (0.92, 0.95, 0.98) | 0.00–0.05 | 0 | 0.50 | transmission=1, IOR=1.52 |
| Carta | (0.85, 0.83, 0.78) | 0.90–1.00 | 0 | 0.05 | no specular |
| Tessuto cotone | (base a scelta) | 0.85–0.95 | 0 | 0.05 | + fuzz/sheen |
| Pelle umana | (0.77, 0.52, 0.40) | 0.60–0.75 | 0 | 0.35 | SSS obbligatorio |

---

### Pattern noise-roughness — Anti "CG-Clean Look"

La roughness uniforme è il segnale più riconoscibile del "CG pulito". Aggiungere micro-variazione con Noise Texture rompe l'uniformità sintetica e rende qualsiasi superficie più reale.

```python
def add_roughness_noise(nodes, links, bsdf,
                        base_roughness=0.50,
                        variation=0.08,
                        scale=80.0,
                        detail=4.0):
    """
    Aggiunge micro-variazione di roughness tramite Noise → ColorRamp → Roughness.

    base_roughness: valore centrale (es. 0.50 per plastica matte)
    variation:      ±variazione attorno alla base (0.05–0.10 è sottile, 0.15+ è visibile)
    scale:          frequenza spaziale del noise (60–120 per micro-texture, 5–20 per macro)
    detail:         ottave del noise (2–4 per plastica, 6–8 per roccia)

    Esempi:
      Plastica matte:    base=0.50, variation=0.08, scale=80
      Plastica semi-gloss: base=0.40, variation=0.05, scale=100
      Legno:             base=0.70, variation=0.15, scale=20, detail=6
      Metallo spazzolato: base=0.35, variation=0.10, scale=60
    """
    noise = nodes.new("ShaderNodeTexNoise")
    noise.inputs["Scale"].default_value    = scale
    noise.inputs["Detail"].default_value   = detail
    noise.inputs["Roughness"].default_value = 0.6
    noise.inputs["Distortion"].default_value = 0.0
    noise.location = (-400, -200)

    ramp = nodes.new("ShaderNodeValToRGB")
    lo = max(0.0, base_roughness - variation)
    hi = min(1.0, base_roughness + variation)
    ramp.color_ramp.elements[0].position = 0.35
    ramp.color_ramp.elements[0].color    = (lo, lo, lo, 1.0)
    ramp.color_ramp.elements[1].position = 0.65
    ramp.color_ramp.elements[1].color    = (hi, hi, hi, 1.0)
    ramp.location = (-200, -200)

    links.new(noise.outputs["Fac"], ramp.inputs["Fac"])
    links.new(ramp.outputs["Color"], bsdf.inputs["Roughness"])
    return noise, ramp

# Uso rapido su un materiale esistente:
def apply_roughness_noise_to(mat, base_roughness=0.50, variation=0.08, scale=80.0):
    """Aggiunge noise-roughness a un materiale già esistente."""
    nt = mat.node_tree
    bsdf = next((n for n in nt.nodes if n.type == 'BSDF_PRINCIPLED'), None)
    if bsdf:
        add_roughness_noise(nt.nodes, nt.links, bsdf,
                            base_roughness, variation, scale)
```

---

### Pattern base: Principled BSDF
```python
mat, nodes, links = new_mat("MyMat")

out  = nodes.new("ShaderNodeOutputMaterial"); out.location = (600, 0)
bsdf = nodes.new("ShaderNodeBsdfPrincipled"); bsdf.location = (200, 0)
links.new(bsdf.outputs["BSDF"], out.inputs["Surface"])

bsdf.inputs["Base Color"].default_value  = (0.8, 0.3, 0.1, 1.0)
bsdf.inputs["Roughness"].default_value   = 0.6
bsdf.inputs["Metallic"].default_value    = 0.0
set_input(bsdf, "Specular IOR Level", "Specular", value=0.5)

# Per plastica: aggiungi subito noise-roughness
add_roughness_noise(nodes, links, bsdf, base_roughness=0.5, variation=0.08)
```

### 2.1 mat_porcelain — Porcellana / Ceramica
```python
def mat_porcelain(name="Porcelain", color=(0.955, 0.940, 0.896)):
    mat, nodes, links = new_mat(name)
    out  = nodes.new("ShaderNodeOutputMaterial"); out.location = (600, 0)
    bsdf = nodes.new("ShaderNodeBsdfPrincipled");  bsdf.location = (200, 0)
    links.new(bsdf.outputs["BSDF"], out.inputs["Surface"])

    # Micro-variazione noise (2%) via ShaderNodeMix RGBA
    noise = nodes.new("ShaderNodeTexNoise"); noise.location = (-400, 100)
    noise.inputs["Scale"].default_value    = 12.0
    noise.inputs["Detail"].default_value   = 2.0
    noise.inputs["Roughness"].default_value= 0.5

    mix = nodes.new("ShaderNodeMix"); mix.data_type = "RGBA"
    mix.location = (-100, 100)
    mix.inputs[6].default_value = (*[c * 0.97 for c in color], 1.0)  # scuro
    mix.inputs[7].default_value = (*[c * 1.01 for c in color], 1.0)  # chiaro
    links.new(noise.outputs["Fac"], mix.inputs[0])
    links.new(mix.outputs[2], bsdf.inputs["Base Color"])

    bsdf.inputs["Roughness"].default_value = 0.08
    set_input(bsdf, "IOR", value=1.52)
    set_input(bsdf, "Coat Weight", "Clearcoat", value=0.2)
    set_input(bsdf, "Coat Roughness", "Clearcoat Roughness", value=0.05)
    set_input(bsdf, "Specular IOR Level", "Specular", value=0.55)
    return mat

# NOTA ShaderNodeMix RGBA:
#   inputs[0]  = Factor (Fac)
#   inputs[6]  = Color A  (NON inputs[1])
#   inputs[7]  = Color B  (NON inputs[2])
#   outputs[2] = risultato Color  (NON outputs[0])
# ShaderNodeMixRGB è DEPRECATO in Blender 4.x/5.x — non usare.
```

### 2.2 mat_organic — Materiale organico con SSS + Noise + Fresnel
```python
def mat_organic(name="Organic",
                base_color=(0.65, 0.05, 0.05),
                roughness_range=(0.50, 0.72),
                sss=0.18, sss_radius=(0.92, 0.10, 0.10),
                bump_strength=0.35, bump_dist=0.008,
                glossy_ior=1.38, glossy_rough=0.06):
    """
    Stack: Noise→ColorRamp→Principled + Noise→Bump + Glossy→Fresnel→Mix
    Usato per: muscoli, frutta, skin, radici, tessuti biologici.
    """
    mat, nodes, links = new_mat(name)

    # Coordinate
    tc  = nodes.new("ShaderNodeTexCoord"); tc.location = (-1100, 0)
    mp  = nodes.new("ShaderNodeMapping");  mp.location = (-900, 0)
    mp.inputs["Scale"].default_value = (1.2, 1.2, 1.0)
    links.new(tc.outputs["Object"], mp.inputs["Vector"])

    # Noise colore
    nc = nodes.new("ShaderNodeTexNoise"); nc.location = (-680, 250)
    nc.inputs["Scale"].default_value     = 6.0
    nc.inputs["Detail"].default_value    = 5.0
    nc.inputs["Roughness"].default_value = 0.65
    nc.inputs["Distortion"].default_value= 0.25
    links.new(mp.outputs["Vector"], nc.inputs["Vector"])

    ramp = nodes.new("ShaderNodeValToRGB"); ramp.location = (-430, 250)
    try:    ramp.color_ramp.color_space = "RGB"
    except: ramp.color_ramp.color_mode  = "RGB"   # Blender 5.x rinomina
    c = base_color
    ramp.color_ramp.elements[0].color = (c[0]*0.55, c[1]*0.55, c[2]*0.55, 1.0)
    ramp.color_ramp.elements[1].color = (*c, 1.0)
    links.new(nc.outputs["Fac"], ramp.inputs["Fac"])

    # Noise rugosità
    nr = nodes.new("ShaderNodeTexNoise"); nr.location = (-680, -50)
    nr.inputs["Scale"].default_value = 18.0
    links.new(mp.outputs["Vector"], nr.inputs["Vector"])
    rr = nodes.new("ShaderNodeValToRGB"); rr.location = (-430, -50)
    rr.color_ramp.elements[0].color = (*[roughness_range[0]]*3, 1.0)
    rr.color_ramp.elements[1].color = (*[roughness_range[1]]*3, 1.0)
    links.new(nr.outputs["Fac"], rr.inputs["Fac"])

    # Bump
    nb = nodes.new("ShaderNodeTexNoise"); nb.location = (-680, -350)
    nb.inputs["Scale"].default_value     = 22.0
    nb.inputs["Detail"].default_value    = 8.0
    nb.inputs["Roughness"].default_value = 0.55
    links.new(mp.outputs["Vector"], nb.inputs["Vector"])
    bump = nodes.new("ShaderNodeBump"); bump.location = (-180, -300)
    bump.inputs["Strength"].default_value = bump_strength
    bump.inputs["Distance"].default_value = bump_dist
    links.new(nb.outputs["Fac"], bump.inputs["Height"])

    # Principled BSDF
    bsdf = nodes.new("ShaderNodeBsdfPrincipled"); bsdf.location = (80, 120)
    links.new(ramp.outputs["Color"], bsdf.inputs["Base Color"])
    links.new(rr.outputs["Color"],   bsdf.inputs["Roughness"])
    links.new(bump.outputs["Normal"],bsdf.inputs["Normal"])
    set_input(bsdf, "Subsurface Weight", "Subsurface", value=sss)
    try:    bsdf.inputs["Subsurface Radius"].default_value = sss_radius
    except: pass
    set_input(bsdf, "Specular IOR Level", "Specular", value=0.28)

    # Glossy (strato umido)
    glossy = nodes.new("ShaderNodeBsdfGlossy"); glossy.location = (80, -200)
    glossy.inputs["Roughness"].default_value = glossy_rough
    links.new(bump.outputs["Normal"], glossy.inputs["Normal"])

    # Fresnel → Mix
    fresnel = nodes.new("ShaderNodeFresnel"); fresnel.location = (80, -380)
    fresnel.inputs["IOR"].default_value = glossy_ior
    mix = nodes.new("ShaderNodeMixShader"); mix.location = (380, 0)
    links.new(fresnel.outputs["Fac"],   mix.inputs["Fac"])
    links.new(bsdf.outputs["BSDF"],     mix.inputs[1])
    links.new(glossy.outputs["BSDF"],   mix.inputs[2])

    out = nodes.new("ShaderNodeOutputMaterial"); out.location = (620, 0)
    links.new(mix.outputs["Shader"], out.inputs["Surface"])
    return mat
```

### 2.3 mat_metal — Metallo PBR
```python
def mat_metal(name="Metal", color=(0.8, 0.8, 0.85), roughness=0.15):
    mat, nodes, links = new_mat(name)
    out  = nodes.new("ShaderNodeOutputMaterial"); out.location = (400, 0)
    bsdf = nodes.new("ShaderNodeBsdfPrincipled");  bsdf.location = (0, 0)
    links.new(bsdf.outputs["BSDF"], out.inputs["Surface"])

    bsdf.inputs["Base Color"].default_value  = (*color, 1.0)
    bsdf.inputs["Metallic"].default_value    = 1.0
    bsdf.inputs["Roughness"].default_value   = roughness
    set_input(bsdf, "Specular IOR Level", "Specular", value=0.5)

    # Anisotropia leggera per look brushed
    if "Anisotropic" in bsdf.inputs:
        bsdf.inputs["Anisotropic"].default_value = 0.3
    return mat
```

### 2.4 mat_glass — Vetro
```python
def mat_glass(name="Glass", color=(0.95, 0.98, 1.0), roughness=0.02, ior=1.45):
    mat, nodes, links = new_mat(name)
    mat.blend_method = "BLEND"  # Eevee transparency
    out  = nodes.new("ShaderNodeOutputMaterial"); out.location = (400, 0)
    bsdf = nodes.new("ShaderNodeBsdfPrincipled");  bsdf.location = (0, 0)
    links.new(bsdf.outputs["BSDF"], out.inputs["Surface"])

    bsdf.inputs["Base Color"].default_value  = (*color, 1.0)
    bsdf.inputs["Roughness"].default_value   = roughness
    set_input(bsdf, "IOR", value=ior)
    set_input(bsdf, "Transmission Weight", "Transmission", value=1.0)
    return mat
```

### 2.5 mat_leather — Cuoio / Pelle
```python
def mat_leather(name="Leather", color=(0.25, 0.10, 0.05), roughness=0.75):
    mat, nodes, links = new_mat(name)
    out  = nodes.new("ShaderNodeOutputMaterial"); out.location = (600, 0)
    bsdf = nodes.new("ShaderNodeBsdfPrincipled");  bsdf.location = (200, 0)
    links.new(bsdf.outputs["BSDF"], out.inputs["Surface"])

    # Voronoi per pori del cuoio
    vc = nodes.new("ShaderNodeTexVoronoi"); vc.location = (-500, 200)
    vc.inputs["Scale"].default_value    = 80.0
    vc.inputs["Randomness"].default_value = 0.8
    noise = nodes.new("ShaderNodeTexNoise"); noise.location = (-500, -100)
    noise.inputs["Scale"].default_value = 12.0
    noise.inputs["Detail"].default_value = 6.0

    # Mix Voronoi + Noise per bump complesso
    mix_h = nodes.new("ShaderNodeMix"); mix_h.data_type = "FLOAT"
    mix_h.location = (-200, 100); mix_h.inputs[0].default_value = 0.4
    links.new(vc.outputs["Distance"],   mix_h.inputs[4])  # A (float)
    links.new(noise.outputs["Fac"],     mix_h.inputs[5])  # B (float)

    bump = nodes.new("ShaderNodeBump"); bump.location = (-20, 0)
    bump.inputs["Strength"].default_value = 0.5
    bump.inputs["Distance"].default_value = 0.003
    links.new(mix_h.outputs[1], bump.inputs["Height"])  # output[1] = float

    # Colore con leggera variazione noise
    cr = nodes.new("ShaderNodeValToRGB"); cr.location = (-200, -200)
    cr.color_ramp.elements[0].color = (*[c*0.7 for c in c

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [MAX-786](https://github.com/MAX-786)
- **Source:** [MAX-786/claude-3d-harness](https://github.com/MAX-786/claude-3d-harness)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-max-786-claude-3d-harness-blender-texture
- Seller: https://agentstack.voostack.com/s/max-786
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
