substance-3d-texturing by freshtechbro/claudedesignskills
npx skills add https://github.com/freshtechbro/claudedesignskills --skill substance-3d-texturing掌握面向网页和实时引擎的 PBR(基于物理的渲染)纹理创建与导出工作流。本技能涵盖从材质创建到网页优化纹理导出的 Substance 3D Painter 工作流,包括用于批量处理的 Python 自动化以及与 WebGL/WebGPU 引擎的集成。
核心能力:
Substance 3D Painter 使用金属度/粗糙度 PBR 工作流,包含以下核心通道:
基础纹理贴图:
baseColor(反照率)- RGB 漫反射颜色,不包含光照信息normal - RGB 法线贴图(切线空间)metallic - 灰度金属度(0 = 非金属,1 = 金属)roughness - 灰度表面粗糙度(0 = 光滑/有光泽,1 = 粗糙/哑光)附加贴图:
ambientOcclusion(AO)- 灰度环境光遮蔽/遮挡广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
height - 灰度置换/高度emissive - RGB 自发光opacity - 灰度透明度Substance 3D Painter 包含针对常用引擎的内置导出预设:
对于网页引擎,PBR Metallic Roughness 是通用标准。
网页常用分辨率(2 的幂次):
网页优化规则: 从 1024×1024 开始,仅在纹理细节可见时放大。
单个纹理集的手动导出工作流:
步骤:
结果文件:
MyAsset_baseColor.png
MyAsset_normal.png
MyAsset_metallicRoughness.png // 打包:R=无,G=粗糙度,B=金属度
MyAsset_emissive.png // 可选
Three.js 用法:
import * as THREE from 'three';
const textureLoader = new THREE.TextureLoader();
const material = new THREE.MeshStandardMaterial({
map: textureLoader.load('MyAsset_baseColor.png'),
normalMap: textureLoader.load('MyAsset_normal.png'),
metalnessMap: textureLoader.load('MyAsset_metallicRoughness.png'),
roughnessMap: textureLoader.load('MyAsset_metallicRoughness.png'),
aoMap: textureLoader.load('MyAsset_ambientOcclusion.png'),
});
自动化导出多个纹理集:
import substance_painter.export
import substance_painter.resource
import substance_painter.textureset
# 定义导出预设
export_preset = substance_painter.resource.ResourceID(
context="starter_assets",
name="PBR Metallic Roughness"
)
# 为所有纹理集配置导出
config = {
"exportShaderParams": False,
"exportPath": "C:/export/web_textures",
"defaultExportPreset": export_preset.url(),
"exportList": [],
"exportParameters": [{
"parameters": {
"fileFormat": "png",
"bitDepth": "8",
"dithering": True,
"paddingAlgorithm": "infinite",
"sizeLog2": 10 // 1024×1024
}
}]
}
# 将所有纹理集添加到导出列表
for texture_set in substance_painter.textureset.all_texture_sets():
config["exportList"].append({
"rootPath": texture_set.name()
})
# 执行导出
result = substance_painter.export.export_project_textures(config)
if result.status == substance_painter.export.ExportStatus.Success:
for stack, files in result.textures.items():
print(f"Exported {stack}: {len(files)} textures")
else:
print(f"Export failed: {result.message}")
为不同资产(例如,主要角色与背景道具)导出不同分辨率:
config = {
"exportPath": "C:/export",
"defaultExportPreset": export_preset.url(),
"exportList": [
{"rootPath": "HeroCharacter"}, # 将使用 2048(下方覆盖)
{"rootPath": "BackgroundProp"} # 将使用 512(下方覆盖)
],
"exportParameters": [
{
"filter": {"dataPaths": ["HeroCharacter"]},
"parameters": {"sizeLog2": 11} # 2048×2048
},
{
"filter": {"dataPaths": ["BackgroundProp"]},
"parameters": {"sizeLog2": 9} # 512×512
}
]
}
创建自定义预设,将金属度和粗糙度导出为单独文件:
custom_preset = {
"exportPresets": [{
"name": "WebGL_Separated",
"maps": [
{
"fileName": "$textureSet_baseColor",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "baseColor"},
{"destChannel": "G", "srcChannel": "G", "srcMapType": "documentMap", "srcMapName": "baseColor"},
{"destChannel": "B", "srcChannel": "B", "srcMapType": "documentMap", "srcMapName": "baseColor"}
]
},
{
"fileName": "$textureSet_normal",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "normal"},
{"destChannel": "G", "srcChannel": "G", "srcMapType": "documentMap", "srcMapName": "normal"},
{"destChannel": "B", "srcChannel": "B", "srcMapType": "documentMap", "srcMapName": "normal"}
]
},
{
"fileName": "$textureSet_metallic",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "metallic"}
],
"parameters": {"fileFormat": "png", "bitDepth": "8"}
},
{
"fileName": "$textureSet_roughness",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "roughness"}
],
"parameters": {"fileFormat": "png", "bitDepth": "8"}
}
]
}]
}
config = {
"exportPath": "C:/export",
"exportPresets": custom_preset["exportPresets"],
"exportList": [{"rootPath": "MyAsset", "exportPreset": "WebGL_Separated"}]
}
针对移动端 WebGL 的激进压缩:
mobile_config = {
"exportPath": "C:/export/mobile",
"defaultExportPreset": export_preset.url(),
"exportList": [{"rootPath": texture_set.name()}],
"exportParameters": [{
"parameters": {
"fileFormat": "jpeg", # 对 baseColor 使用 JPEG(有损但更小)
"bitDepth": "8",
"sizeLog2": 9, # 最大 512×512
"paddingAlgorithm": "infinite"
}
}, {
"filter": {"outputMaps": ["$textureSet_normal", "$textureSet_metallicRoughness"]},
"parameters": {
"fileFormat": "png" # 对数据贴图使用 PNG(需要无损)
}
}]
}
导出后处理: 使用 pngquant 或 tinypng 等工具进行进一步压缩。
为 glTF 2.0 格式导出纹理:
gltf_config = {
"exportPath": "C:/export/gltf",
"defaultExportPreset": substance_painter.resource.ResourceID(
context="starter_assets",
name="PBR Metallic Roughness"
).url(),
"exportList": [{"rootPath": texture_set.name()}],
"exportParameters": [{
"parameters": {
"fileFormat": "png",
"bitDepth": "8",
"sizeLog2": 10, # 1024×1024
"paddingAlgorithm": "infinite"
}
}]
}
# 导出后,在 glTF 中引用:
# {
# "materials": [{
# "name": "Material",
# "pbrMetallicRoughness": {
# "baseColorTexture": {"index": 0},
# "metallicRoughnessTexture": {"index": 1}
# },
# "normalTexture": {"index": 2}
# }]
# }
使用 Python 插件在保存时自动导出:
import substance_painter.event
import substance_painter.export
import substance_painter.project
def auto_export(e):
if not substance_painter.project.is_open():
return
config = {
"exportPath": substance_painter.project.file_path().replace('.spp', '_textures'),
"defaultExportPreset": substance_painter.resource.ResourceID(
context="starter_assets", name="PBR Metallic Roughness"
).url(),
"exportList": [{"rootPath": ts.name()} for ts in substance_painter.textureset.all_texture_sets()],
"exportParameters": [{
"parameters": {"fileFormat": "png", "bitDepth": "8", "sizeLog2": 10}
}]
}
substance_painter.export.export_project_textures(config)
print("Auto-export completed")
# 注册事件
substance_painter.event.DISPATCHER.connect(
substance_painter.event.ProjectSaved,
auto_export
)
在 R3F 中使用导出的纹理:
import { useTexture } from '@react-three/drei';
function TexturedMesh() {
const [baseColor, normal, metallicRoughness, ao] = useTexture([
'/textures/Asset_baseColor.png',
'/textures/Asset_normal.png',
'/textures/Asset_metallicRoughness.png',
'/textures/Asset_ambientOcclusion.png',
]);
return (
<mesh>
<boxGeometry />
<meshStandardMaterial
map={baseColor}
normalMap={normal}
metalnessMap={metallicRoughness}
roughnessMap={metallicRoughness}
aoMap={ao}
/>
</mesh>
);
}
查看 react-three-fiber 技能以获取高级 R3F 材质工作流。
import { PBRMaterial, Texture } from '@babylonjs/core';
const pbr = new PBRMaterial("pbr", scene);
pbr.albedoTexture = new Texture("/textures/Asset_baseColor.png", scene);
pbr.bumpTexture = new Texture("/textures/Asset_normal.png", scene);
pbr.metallicTexture = new Texture("/textures/Asset_metallicRoughness.png", scene);
pbr.useRoughnessFromMetallicTextureAlpha = false;
pbr.useRoughnessFromMetallicTextureGreen = true;
pbr.useMetallnessFromMetallicTextureBlue = true;
查看 babylonjs-engine 技能以获取高级 PBR 工作流。
.gltf JSON 中引用 Substance 纹理gltf-pipeline 进行 Draco 压缩:gltf-pipeline -i model.gltf -o model.glb -d
查看 blender-web-pipeline 技能以获取完整的 3D 资产管线。
桌面 WebGL: 总计约 100-150MB 纹理内存 移动端 WebGL: 总计约 30-50MB 纹理内存
每个资产的预算:
.basis)- GPU 纹理压缩(小 90%)将灰度贴图打包到 RGB 通道中以减少纹理数量:
打包的 ORM(环境光遮蔽-粗糙度-金属度):
在 Substance 中导出:
orm_map = {
"fileName": "$textureSet_ORM",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "ambientOcclusion"},
{"destChannel": "G", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "roughness"},
{"destChannel": "B", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "metallic"}
]
}
始终在引擎中为远距离观看的纹理启用 mipmaps:
// Three.js(自动)
texture.generateMipmaps = true;
// Babylon.js
texture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE);
问题: 在线性空间中导出的 BaseColor 看起来褪色。
解决方案: Substance 默认以 sRGB 导出 baseColor(正确)。确保引擎使用 sRGB:
// Three.js
baseColorTexture.colorSpace = THREE.SRGBColorSpace;
// Babylon.js(对 albedoTexture 自动处理)
问题: 法线贴图显示反转或不正确的着色。
解决方案:
问题: 金属度/粗糙度纹理的通道交换了。
解决方案: 默认 Substance 导出:
问题: UV 接缝处出现黑色或彩色线条。
解决方案: 在导出设置中将填充算法设置为 "infinite":
"paddingAlgorithm": "infinite"
问题: 4K 纹理导致网页加载时间长和内存问题。
解决方案:
问题: AO 贴图已导出但在引擎中不可见。
解决方案:
geometry.attributes.uv2)material.useAmbientOcclusionFromMetallicTextureRed = true查看捆绑资源以获取完整工作流:
每周安装次数
74
仓库
GitHub 星标数
11
首次出现
2026 年 2 月 27 日
安全审计
安装于
opencode74
cursor72
gemini-cli72
claude-code72
github-copilot72
amp72
Master PBR (Physically Based Rendering) texture creation and export workflows for web and real-time engines. This skill covers Substance 3D Painter workflows from material creation through web-optimized texture export, with Python automation for batch processing and integration with WebGL/WebGPU engines.
Key capabilities:
Substance 3D Painter uses the metallic/roughness PBR workflow with these core channels:
Base Texture Maps:
baseColor (Albedo) - RGB diffuse color, no lighting informationnormal - RGB normal map (tangent space)metallic - Grayscale metalness (0 = dielectric, 1 = metal)roughness - Grayscale surface roughness (0 = smooth/glossy, 1 = rough/matte)Additional Maps:
ambientOcclusion (AO) - Grayscale cavity/occlusionheight - Grayscale displacement/heightemissive - RGB self-illuminationopacity - Grayscale transparencySubstance 3D Painter includes built-in export presets for common engines:
For web engines, PBR Metallic Roughness is the universal standard.
Common resolutions for web (powers of 2):
Web optimization rule: Start at 1024×1024, scale up only when texture detail is visible.
Manual export workflow for single texture set:
Steps:
Result files:
MyAsset_baseColor.png
MyAsset_normal.png
MyAsset_metallicRoughness.png // Packed: R=nothing, G=roughness, B=metallic
MyAsset_emissive.png // Optional
Three.js usage:
import * as THREE from 'three';
const textureLoader = new THREE.TextureLoader();
const material = new THREE.MeshStandardMaterial({
map: textureLoader.load('MyAsset_baseColor.png'),
normalMap: textureLoader.load('MyAsset_normal.png'),
metalnessMap: textureLoader.load('MyAsset_metallicRoughness.png'),
roughnessMap: textureLoader.load('MyAsset_metallicRoughness.png'),
aoMap: textureLoader.load('MyAsset_ambientOcclusion.png'),
});
Automate export for multiple texture sets:
import substance_painter.export
import substance_painter.resource
import substance_painter.textureset
# Define export preset
export_preset = substance_painter.resource.ResourceID(
context="starter_assets",
name="PBR Metallic Roughness"
)
# Configure export for all texture sets
config = {
"exportShaderParams": False,
"exportPath": "C:/export/web_textures",
"defaultExportPreset": export_preset.url(),
"exportList": [],
"exportParameters": [{
"parameters": {
"fileFormat": "png",
"bitDepth": "8",
"dithering": True,
"paddingAlgorithm": "infinite",
"sizeLog2": 10 // 1024×1024
}
}]
}
# Add all texture sets to export list
for texture_set in substance_painter.textureset.all_texture_sets():
config["exportList"].append({
"rootPath": texture_set.name()
})
# Execute export
result = substance_painter.export.export_project_textures(config)
if result.status == substance_painter.export.ExportStatus.Success:
for stack, files in result.textures.items():
print(f"Exported {stack}: {len(files)} textures")
else:
print(f"Export failed: {result.message}")
Export different resolutions for different assets (e.g., hero vs. background):
config = {
"exportPath": "C:/export",
"defaultExportPreset": export_preset.url(),
"exportList": [
{"rootPath": "HeroCharacter"}, # Will use 2048 (override below)
{"rootPath": "BackgroundProp"} # Will use 512 (override below)
],
"exportParameters": [
{
"filter": {"dataPaths": ["HeroCharacter"]},
"parameters": {"sizeLog2": 11} # 2048×2048
},
{
"filter": {"dataPaths": ["BackgroundProp"]},
"parameters": {"sizeLog2": 9} # 512×512
}
]
}
Create custom preset to export metallic and roughness as separate files:
custom_preset = {
"exportPresets": [{
"name": "WebGL_Separated",
"maps": [
{
"fileName": "$textureSet_baseColor",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "baseColor"},
{"destChannel": "G", "srcChannel": "G", "srcMapType": "documentMap", "srcMapName": "baseColor"},
{"destChannel": "B", "srcChannel": "B", "srcMapType": "documentMap", "srcMapName": "baseColor"}
]
},
{
"fileName": "$textureSet_normal",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "normal"},
{"destChannel": "G", "srcChannel": "G", "srcMapType": "documentMap", "srcMapName": "normal"},
{"destChannel": "B", "srcChannel": "B", "srcMapType": "documentMap", "srcMapName": "normal"}
]
},
{
"fileName": "$textureSet_metallic",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "metallic"}
],
"parameters": {"fileFormat": "png", "bitDepth": "8"}
},
{
"fileName": "$textureSet_roughness",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "roughness"}
],
"parameters": {"fileFormat": "png", "bitDepth": "8"}
}
]
}]
}
config = {
"exportPath": "C:/export",
"exportPresets": custom_preset["exportPresets"],
"exportList": [{"rootPath": "MyAsset", "exportPreset": "WebGL_Separated"}]
}
Aggressive compression for mobile WebGL:
mobile_config = {
"exportPath": "C:/export/mobile",
"defaultExportPreset": export_preset.url(),
"exportList": [{"rootPath": texture_set.name()}],
"exportParameters": [{
"parameters": {
"fileFormat": "jpeg", # JPEG for baseColor (lossy but smaller)
"bitDepth": "8",
"sizeLog2": 9, # 512×512 maximum
"paddingAlgorithm": "infinite"
}
}, {
"filter": {"outputMaps": ["$textureSet_normal", "$textureSet_metallicRoughness"]},
"parameters": {
"fileFormat": "png" # PNG for data maps (need lossless)
}
}]
}
Post-export: Use tools like pngquant or tinypng for further compression.
Export textures for glTF 2.0 format:
gltf_config = {
"exportPath": "C:/export/gltf",
"defaultExportPreset": substance_painter.resource.ResourceID(
context="starter_assets",
name="PBR Metallic Roughness"
).url(),
"exportList": [{"rootPath": texture_set.name()}],
"exportParameters": [{
"parameters": {
"fileFormat": "png",
"bitDepth": "8",
"sizeLog2": 10, # 1024×1024
"paddingAlgorithm": "infinite"
}
}]
}
# After export, reference in glTF:
# {
# "materials": [{
# "name": "Material",
# "pbrMetallicRoughness": {
# "baseColorTexture": {"index": 0},
# "metallicRoughnessTexture": {"index": 1}
# },
# "normalTexture": {"index": 2}
# }]
# }
Auto-export on save using Python plugin:
import substance_painter.event
import substance_painter.export
import substance_painter.project
def auto_export(e):
if not substance_painter.project.is_open():
return
config = {
"exportPath": substance_painter.project.file_path().replace('.spp', '_textures'),
"defaultExportPreset": substance_painter.resource.ResourceID(
context="starter_assets", name="PBR Metallic Roughness"
).url(),
"exportList": [{"rootPath": ts.name()} for ts in substance_painter.textureset.all_texture_sets()],
"exportParameters": [{
"parameters": {"fileFormat": "png", "bitDepth": "8", "sizeLog2": 10}
}]
}
substance_painter.export.export_project_textures(config)
print("Auto-export completed")
# Register event
substance_painter.event.DISPATCHER.connect(
substance_painter.event.ProjectSaved,
auto_export
)
Use exported textures in R3F:
import { useTexture } from '@react-three/drei';
function TexturedMesh() {
const [baseColor, normal, metallicRoughness, ao] = useTexture([
'/textures/Asset_baseColor.png',
'/textures/Asset_normal.png',
'/textures/Asset_metallicRoughness.png',
'/textures/Asset_ambientOcclusion.png',
]);
return (
<mesh>
<boxGeometry />
<meshStandardMaterial
map={baseColor}
normalMap={normal}
metalnessMap={metallicRoughness}
roughnessMap={metallicRoughness}
aoMap={ao}
/>
</mesh>
);
}
See react-three-fiber skill for advanced R3F material workflows.
import { PBRMaterial, Texture } from '@babylonjs/core';
const pbr = new PBRMaterial("pbr", scene);
pbr.albedoTexture = new Texture("/textures/Asset_baseColor.png", scene);
pbr.bumpTexture = new Texture("/textures/Asset_normal.png", scene);
pbr.metallicTexture = new Texture("/textures/Asset_metallicRoughness.png", scene);
pbr.useRoughnessFromMetallicTextureAlpha = false;
pbr.useRoughnessFromMetallicTextureGreen = true;
pbr.useMetallnessFromMetallicTextureBlue = true;
See babylonjs-engine skill for advanced PBR workflows.
.gltf JSONgltf-pipeline for Draco compression:gltf-pipeline -i model.gltf -o model.glb -d
See blender-web-pipeline skill for complete 3D asset pipeline.
Desktop WebGL: ~100-150MB total texture memory Mobile WebGL: ~30-50MB total texture memory
Budget per asset:
.basis) - GPU texture compression (90% smaller)Pack grayscale maps into RGB channels to reduce texture count:
Packed ORM (Occlusion-Roughness-Metallic):
Export in Substance:
orm_map = {
"fileName": "$textureSet_ORM",
"channels": [
{"destChannel": "R", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "ambientOcclusion"},
{"destChannel": "G", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "roughness"},
{"destChannel": "B", "srcChannel": "R", "srcMapType": "documentMap", "srcMapName": "metallic"}
]
}
Always enable mipmaps in engine for textures viewed at distance:
// Three.js (automatic)
texture.generateMipmaps = true;
// Babylon.js
texture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE);
Problem: BaseColor exported in linear space looks washed out.
Solution: Substance exports baseColor in sRGB by default (correct). Ensure engine uses sRGB:
// Three.js
baseColorTexture.colorSpace = THREE.SRGBColorSpace;
// Babylon.js (automatic for albedoTexture)
Problem: Normal maps show inverted or incorrect shading.
Solution:
Problem: Metallic/roughness texture has swapped channels.
Solution: Default Substance export:
Problem: Black or colored lines appear at UV seams.
Solution: Set padding algorithm to "infinite" in export settings:
"paddingAlgorithm": "infinite"
Problem: 4K textures cause long load times and memory issues on web.
Solution:
Problem: AO map exported but not visible in engine.
Solution:
geometry.attributes.uv2)material.useAmbientOcclusionFromMetallicTextureRed = trueSee bundled resources for complete workflows:
Weekly Installs
74
Repository
GitHub Stars
11
First Seen
Feb 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode74
cursor72
gemini-cli72
claude-code72
github-copilot72
amp72
Skills CLI 使用指南:AI Agent 技能包管理器安装与管理教程
46,600 周安装