Install
$ agentstack add skill-max-786-claude-3d-harness-blender-texture ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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):
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)
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)
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)
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)
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)
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.
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
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
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
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
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
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
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.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.