关于 blender-materials
blender-materials 是用于通过 Python 为 Blender 对象分配基于物理的材质的技能。它以 Principled BSDF 为核心,提供金属、玻璃、塑料、布料、皮肤和自发光材质的配方。
主要功能
- 01Principled BSDF 材质配方
- 02金属与非金属材质设置指引
- 03透明和彩色玻璃流程
- 04塑料、布料和皮肤配方
- 05用于光源的自发光材质
适用场景
- 01应用拉丝钢或抛光金属
- 02制作透明或彩色玻璃
- 03设置塑料、天鹅绒或硅胶材质
- 04创建发光灯泡或标牌
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ roble3/ cc-blender-skill --skill blender-materials安装后在 Claude Code 中输入/blender-materials确认已加载。
附带文件evals/evals.jsonreferences/overview.md
详细介绍
Blender 材质
为物体应用基于物理的材质。仅使用 Principled BSDF — 这是唯一能干净导出到 glTF、且符合其他 DCC 工具预期的着色器。
金属度开关 — 切勿设为中间值
最重要的规则:金属度是开关,不是滑块。 将其设为 0.0(电介质:塑料、木材、玻璃、皮肤)或 1.0(金属:钢、金、铜)。介于 0.2 和 0.8 之间的值几乎总是错误的;它们会产生不符合能量守恒的渲染结果,看起来“像塑料”。
例外:深色镜面镜片(太阳镜)使用约 0.8 的值,以兼顾强反射与轻微色调 — 这是风格选择,而非严格的 PBR 做法。
决策树
What is it made of?
├── Raw metal (steel, gold, copper, etc.)
│ → Metallic=1.0, Base Color = F0 reflectance from physicallybased.info
│ → Roughness controls polish (0.05 mirror → 0.4 brushed → 0.7+ weathered)
│
├── Glass / clear / refractive
│ → Metallic=0, Transmission=1.0, IOR=1.5 (glass), Roughness=0.0
│ → Add Volume Absorption for thick tinted glass
│
├── Plastic / wood / stone (dielectric, opaque)
│ → Metallic=0, IOR=1.45 (plastic) or 1.5 (most others)
│ → Roughness per finish (0.15 glossy / 0.6 matte)
│ → Coat Weight 0.5+ for varnished/lacquered surfaces
│
├── Skin / wax / marble (subsurface scattering)
│ → Metallic=0, Subsurface Weight=1.0
│ → Subsurface Radius RGB tuned per material (skin: red scatters deepest)
│
├── Cloth / fabric (sheen)
│ → Metallic=0, Sheen Weight 0.2-0.5
│ → Roughness 0.6+, Sheen Roughness 0.5
│
└── Mirror / chrome (special metal)
→ Metallic=1.0, Roughness=0.02-0.05, near-white base
参考外观交接
如果目标是匹配原始图像/参考图像,而非制作通常意义上美观的渲染,请链式加载 reference-look-calibration。它负责测量色相/饱和度/明度、物体范围、辉光/光晕颜色,以及前后外观指标。随后,本技能应在经过校准的目标范围内应用所需的材质/灯光/渲染更改。
配方(必知的 12 种)
每种配方都会创建材质并将其分配给目标物体。将 'GEO-target' 替换为实际的物体名称。
set_input helper — 部分 Blender 5.x BSDF 输入必需
在 Blender 5.x 的 Principled BSDF v2 中,数据 API 将两个输入标记为 enabled=False:Weight 和 Subsurface IOR。这些输入可以通过迭代或索引访问,但不能通过字符串键查找 — 即使输入确实存在,bsdf.inputs['Subsurface IOR'] 也会引发 KeyError,尽管渲染时仍会采用其值。这是 Blender 5.x 的一个特性问题,在 v0.4.0 → v0.5.0 验证期间发现。
当配方需要设置可能处于“已禁用但仍有效”状态的输入时,请使用此 helper。它适用于所有输入(无论是否启用),并且具备前向兼容性,即使未来的 Blender 版本禁用了更多输入也能正常工作:
def set_input(node, name, value):
"""Set a node input by name. Works on inputs with enabled=False
that fail string-key lookup (e.g. 'Subsurface IOR' on Blender 5.x).
"""
for inp in node.inputs:
if inp.name == name:
inp.default_value = value
return True
return False
对于始终处于启用状态的输入(Base Color、Metallic、Roughness、IOR、Transmission Weight、Sheen Weight 等),直接通过字符串键赋值仍然有效 — 只有在输入被有条件地禁用时才需要使用 helper。配方 9(皮肤)会使用它,因为 Subsurface IOR 属于受影响的输入。
配方 1 — 拉丝钢
import bpy
mat = bpy.data.materials.new('MAT-steel_brushed')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.56, 0.57, 0.58, 1.0)
bsdf.inputs['Metallic'].default_value = 1.0
bsdf.inputs['Roughness'].default_value = 0.25
obj = bpy.data.objects['GEO-target']
if obj.data.materials:
obj.data.materials[0] = mat
else:
obj.data.materials.append(mat)
print(f"material:MAT-steel_brushed→{obj.name}")
配方 2 — 抛光金
import bpy
mat = bpy.data.materials.new('MAT-gold_polished')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (1.022, 0.782, 0.344, 1.0)
bsdf.inputs['Metallic'].default_value = 1.0
bsdf.inputs['Roughness'].default_value = 0.05
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:gold_polished')
配方 3 — 抛光铜
import bpy
mat = bpy.data.materials.new('MAT-copper_polished')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.926, 0.721, 0.504, 1.0)
bsdf.inputs['Metallic'].default_value = 1.0
bsdf.inputs['Roughness'].default_value = 0.05
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:copper_polished')
配方 4 — 镜面铬
import bpy
mat = bpy.data.materials.new('MAT-chrome')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.55, 0.56, 0.55, 1.0)
bsdf.inputs['Metallic'].default_value = 1.0
bsdf.inputs['Roughness'].default_value = 0.02
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:chrome')
配方 5 — 透明玻璃
import bpy
mat = bpy.data.materials.new('MAT-glass_clear')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (1.0, 1.0, 1.0, 1.0)
bsdf.inputs['Metallic'].default_value = 0.0
bsdf.inputs['Roughness'].default_value = 0.0
bsdf.inputs['Transmission Weight'].default_value = 1.0
bsdf.inputs['IOR'].default_value = 1.5
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:glass_clear')
配方 6 — 磨砂玻璃
import bpy
mat = bpy.data.materials.new('MAT-glass_frosted')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (1.0, 1.0, 1.0, 1.0)
bsdf.inputs['Transmission Weight'].default_value = 1.0
bsdf.inputs['IOR'].default_value = 1.5
bsdf.inputs['Roughness'].default_value = 0.3
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:glass_frosted')
配方 6b — 彩色玻璃(酒瓶、有色小瓶、装饰玻璃)
仅使用 Base Color 为 Principled BSDF 着色,会让彩色玻璃看起来平淡或像金属。真实的彩色玻璃具有体积吸收:光线穿过玻璃时会根据行进距离染上颜色,因此厚的部分看起来更暗,薄的部分看起来更亮。这种基于深度的丰富效果,能让玻璃看起来像玻璃。
模式:让表面接近白色且略带粗糙度,并将 Volume Absorption 着色器连接到 Material Output 的 Volume 输入。
import bpy
def set_input(node, name, value):
for inp in node.inputs:
if inp.name == name:
inp.default_value = value
return True
return False
mat = bpy.data.materials.new('MAT-glass_wine')
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
bsdf = nodes['Principled BSDF']
output = nodes['Material Output']
# Surface: near-white with tiny roughness (breaks mirror-finish look)
set_input(bsdf, 'Base Color', (0.85, 0.95, 0.85, 1.0)) # near-white
set_input(bsdf, 'Metallic', 0.0)
set_input(bsdf, 'Roughness', 0.025) # critical: not 0.0; that looks metallic
set_input(bsdf, 'Transmission Weight', 1.0)
set_input(bsdf, 'IOR', 1.52) # bottle glass
# Volume Absorption — depth-based tint
volume = nodes.new('ShaderNodeVolumeAbsorption')
set_input(volume, 'Color', (0.10, 0.45, 0.18, 1.0)) # saturated wine-bottle green
set_input(volume, 'Density', 30.0) # higher = more colour over short distance
links.new(volume.outputs['Volume'], output.inputs['Volume'])
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:glass_wine_volume_absorption')
调整密度:0–10 = 极淡的色调(透明瓶);20–40 = 清澈的瓶绿色或琥珀色;60–100+ = 几乎不透明(钴蓝色药瓶)。
调整颜色:直觉要反过来——体积颜色代表穿过的光线中被吸收的颜色,因此要呈现“酒瓶绿”,请使用饱和绿色;要呈现“琥珀色”,请使用饱和的黄橙色。
其他彩色玻璃示例(主体类别照明修复后,密度值已在 v0.9.0 更新):
| 名称 | 体积颜色 | 密度 | 表面色调 |
|---|---|---|---|
| 酒瓶(深绿色) | (0.05, 0.32, 0.10) | 80 | 接近白色 |
| 浅色调(透明小瓶) | (0.10, 0.45, 0.18) | 15 | 接近白色 |
| 香槟色/浅金色 | (0.85, 0.65, 0.30) | 25 | 接近白色 |
| 钴蓝色(药瓶) | (0.10, 0.20, 0.85) | 80 | 接近白色 |
| 琥珀色/棕色啤酒瓶 | (0.80, 0.40, 0.10) | 70 | 接近白色 |
| 红宝石红 | (0.85, 0.10, 0.15) | 100 | 接近白色 |
中性/玻璃类别照明下的密度调整经验法则:
- 密度 5–15 = 颜色淡淡显现(透明且带色)
- 密度 30–50 = 中等色调,在薄的部分可见
- 密度 60–100 = 呈现出理想的葡萄酒/啤酒/钴蓝色瓶效果(推荐用于主视觉镜头)
- 密度 100+ = 几乎不透明(艺术/装饰效果)
如果在标准的 4:1:2 金属类别照明下,体积色调被冲淡(v0.7.0 问题),不要通过增加密度来补偿——请切换到 blender-lighting 配方 0a 中的 subject_class='glass' 照明,该设置使用更柔和的轮廓光来保留体积颜色。
重要:对于厚重或分层的彩色玻璃,Cycles 的 transmission_bounces 必须 ≥ 16(默认值为 12);否则光线会终止,导致玻璃内部呈现黑色。
scene.cycles.transmission_bounces = 24
注意事项:不要将 Base Color 设为染色颜色,同时又添加 Volume——这会导致重复着色,效果不佳。表面保持接近白色,由体积负责着色。
配方 7 — 哑光塑料(红色)
import bpy
mat = bpy.data.materials.new('MAT-plastic_matte_red')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.8, 0.1, 0.05, 1.0)
bsdf.inputs['Metallic'].default_value = 0.0
bsdf.inputs['Roughness'].default_value = 0.6
bsdf.inputs['IOR'].default_value = 1.45
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:plastic_matte_red')
配方 8 — 漆面塑料(汽车漆效果)
import bpy
mat = bpy.data.materials.new('MAT-plastic_lacquered')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.8, 0.1, 0.05, 1.0)
bsdf.inputs['Metallic'].default_value = 0.0
bsdf.inputs['Roughness'].default_value = 0.15
bsdf.inputs['IOR'].default_value = 1.45
bsdf.inputs['Coat Weight'].default_value = 0.8
bsdf.inputs['Coat Roughness'].default_value = 0.05
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:plastic_lacquered')
配方 9 — 皮肤(浅色调)
使用 set_input 辅助函数,因为在 Blender 5.x 中,Subsurface IOR 的 enabled=False,无法通过字符串键查找访问。其他输入则两种方式都可以正常使用;统一使用此辅助函数可确保配方在不同版本中都安全可用。
import bpy
def set_input(node, name, value):
for inp in node.inputs:
if inp.name == name:
inp.default_value = value
return True
return False
mat = bpy.data.materials.new('MAT-skin_light')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
set_input(bsdf, 'Base Color', (0.85, 0.65, 0.55, 1.0))
set_input(bsdf, 'Metallic', 0.0)
set_input(bsdf, 'Roughness', 0.4)
set_input(bsdf, 'Subsurface Weight', 1.0)
set_input(bsdf, 'Subsurface Radius', (1.0, 0.2, 0.1))
set_input(bsdf, 'Subsurface IOR', 1.4) # ← string-key fails on Blender 5.x; helper bypasses it
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:skin_light')
配方 10 — 天鹅绒/带光泽的织物
import bpy
mat = bpy.data.materials.new('MAT-velvet_red')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.6, 0.0, 0.1, 1.0)
bsdf.inputs['Roughness'].default_value = 0.9
bsdf.inputs['Sheen Weight'].default_value = 0.5
bsdf.inputs['Sheen Roughness'].default_value = 0.5
bsdf.inputs['Sheen Tint'].default_value = (0.8, 0.6, 0.6, 1.0)
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:velvet_red')
配方 11 — 柔软硅胶
import bpy
mat = bpy.data.materials.new('MAT-silicone')
mat.use_nodes = True
bsdf = mat.node_tree.nodes['Principled BSDF']
bsdf.inputs['Base Color'].default_value = (0.65, 0.63, 0.60, 1.0)
bsdf.inputs['Metallic'].default_value = 0.0
bsdf.inputs['Roughness'].default_value = 0.7
bsdf.inputs['IOR'].default_value = 1.4
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:silicone')
配方 11b — 发光(发光网格,例如灯泡、霓虹招牌、屏幕辉光)
发光着色器是独立于 Principled BSDF 的着色器——完全替换 BSDF,改用 ShaderNodeEmission 并连接到 Material Output 的 Surface 输入。网格本身会成为光源(在 Cycles 中参与场景照明)。
import bpy
mat = bpy.data.materials.new('MAT-bulb_emission')
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
# Remove the default Principled BSDF
for n in list(nodes):
if n.type == 'BSDF_PRINCIPLED':
nodes.remove(n)
emission = nodes.new('ShaderNodeEmission')
emission.inputs['Color'].default_value = (1.0, 0.92, 0.78, 1.0) # warm tungsten
emission.inputs['Strength'].default_value = 1500.0 # see strength guide below
output = nodes['Material Output']
links.new(emission.outputs['Emission'], output.inputs['Surface'])
bpy.data.objects['GEO-bulb'].data.materials.append(mat)
print('material:bulb_emission')
Strength 调节——对网格发射器至关重要
网格发射的实际亮度取决于网格表面积,而不仅仅是 Strength 值。Strength=50 的小球几乎看不见;同一个球在 Strength=1500 时,照亮桌面的效果就像真正的灯泡。以下表格可用作大致参考值:
| 网格尺寸 | 相当于 | Strength |
|---|---|---|
| 1-2 cm 球体(爱迪生灯泡) | 40W 暖光灯泡 | 800-1500 |
| 3-5 cm 球体(LED 球泡) | 60-100W 灯泡 | 1500-3000 |
| 10×10 cm 平面板(LED 面板) | 室内照明面板 | 100-300 |
| 100×30 cm 长条(霓虹灯管) | 霓虹灯招牌 | 50-150 |
| 大型窗户平面(天空模拟) | 日光 | 5-20 |
经验法则:表面积越小,所需 Strength 越高。球体半径加倍,所需 Strength 会降低约 4 倍(与表面积成反比)。
使用 (R, G, B) 设置色温:
- 钨丝灯(3200K)——
(1.0, 0.85, 0.6) - 暖光 LED(3000K)——
(1.0, 0.8, 0.6) - 日光(5500K)——
(1.0, 1.0, 1.0) - 冷白荧光灯(4500K)——
(0.95, 0.95, 1.0)
灯罩——单独翻转法线的内侧网格
如果灯泡位于灯罩内,灯罩的内侧表面需要是明亮的哑光白色,才能逼真地反射灯泡光线。单网格灯罩只会显示外侧材质。解决方法:复制灯罩网格,翻转法线,将尺寸缩小至 97%,并应用亮白色材质。这样灯泡照亮灯罩时,内侧就能呈现恰当的发光效果。
import bpy
# Assuming `shade` is the outer cone with the dark exterior material already applied
shade = bpy.data.objects['GEO-lamp_shade']
bpy.ops.object.select_all(action='DESELECT')
shade.select_set(True); bpy.context.view_layer.objects.active = shade
bpy.ops.object.duplicate()
shade_in = bpy.context.active_object
shade_in.name = shade.name + '_interior'
# Flip normals so the inside surface faces inward
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.flip_normals()
bpy.ops.object.mode_set(mode='OBJECT')
# Bright white interior
mat_in = bpy.data.materials.new('MAT-shade_interior')
mat_in.use_nodes = True
b = mat_in.node_tree.nodes['Principled BSDF']
b.inputs['Base Color'].default_value = (0.95, 0.93, 0.88, 1.0)
b.inputs['Roughness'].default_value = 0.5
shade_in.data.materials.clear()
shade_in.data.materials.append(mat_in)
shade_in.scale = (0.97, 0.97, 0.97)
print('material:shade_interior_white')
配方 12——程序化木材(10 个节点)
import bpy
mat = bpy.data.materials.new('MAT-wood_procedural')
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
bsdf = nodes['Principled BSDF']
# Texture coordinate
tex_coord = nodes.new('ShaderNodeTexCoord')
tex_coord.location = (-800, 0)
# Mapping
mapping = nodes.new('ShaderNodeMapping')
mapping.location = (-600, 0)
mapping.inputs['Scale'].default_value = (3, 3, 3)
# Wave (the grain)
wave = nodes.new('ShaderNodeTexWave')
wave.location = (-400, 100)
wave.wave_type = 'BANDS'
wave.bands_direction = 'X'
wave.inputs['Scale'].default_value = 5.0
wave.inputs['Distortion'].default_value = 4.0
# Noise (variation)
noise = nodes.new('ShaderNodeTexNoise')
noise.location = (-400, -100)
noise.inputs['Scale'].default_value = 8.0
# Mix wave + noise
mix = nodes.new('ShaderNodeMixRGB')
mix.location = (-200, 0)
mix.blend_type = 'MULTIPLY'
mix.inputs[0].default_value = 0.5
# ColorRamp (tonal range)
ramp = nodes.new('ShaderNodeValToRGB')
ramp.location = (0, 0)
ramp.color_ramp.elements[0].color = (0.15, 0.07, 0.03, 1.0) # dark wood
ramp.color_ramp.elements[1].color = (0.6, 0.35, 0.18, 1.0) # light wood
# Wire
links.new(tex_coord.outputs['Generated'], mapping.inputs['Vector'])
links.new(mapping.outputs['Vector'], wave.inputs['Vector'])
links.new(mapping.outputs['Vector'], noise.inputs['Vector'])
links.new(wave.outputs['Color'], mix.inputs[1])
links.new(noise.outputs['Color'], mix.inputs[2])
links.new(mix.outputs['Color'], ramp.inputs['Fac'])
links.new(ramp.outputs['Color'], bsdf.inputs['Base Color'])
bsdf.inputs['Roughness'].default_value = 0.7
bpy.data.objects['GEO-target'].data.materials.append(mat)
print('material:wood_procedural')
注意:程序化材质不会导出到 glTF。若要导出到网页或游戏,需先烘焙为图像纹理。
PBR 值参考
如需查询任何金属的精确 F0 反射率值,请参阅:physicallybased.info——涵盖 50 多种材质。上述配方使用的数值均来自此数据库。
材质命名规范
MAT-{purpose}_{subtype}_{finish}。示例:
MAT-frame_metal_brushedMAT-lens_glass_dark_mirrorMAT-pad_silicone_warm_grayMAT-wood_oak_glossy
避免使用 Material.001、Material.027。务必重命名。
常见问题
| 症状 | 修复方法 |
|---|---|
| 金属看起来“像塑料” | Metallic 必须恰好为 0 或 1 |
| 金属呈黑色 | 基色太暗;金属会反射 30–100%;保持 ≥0.5 sRGB |
| Roughness 设为 0 时出现伪影 | 最低使用 0.01–0.05 |
| 玻璃渲染为黑色 | 增加 Cycles 的传输反弹次数(配方章节 11-rendering) |
| glTF 中看不到材质 | 使用了程序化着色器;请先烘焙为图像 |
| 法线贴图效果异常 | 将图像纹理的色彩空间设为“Non-Color” |
| 粗糙度贴图使用 sRGB | 将图像纹理设为“Non-Color” |
KeyError: 'Subsurface IOR'(或任何其他输入) | Blender 5.x 特性问题:输入的 enabled=False;请改用本文件顶部的 set_input 辅助函数,不要使用 bsdf.inputs['Name'] |
何时加载 references/overview.md
以下情况请加载:
- 所需配方不在上述 12 个配方中
- 需要各向异性(金属拉丝方向)、体积吸收(有色厚玻璃)或高级着色器节点组合
- 用户要求在同一网格上使用不同材质(Mix Shader 模式)
- 正在为导出将程序化纹理烘焙为图像纹理
参考内容包括:完整的 Principled BSDF 参数映射、包含 50 多种材质的数据库链接、程序化纹理组合(Voronoi、Wave、Noise)、Sheen + Subsurface 深入介绍,以及用于导出的烘焙工作流。