Skill di sculpting procedurale per Blender. Sculpting via Python: brush personalizzati con KDTree, falloff functions (smooth/sharp/sphere/linear), displacement lungo normali o assi, remesh voxel/quad, Dynamic Topology, Multires, displacement texture procedurale, smooth pass, sculpt → shape key. Usa questa skill quando: la forma deve essere organica/irregolare, ha dettagli di superficie (pori, rughe, dents, rilievi), o parte da una primitiva che va deformata artisticamente invece che costruita...
Scanned 9/22/2026
npx -y skills add MAX-786/claude-3d-harness --skill blender-sculpt --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Blender Sculpt?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/max-786-blender-sculpt)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
description: >
Skill di sculpting procedurale per Blender. Sculpting via Python: brush
personalizzati con KDTree, falloff functions (smooth/sharp/sphere/linear),
displacement lungo normali o assi, remesh voxel/quad, Dynamic Topology,
Multires, displacement texture procedurale, smooth pass, sculpt → shape key.
Usa questa skill quando: la forma deve essere organica/irregolare, ha dettagli
di superficie (pori, rughe, dents, rilievi), o parte da una primitiva che
va deformata artisticamente invece che costruita pezzo per pezzo.
allowed-tools:
- Bash
- Read
- Write
- mcp__Blender__execute_blender_code
- mcp__Blender__get_screenshot_of_window_as_image
- mcp__Blender__render_viewport_to_path
- mcp__Blender__get_objects_summary
- mcp__Blender__get_object_detail_summary
---
# Skill: Blender Sculpting (Procedurale)
Sei un esperto di sculpting 3D in Blender via Python.
Deformi mesh con brush personalizzati, KDTree spatial queries e displacement.
---
## Connessione — MCP (predefinito)
```python
mcp__Blender__execute_blender_code(code="""
import bpy, bmesh, math
from mathutils import Vector
from mathutils.kdtree import KDTree
# ... codice ...
result = {"ok": True}
""")
mcp__Blender__get_screenshot_of_window_as_image()
mcp__Blender__render_viewport_to_path(output_path="<JOB_DIR>/sculpt.png")
```
---
## Visual Loop
```
1. execute_blender_code(mesh_base_code) ← crea primitiva + remesh
2. execute_blender_code(sculpt_code) ← applica brush
3. get_screenshot_of_window_as_image() ← analisi rapida viewport
4. execute_blender_code(fix_code) ← raffina → itera
5. render_viewport_to_path("final.png") ← render finale
```
---
## IL PRINCIPIO — Sculpting Procedurale vs. Sculpt Mode
Blender ha due approcci:
| | Sculpt Mode (operatori) | Sculpting Procedurale (Python) |
|-|------------------------|-------------------------------|
| Controllo | Limitato da Claude | Totale — ogni vertice |
| Riproducibilità | No | Sì — deterministico |
| Falloff | Preset fissi | Personalizzabile |
| Spatial query | Manuale | KDTree O(log n) |
| Iterabilità | Difficile | Facile |
**Usa Sculpt Mode** solo per operazioni globali (remesh, smooth, dyntopo setup).
**Usa Python** per tutto il resto — brush, displacement, deformazioni specifiche.
---
## SETUP MESH — Prima di Sculpting
La mesh di partenza determina la qualità del risultato.
Regola: **più poligoni = più dettaglio**, ma con costo. Bilanciare sempre.
```python
import bpy, math
def sculpt_base_sphere(name="SculptObj", radius=0.12,
segments=64, rings=48):
"""
Sfera UV — punto di partenza ottimale per oggetti organici.
segments=64, rings=48 → ~6000 vertici, buon punto di partenza.
Dopo: applica remesh o subdivide per più dettaglio.
IMPORTANTE: usa shade_smooth() e origin_to_geometry subito.
"""
bpy.ops.mesh.primitive_uv_sphere_add(
radius=radius, segments=segments, ring_count=rings,
location=(0, 0, 0))
obj = bpy.context.active_object
obj.name = name
obj.data.shade_smooth()
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
result = {'verts': len(obj.data.vertices), 'name': obj.name}
return obj
def sculpt_base_cube(name="SculptBox", size=0.2, cuts=8):
"""
Cubo suddiviso — per sculpting di forme angolari/rocciose.
cuts=8 → ~4000 facce. Aumenta per più dettaglio.
"""
bpy.ops.mesh.primitive_cube_add(size=size, location=(0,0,0))
obj = bpy.context.active_object
obj.name = name
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.subdivide(number_cuts=cuts)
bpy.ops.object.mode_set(mode='OBJECT')
obj.data.shade_smooth()
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
return obj
```
---
## REMESH — Topologia uniforme pre-sculpt
Il remesh crea una topologia uniforme (tutti i poligoni della stessa dimensione)
— fondamentale per sculpting uniforme senza artefatti.
```python
def remesh_voxel(obj, voxel_size=0.005, adaptivity=0.0):
"""
Remesh voxelico — il più semplice e robusto.
Crea topologia quad uniforme da qualsiasi mesh.
voxel_size: dimensione del voxel [BU]
0.010 → ~2000 facce (preview rapido)
0.005 → ~8000 facce (sculpting base)
0.002 → ~50000 facce (sculpting dettagliato)
0.001 → ~200000 facce (sculpting fine — lento!)
adaptivity: 0=completamente uniforme, 0.5=adattivo (meno facce sulle piatte)
NOTA: distrugge UV, materiali per slot, vertex groups — fallo PRIMA
di aggiungere materiali o weight groups.
"""
bpy.context.view_layer.objects.active = obj
mod = obj.modifiers.new("Remesh", "REMESH")
mod.mode = 'VOXEL'
mod.voxel_size = voxel_size
mod.adaptivity = adaptivity
mod.use_smooth_shade = True
bpy.ops.object.modifier_apply(modifier="Remesh")
obj.data.shade_smooth()
return obj
def remesh_quad(obj, depth=6):
"""
Remesh Quad (Instant Meshes-style) — crea quad ordinati.
depth: 4=bassa (pochi poligoni), 6=media, 8=alta
Meno usato del voxel perché più lento e meno prevedibile.
"""
bpy.context.view_layer.objects.active = obj
mod = obj.modifiers.new("Remesh", "REMESH")
mod.mode = 'SHARP'
mod.octree_depth = depth
mod.use_smooth_shade = True
bpy.ops.object.modifier_apply(modifier="Remesh")
return obj
def subdivide_smooth(obj, levels=2):
"""
SubSurf applicato — alternativa al remesh per mesh già ben topologizzate.
Usato per aggiungere risoluzione a una mesh bmesh costruita a mano.
levels=2 → ×4 vertici, levels=3 → ×16 vertici
"""
mod = obj.modifiers.new("Subdivision", "SUBSURF")
mod.levels = levels
mod.render_levels = levels
mod.subdivision_type = 'CATMULL_CLARK'
bpy.ops.object.modifier_apply(modifier="Subdivision")
obj.data.shade_smooth()
return obj
```
---
## KDTREE — Spatial Query
Il KDTree è la struttura dati centrale per il sculpting procedurale.
Dato un punto nello spazio, trova in O(log n) tutti i vertici entro un raggio.
```python
from mathutils import Vector
from mathutils.kdtree import KDTree
def build_kd(obj):
"""
Costruisce un KDTree dai vertici di obj in world space.
IMPORTANTE: chiama bpy.context.view_layer.update() prima se l'oggetto
è stato modificato di recente (matrix_world potrebbe essere stale).
Ritorna: (kd, world_verts) — il KD-tree e la lista di posizioni world
"""
bpy.context.view_layer.update()
mw = obj.matrix_world
wv = [mw @ v.co for v in obj.data.vertices]
kd = KDTree(len(wv))
for i, v in enumerate(wv):
kd.insert(v, i)
kd.balance()
return kd, wv
# Uso:
# kd, wv = build_kd(obj)
# hits = kd.find_range(Vector((0, 0, 0.12)), 0.04)
# for world_pos, vertex_index, distance in hits:
# v = obj.data.vertices[vertex_index]
# # ... modifica v.co ...
```
---
## FALLOFF FUNCTIONS
Il falloff determina come l'influenza del brush decresce dalla distanza:
```python
def fo_smooth(d, r):
"""Smoothstep — transizione morbida. Default per la maggior parte dei brush."""
t = min(d / r, 1.0)
return 1.0 - (3*t*t - 2*t*t*t)
def fo_sharp(d, r):
"""Cubica inversa — picco acuto al centro, cade rapidamente. Per dents e dimples."""
t = min(d / r, 1.0)
return (1.0 - t) ** 3
def fo_sphere(d, r):
"""Semicircolare — deformazione a cupola. Per bump e rigonfiamenti."""
t = min(d / r, 1.0)
return math.sqrt(max(0.0, 1.0 - t*t))
def fo_linear(d, r):
"""Lineare — cono perfetto. Per creste e spine."""
return max(0.0, 1.0 - d / r)
def fo_constant(d, r):
"""Costante — tutto al massimo nel raggio, 0 fuori. Per tagli netti."""
return 1.0 if d < r else 0.0
FALLOFFS = {
'smooth': fo_smooth,
'sharp': fo_sharp,
'sphere': fo_sphere,
'linear': fo_linear,
'constant': fo_constant,
}
```
---
## BRUSH — Il motore centrale
```python
def brush(obj, center, radius, strength,
direction='normal', falloff='smooth',
mask_fn=None):
"""
Brush procedurale universale — la funzione più importante della skill.
center : Vector o tuple (x,y,z) in WORLD SPACE — centro del brush
radius : raggio di influenza [BU]
strength : intensità dello spostamento [BU] (positivo=fuori, negativo=dentro)
direction : come si spostano i vertici
'normal' → lungo la normale locale del vertice (inflate/deflate)
'z' → lungo l'asse Z world (flatten verso l'alto/basso)
'-z' → verso il basso
'smooth' → media con i vicini (smooth pass)
Vector((x,y,z)) → direzione arbitraria in world space
falloff : 'smooth' | 'sharp' | 'sphere' | 'linear' | 'constant'
mask_fn : funzione(vertex_co_world) → float [0,1]
usata per mascherare aree (es: solo la parte superiore)
None = nessuna maschera
Ritorna: numero di vertici modificati
ESEMPI:
# Dimple in cima (come mela, albicocca)
brush(obj, (0,0,0.12), radius=0.045, strength=-0.028,
direction='normal', falloff='sharp')
# Base appiattita
brush(obj, (0,0,-0.12), radius=0.055, strength=-0.018,
direction='z', falloff='sphere')
# Bump equatoriale
brush(obj, (0.12,0,0), radius=0.07, strength=0.008,
direction='normal', falloff='smooth')
# Ruga (cresta lineare)
brush(obj, (0,0,0.05), radius=0.02, strength=0.005,
direction='normal', falloff='sharp')
"""
mw = obj.matrix_world
mwi = mw.inverted()
nm = mwi.transposed().to_3x3() # per trasformare le normali
fn = FALLOFFS.get(falloff, fo_smooth)
kd, _ = build_kd(obj)
center_v = Vector(center)
hits = kd.find_range(center_v, radius)
if not hits:
return 0
for world_pos, vi, dist in hits:
v = obj.data.vertices[vi]
w = fn(dist, radius)
# Maschera opzionale
if mask_fn is not None:
w *= mask_fn(world_pos)
if w < 1e-4:
continue
# Calcola direzione di spostamento
if direction == 'normal':
d_world = (nm @ v.normal).normalized() * strength * w
elif direction == 'z':
d_world = Vector((0, 0, strength * w))
elif direction == '-z':
d_world = Vector((0, 0, -abs(strength) * w))
elif direction == 'smooth':
# Media con i vicini (smooth pass locale)
neighbors = [obj.data.vertices[e.other_vert(v).index].co
for e in v.link_edges]
if neighbors:
avg = sum(neighbors, Vector()) / len(neighbors)
d_local = (avg - v.co) * w * abs(strength) * 10
v.co += d_local
continue
else:
d_world = Vector(direction).normalized() * strength * w
# Trasforma da world a local e applica
v.co += mwi.to_3x3() @ d_world
obj.data.update()
return len(hits)
def smooth_pass(obj, center, radius, strength=0.5, iterations=1):
"""
Smooth pass localizzato — attenua rughe/artefatti in un'area.
Chiama più volte per smooth progressivo.
strength: 0=nessun effetto, 1=media completa con i vicini
"""
kd, _ = build_kd(obj)
center_v = Vector(center)
fn = fo_smooth
for _ in range(iterations):
hits = kd.find_range(center_v, radius)
displacements = {}
for _, vi, dist in hits:
v = obj.data.vertices[vi]
w = fn(dist, radius) * strength
neighbors = [obj.data.vertices[e.other_vert(v).index].co
for e in v.link_edges]
if neighbors:
avg = sum(neighbors, Vector()) / len(neighbors)
displacements[vi] = (avg - v.co) * w
for vi, delta in displacements.items():
obj.data.vertices[vi].co += delta
obj.data.update()
kd, _ = build_kd(obj) # rebuild dopo modifica
return len(hits) if hits else 0
def global_smooth(obj, strength=0.3, iterations=2):
"""
Smooth globale su tutta la mesh.
Utile dopo displacement texture o sculpt aggressivo.
"""
for _ in range(iterations):
for v in obj.data.vertices:
neighbors = [obj.data.vertices[e.other_vert(v).index].co
for e in v.link_edges]
if neighbors:
avg = sum(neighbors, Vector()) / len(neighbors)
v.co += (avg - v.co) * strength
obj.data.update()
```
---
## DISPLACEMENT TEXTURE — Dettaglio procedurale
```python
import math, random
def noise3d(x, y, z, seed=0, octaves=4, persistence=0.5, lacunarity=2.0):
"""
Noise 3D approssimato (puro Python, senza dipendenze).
Usa per displacement, rughe, variazioni di superficie.
octaves : 1=liscio, 4=medio, 8=molto dettagliato
persistence : quanto ogni ottava diminuisce (0.5=standard)
lacunarity : quanto ogni ottava aumenta di frequenza (2.0=standard)
Ritorna: float in circa [-1, 1]
"""
rng = random.Random(seed)
offsets = [(rng.uniform(-100,100), rng.uniform(-100,100),
rng.uniform(-100,100)) for _ in range(octaves)]
val = 0.0
amp = 1.0
freq = 1.0
max_val = 0.0
for ox, oy, oz in offsets:
# Lattice noise approssimato (hash di coordinate)
xi = int(math.floor((x + ox) * freq))
yi = int(math.floor((y + oy) * freq))
zi = int(math.floor((z + oz) * freq))
def h(a, b, c):
return ((a * 1619 + b * 31337 + c * 6971 + seed * 1013) & 0x7fffffff) / 0x7fffffff
# Trilinear interpolation
fx = (x + ox) * freq - math.floor((x + ox) * freq)
fy = (y + oy) * freq - math.floor((y + oy) * freq)
fz = (z + oz) * freq - math.floor((z + oz) * freq)
# Smoothstep
ux = fx*fx*(3-2*fx); uy = fy*fy*(3-2*fy); uz = fz*fz*(3-2*fz)
n = (h(xi,yi,zi)*(1-ux)*(1-uy)*(1-uz) + h(xi+1,yi,zi)*ux*(1-uy)*(1-uz) +
h(xi,yi+1,zi)*(1-ux)*uy*(1-uz) + h(xi+1,yi+1,zi)*ux*uy*(1-uz) +
h(xi,yi,zi+1)*(1-ux)*(1-uy)*uz + h(xi+1,yi,zi+1)*ux*(1-uy)*uz +
h(xi,yi+1,zi+1)*(1-ux)*uy*uz + h(xi+1,yi+1,zi+1)*ux*uy*uz)
val += (n * 2 - 1) * amp
max_val += amp
amp *= persistence
freq *= lacunarity
return val / max_val if max_val > 0 else 0.0
def displace_noise(obj, scale=8.0, strength=0.005, seed=42,
octaves=4, direction='normal'):
"""
Applica displacement noise su tutta la mesh.
scale : frequenza del noise (2=grossolano, 8=medio, 20=fine come pori)
strength : ampiezza del displacement [BU]
direction: 'normal' (lungo normali) o 'z' (solo verticale)
Usi tipici:
Pelle/frutta: scale=10, strength=0.003, octaves=5
Roccia: scale=4, strength=0.015, octaves=6
Legno: scale=8, strength=0.006, octaves=3
Tessuto: scale=20, strength=0.002, octaves=2
"""
mw = obj.matrix_world
mwi = mw.inverted()
nm = mwi.transposed().to_3x3()
for v in obj.data.vertices:
wp = mw @ v.co
n = noise3d(wp.x * scale, wp.y * scale, wp.z * scale,
seed=seed, octaves=octaves)
if direction == 'normal':
d_world = (nm @ v.normal).normalized() * n * strength
else: # 'z'
d_world = Vector((0, 0, n * strength))
v.co += mwi.to_3x3() @ d_world
obj.data.update()
def displace_wave(obj, axis='z', wavelength=0.05, amplitude=0.003,
phase=0.0):
"""
Displacement ondulatorio periodico — per rughe parallele, squame, tessuto.
axis : 'x', 'y', 'z' — direzione delle onde
wavelength : distanza tra due creste [BU]
amplitude : altezza delle creste [BU]
Usi tipici:
Rughe su fronte: axis='z', wavelength=0.012, amplitude=0.002
Squame di pesce: axis='x', wavelength=0.008, amplitude=0.004
Trama tessuto: axis='y', wavelength=0.005, amplitude=0.001
"""
mw = obj.matrix_world
mwi = mw.inverted()
nm = mwi.transposed().to_3x3()
ax = {'x': 0, 'y': 1, 'z': 2}[axis]
for v in obj.data.vertices:
wp = mw @ v.co
t = math.sin(wp[ax] * 2 * math.pi / wavelength + phase)
d_world = (nm @ v.normal).normalized() * t * amplitude
v.co += mwi.to_3x3() @ d_world
obj.data.update()
```
---
## DEFORMAZIONI GLOBALI — Schiacciamento, torsione, bend
```python
def flatten_axis(obj, axis='z', factor=0.85):
"""
Schiaccia l'oggetto lungo un asse (factor < 1 = più piatto).
Tipico: mela (z*0.88), arancio (z*0.92), disco (z*0.3)
"""
ax = {'x': 0, 'y': 1, 'z': 2}[axis]
for v in obj.data.vertices:
v.co[ax] *= factor
obj.data.update()
def pinch(obj, center, radius, strength=0.3, axis=None):
"""
Pinch: attrae i vertici verso il centro del brush.
Opposto di Inflate. Utile per: pupilla, bocca chiusa, orecchio elfico.
axis: None=pinch 3D, 'z'=pinch solo laterale (mantiene altezza)
"""
kd, _ = build_kd(obj)
mw = obj.matrix_world
mwi = mw.inverted()
center_w = Vector(center)
for _, vi, dist in kd.find_range(center_w, radius):
v = obj.data.vertices[vi]
wp = mw @ v.co
w = fo_smooth(dist, radius) * strength
direction = (center_w - wp)
if axis == 'z':
direction.z = 0
if direction.length > 1e-6:
v.co += mwi.to_3x3() @ (direction.normalized() * w * dist)
obj.data.update()
def crease(obj, start, end, width=0.01, depth=0.008, falloff='sharp'):
"""
Crea una ruga/piega lineare tra due punti.
start, end : Vector — estremi della piega in world space
width : larghezza della piega [BU]
depth : profondità [BU] (positivo=fuori, negativo=dentro)
Usi: rughe su viso, cuciture su cuoio, nervature su foglia
"""
mw = obj.matrix_world
mwi = mw.inverted()
nm = mwi.transposed().to_3x3()
fn = FALLOFFS.get(falloff, fo_sharp)
start_v = Vector(start)
end_v = Vector(end)
seg = end_v - start_v
seg_len = seg.length
if seg_len < 1e-8:
return
seg_n = seg / seg_len
for v in obj.data.vertices:
wp = mw @ v.co
# Distanza dal punto di proiezione sul segmento
t = max(0.0, min(1.0, (wp - start_v).dot(seg_n) / seg_len))
proj = start_v + seg_n * t * seg_len
dist = (wp - proj).length
w = fn(dist, width)
if w < 1e-4:
continue
d_world = (nm @ v.normal).normalized() * depth * w
v.co += mwi.to_3x3() @ d_world
obj.data.update()
```
---
## DYNAMIC TOPOLOGY — Dyntopo
Il Dyntopo suddivide/unisce gli edge durante lo sculpt per mantenere
la densità di poligoni uniforme. Utile solo con bpy.ops sculpt mode.
```python
def enable_dyntopo(obj, detail_size=12, method='RELATIVE', constant_detail=6):
"""
Abilita Dynamic Topology per sculpting adattivo.
detail_size : risoluzione relativa (più basso = più dettaglio)
method : 'RELATIVE' (adattivo alla vista) o 'CONSTANT' (fisso)
constant_detail : risoluzione per CONSTANT (facce/cm²)
NOTA: il Dyntopo funziona SOLO in Sculpt Mode.
Dopo, torna in Object Mode per sculpt procedurale Python.
"""
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='SCULPT')
if not bpy.context.scene.tool_settings.sculpt.use_dyntopo:
bpy.ops.sculpt.dynamic_topology_toggle()
sc = bpy.context.scene.tool_settings.sculpt
sc.detail_type_method = method
if method == 'RELATIVE':
sc.detail_size = detail_size
else:
sc.constant_detail_resolution = constant_detail
bpy.ops.object.mode_set(mode='OBJECT')
def apply_sculpt_smooth_brush(obj, strength=1.0, size=50):
"""
Applica uno smooth globale tramite Sculpt Mode operator.
Meno preciso del smooth procedurale ma più rapido su mesh grandi.
"""
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='SCULPT')
bpy.context.scene.tool_settings.sculpt.use_symmetry_x = False
# Nota: bpy.ops.sculpt.brush_stroke non è facilmente invocabile da Python
# Usa il smooth procedurale (global_smooth) invece
bpy.ops.object.mode_set(mode='OBJECT')
```
---
## SCULPT → SHAPE KEY
Per animare tra una forma sculpted e la forma originale.
```python
def sculpt_to_shape_key(obj, key_name, sculpt_fn, basis_name="Basis"):
"""
Esegue sculpt_fn e salva il risultato come Shape Key.
L'oggetto torna alla forma originale dopo.
sculpt_fn : callable(obj) — la funzione di sculpting da applicare
Esempio:
def add_smile(obj):
brush(obj, (0.04, -0.08, 0.02), 0.03, 0.005, 'normal', 'smooth')
brush(obj, (-0.04,-0.08, 0.02), 0.03, 0.005, 'normal', 'smooth')
sculpt_to_shape_key(face, "Smile", add_smile)
# Ora face ha due shape keys: Basis e Smile
"""
# Salva posizioni originali
original = [v.co.copy() for v in obj.data.vertices]
# Assicurati che esista Basis key
if not obj.data.shape_keys:
obj.shape_key_add(name=basis_name, from_mix=False)
# Applica sculting
sculpt_fn(obj)
# Salva come shape key
sk = obj.shape_key_add(name=key_name, from_mix=False)
for i, v in enumerate(obj.data.vertices):
sk.data[i].co = v.co.copy()
# Ripristina posizioni originali
for i, v in enumerate(obj.data.vertices):
v.co = original[i]
obj.data.update()
return sk
```
---
## PATTERN — Forme Organiche Comuni
### Mela / Pera / Agrumi
```python
def make_apple(name="Apple", radius=0.12):
"""Mela procedurale completa — testato e verificato visivamente."""
# Base
bpy.ops.mesh.primitive_uv_sphere_add(radius=radius, segments=64,
ring_count=48, location=(0,0,0))
obj = bpy.context.active_object; obj.name = name
obj.data.shade_smooth()
top_z = radius; bot_z = -radius
# Dimple (fossetta) in cima
brush(obj, (0, 0, top_z), radius*0.37, -radius*0.23, 'normal', 'sharp')
# Appiattimento base
brush(obj, (0, 0, bot_z), radius*0.46, -radius*0.15, 'z', 'sphere')
# Bump equatoriale (4 lobi)
for deg in [0, 90, 180, 270]:
a = math.radians(deg)
cx, cy = radius * math.cos(a), radius * math.sin(a)
brush(obj, (cx, cy, 0), radius*0.58, radius*0.067, 'normal', 'smooth')
# Schiacciamento verticale (mela più larga che alta)
flatten_axis(obj, 'z', 0.88)
# Safe place — base a z=0
safe_place(obj)
return obj
def make_pear(name="Pear", height=0.16):
"""Pera — base larga, parte alta stretta con dimple."""
bpy.ops.mesh.primitive_uv_sphere_add(radius=height*0.5, segments=64,
ring_count=48, location=(0,0,0))
obj = bpy.context.active_object; obj.name = name
obj.data.shade_smooth()
h = height
# Restringe la parte alta (pinch)
pinch(obj, (0,0, h*0.3), radius=h*0.35, strength=0.5, axis='z')
# Dimple top
brush(obj, (0, 0, h*0.5), h*0.12, -h*0.12, 'normal', 'sharp')
# Allarga parte bassa
brush(obj, (0, 0,-h*0.15), h*0.4, h*0.04, 'normal', 'smooth')
safe_place(obj)
return obj
```
### Roccia / Asteroid
```python
def make_rock(name="Rock", size=0.15, seed=42):
"""Roccia irregolare con noise multi-ottava."""
bpy.ops.mesh.primitive_ico_sphere_add(radius=size, subdivisions=4,
location=(0,0,0))
obj = bpy.context.active_object; obj.name = name
# Remesh per topologia uniforme
remesh_voxel(obj, voxel_size=size*0.06)
# Noise multi-scala (forma grossa + dettaglio fine)
displace_noise(obj, scale=3.0, strength=size*0.15, seed=seed, octaves=2)
displace_noise(obj, scale=8.0, strength=size*0.04, seed=seed+1, octaves=4)
displace_noise(obj, scale=20.0, strength=size*0.01, seed=seed+2, octaves=3)
# Smooth leggero per eliminare artefatti
global_smooth(obj, strength=0.1, iterations=2)
obj.data.shade_smooth()
safe_place(obj)
return obj
```
### Terreno procedurale
```python
def make_terrain(name="Terrain", size=2.0, resolution=80, height=0.3, seed=0):
"""
Piano terreno con heightmap procedurale.
resolution : n. vertici per lato (80→6400 vertici, 120→14400)
height : altezza massima del terreno [BU]
"""
bpy.ops.mesh.primitive_grid_add(x_subdivisions=resolution,
y_subdivisions=resolution, size=size, location=(0,0,0))
obj = bpy.context.active_object; obj.name = name
mw = obj.matrix_world
for v in obj.data.vertices:
wp = mw @ v.co
# Noise multi-ottava per terreno realistico
h = (noise3d(wp.x*2, wp.y*2, 0, seed, octaves=6, persistence=0.55) + 1) * 0.5
v.co.z = h * height
obj.data.update()
obj.data.shade_smooth()
return obj
```
---
## SAFE PLACE — Posizionamento dopo sculpt
Dopo il sculpting la mesh può essere centrata male.
`safe_place` corregge automaticamente usando world bounds.
```python
def safe_place(obj, x=0.0, y=0.0, z=0.0, anchor='bottom'):
"""
Posiziona l'oggetto nel world space dopo sculting.
Usa sempre questa — mai obj.location = (x,y,z) direttamente.
anchor: 'bottom' → il punto più basso dell'oggetto va a z
'center' → il centro geometrico va a (x,y,z)
'top' → il punto più alto va a z
NOTA: necessita view_layer.update() per matrix_world corretta.
"""
bpy.context.view_layer.update()
mw = obj.matrix_world
wv = [mw @ v.co for v in obj.data.vertices]
xs = [v.x for v in wv]; ys = [v.y for v in wv]; zs = [v.z for v in wv]
cx = (min(xs)+max(xs))/2
cy = (min(ys)+max(ys))/2
cz = (min(zs)+max(zs))/2
# Offset per correggere il centro geometrico
ox = obj.location.x - cx
oy = obj.location.y - cy
oz = obj.location.z - cz
if anchor == 'bottom':
bfc = cz - min(zs) # distanza dal centro al fondo
obj.location = (x + ox, y + oy, z + oz + bfc)
elif anchor == 'top':
bfc = max(zs) - cz
obj.location = (x + ox, y + oy, z + oz - bfc)
else: # center
obj.location = (x + ox, y + oy, z + oz)
bpy.context.view_layer.update()
```
---
## MATERIALE POST-SCULPT — SSS + noise per organici
```python
def mat_organic(name, base_color, sss_color=None, roughness=0.55,
noise_scale=8.0, noise_strength=0.08, coat=0.0):
"""
Materiale per oggetti organici sculpted: SSS + variazione noise + coat.
base_color : tuple RGB [0-1] — colore principale
sss_color : colore SSS (None = variante più rossa di base_color)
noise_scale : frequenza variazione colore superficiale
noise_strength: intensità variazione colore [0-1]
coat : coat lucido [0-1] (0.5=mela, 0=pelle opaca)
Esempi:
mat_organic("Apple", (0.72, 0.04, 0.02), coat=0.55)
mat_organic("Skin", (0.84, 0.61, 0.50), roughness=0.65)
mat_organic("Orange", (0.95, 0.45, 0.05), noise_scale=12)
"""
m = bpy.data.materials.get(name) or bpy.data.materials.new(name)
m.use_nodes = True
tree = m.node_tree; tree.nodes.clear()
out = tree.nodes.new('ShaderNodeOutputMaterial')
bsdf = tree.nodes.new('ShaderNodeBsdfPrincipled')
tree.links.new(bsdf.outputs['BSDF'], out.inputs['Surface'])
inp = [i.name for i in bsdf.inputs]
# Noise texture → colore
noise = tree.nodes.new('ShaderNodeTexNoise')
noise.inputs['Scale'].default_value = noise_scale
noise.inputs['Detail'].default_value = 5.0
noise.inputs['Roughness'].default_value = 0.6
# Mix: colore base + variazione noise
# Blender 5.x: usa ShaderNodeMix con data_type='RGBA' (NON ShaderNodeMixRGB!)
mix = tree.nodes.new('ShaderNodeMix')
mix.data_type = 'RGBA'
mix.blend_type = 'MIX'
mix.inputs[6].default_value = (*base_color, 1.0) # Color A = base
# Color B = variante leggermente diversa
c2 = tuple(min(1, c * (1 + noise_strength)) for c in base_color)
mix.inputs[7].default_value = (*c2, 1.0) # Color B = variante
tree.links.new(noise.outputs['Fac'], mix.inputs[0])
tree.links.new(mix.outputs[2], bsdf.inputs['Base Color'])
bsdf.inputs['Roughness'].default_value = roughness
# SSS
sss_c = sss_color or tuple(min(1, c * 0.7) for c in base_color)
for sn in ['Subsurface Weight', 'Subsurface']:
if sn in inp:
bsdf.inputs[sn].default_value = 0.15; break
if 'Subsurface Radius' in inp:
bsdf.inputs['Subsurface Radius'].default_value = (1.0, 0.2, 0.1)
if 'Subsurface Scale' in inp:
bsdf.inputs['Subsurface Scale'].default_value = 0.008
# Coat (lucido superficiale per frutta, pelle tesa)
if coat > 0:
for cn in ['Coat Weight', 'Clearcoat']:
if cn in inp:
bsdf.inputs[cn].default_value = coat; break
for cr in ['Coat Roughness', 'Clearcoat Roughness']:
if cr in inp:
bsdf.inputs[cr].default_value = 0.08; break
return m
```
---
## REGOLE QUALITÀ SCULPTING
1. **Remesh prima dello sculpt** — mai sculpting su mesh con topologia irregolare.
`remesh_voxel(obj, voxel_size)` dà topologia uniforme → brush uniformi.
2. **Ordine brush**: forma globale → forma media → dettaglio fine.
Non iniziare con il dettaglio (rughe) prima di aver definito la forma (schiacciamento, dimple).
3. **`build_kd()` ad ogni iterazione** se la mesh è stata modificata.
Il KDTree usa le posizioni al momento della costruzione — se i vertici si sono mossi
le query spaziali tornano risultati errati.
4. **`safe_place()` dopo ogni sessione di sculpt** — mai leggere `obj.location`
per determinare la posizione dei vertici. Usa sempre world bounds.
5. **`view_layer.update()`** prima di qualsiasi calcolo con `matrix_world`.
6. **Noise displacement DOPO smooth** — il pattern è:
`forma base → smooth leggero → noise fine → smooth leggerissimo finale`.
7. **ShaderNodeMixRGB è deprecato** in Blender 5.x → usa `ShaderNodeMix`
con `data_type='RGBA'`, Color A = `inputs[6]`, Color B = `inputs[7]`, output = `outputs[2]`.
8. **SSS richiede Cycles** per risultati fotorealistici. EEVEE Next supporta SSS
ma meno preciso. Per preview usa EEVEE, per finale usa Cycles.
---
## ANALISI RICHIESTA
| Keyword | Tecnica |
|---------|---------|
| `mela / pera / frutto` | make_apple / make_pear + mat_organic |
| `roccia / asteroide / pietra` | make_rock + displace_noise multi-scala |
| `terreno / paesaggio` | make_terrain + noise3d heightmap |
| `ruga / piega / cuciture` | crease() + displace_wave |
| `dimple / fossetta / ombelico` | brush(..., 'sharp', negativo) |
| `pelle / organico / SSS` | displace_noise fine + mat_organic |
| `smooth / leviga / artefatti` | smooth_pass / global_smooth |
| `animabile / morphing` | sculpt_to_shape_key |
| `topologia uniforme` | remesh_voxel prima |
**Se richiesta ambigua → chiedi: "L'oggetto ha una forma base riconoscibile
(sfera, cilindro, cubo)? Ha dettagli di superficie (rughe, pori, bump)?
Deve essere animato?"**
## Output
- Codice Python completo, nessun placeholder
- Sempre: remesh → brush dalla forma grossa al dettaglio → smooth finale
- Usa mat_organic per oggetti biologici (SSS + noise + coat)
- Dopo esecuzione: get_screenshot → analisi visiva → itera brush
- safe_place() sempre alla fine prima del render
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!