4. 材质与外观:让物体“看起来”真实
好,我们进入第四讲。前几章我们把物体放进了场景,让它们动了起来。但说实话,光秃秃的灰色模型,看着就像没上漆的石膏像。这一章,我们就来给它们“上色”——赋予材质、调整属性、贴上纹理。
在 Isaac Sim 里,材质控制着物体如何与光线交互。你想想看,一个金属球和一个塑料球,在同样的光照下,反射出来的感觉完全不同。这就是材质在起作用。
4.1 材质的核心属性
我个人习惯把材质属性分成三类:颜色、粗糙度、金属度。这三者基本决定了物体80%的外观。
| 属性 | 含义 | 取值范围 | 典型效果 |
|---|---|---|---|
| 颜色 (Color) | 物体表面的基础色调 | RGB 0.0 ~ 1.0 | 红色塑料、蓝色金属 |
| 粗糙度 (Roughness) | 表面微观不平整程度 | 0.0 ~ 1.0 | 0.0 镜面,1.0 磨砂 |
| 金属度 (Metallic) | 表面金属化程度 | 0.0 ~ 1.0 | 0.0 非金属,1.0 纯金属 |
这里有个小陷阱。我曾经在项目里把金属度设成0.5,结果物体看起来既不像金属也不像塑料,非常奇怪。后来我明白了,金属度最好只用0或1这两个极端值,中间值容易产生不自然的“塑料金属”感。
4.2 用 Python 创建和赋予材质
在 Isaac Sim 里操作材质,核心就两步:先创建材质对象,再把它赋给物体的视觉属性。我们直接看代码。
import omni.usd
from pxr import Usd, UsdShade, Gf, Sdf
# 获取当前 stage
stage = omni.usd.get_context().get_stage()
# 1. 创建一个材质 prim
material_path = "/World/Materials/MyRedPlastic"
material_prim = stage.DefinePrim(material_path, "Material")
material_prim.CreateAttribute("inputs:diffuse_color", Sdf.ValueTypeNames.Color3f).Set(Gf.Vec3f(1.0, 0.2, 0.2))
material_prim.CreateAttribute("inputs:roughness", Sdf.ValueTypeNames.Float).Set(0.6)
material_prim.CreateAttribute("inputs:metallic", Sdf.ValueTypeNames.Float).Set(0.0)
# 2. 创建材质输出
outputs = UsdShade.Material(material_prim).CreateSurfaceOutput()
outputs.SetConnectedSource(
UsdShade.Shader(material_prim),
"surface"
)
# 3. 获取目标物体并绑定材质
cube_prim = stage.GetPrimAtPath("/World/Cube")
if cube_prim:
binding_api = UsdShade.MaterialBindingAPI(cube_prim)
binding_api.Bind(UsdShade.Material(material_prim))
4.3 修改已有材质的属性
有时候我们不想创建新材质,只想改改现有物体的颜色。比如,让一个红色球变成蓝色。怎么做?直接修改材质属性就行。
# 获取已有材质
material_prim = stage.GetPrimAtPath("/World/Materials/MyRedPlastic")
if material_prim:
# 修改颜色属性
color_attr = material_prim.GetAttribute("inputs:diffuse_color")
if color_attr:
color_attr.Set(Gf.Vec3f(0.2, 0.5, 1.0)) # 改成蓝色
# 修改粗糙度
rough_attr = material_prim.GetAttribute("inputs:roughness")
if rough_attr:
rough_attr.Set(0.2) # 变得更光滑
嗯,这里要注意。修改属性前一定要检查属性是否存在。我曾经因为拼写错误,把 diffuse_color 写成了 diffuse_colour,结果脚本跑完物体还是老样子,排查了半天才发现是拼写问题。
4.4 使用纹理贴图
纯色材质太单调了。真实世界里,木头有木纹,大理石有花纹,这些都需要纹理贴图来实现。在 Isaac Sim 里,纹理贴图本质上就是一张图片文件,我们把它作为材质的一个输入。
我个人建议纹理图片用 PNG 或 JPEG 格式,分辨率控制在 1024x1024 以内,太大反而影响加载速度。
import omni.usd
from pxr import Usd, UsdShade, Sdf
stage = omni.usd.get_context().get_stage()
# 创建带纹理的材质
material_path = "/World/Materials/WoodFloor"
material_prim = stage.DefinePrim(material_path, "Material")
# 创建纹理着色器
shader_path = material_path + "/Shader"
shader_prim = stage.DefinePrim(shader_path, "Shader")
shader_prim.CreateAttribute("info:id", Sdf.ValueTypeNames.).Set("UsdPreviewSurface")
# 设置纹理文件路径(使用相对路径或路径)
texture_path = "./textures/wood_floor_diffuse.png"
shader_prim.CreateAttribute("inputs:diffuseColor", Sdf.ValueTypeNames.Asset).Set(texture_path)
shader_prim.CreateAttribute("inputs:roughness", Sdf.ValueTypeNames.Float).Set(0.8)
shader_prim.CreateAttribute("inputs:metallic", Sdf.ValueTypeNames.Float).Set(0.0)
# 连接材质输出到着色器
material = UsdShade.Material(material_prim)
shader = UsdShade.Shader(shader_prim)
material.CreateSurfaceOutput().SetConnectedSource(shader, "surface")
# 绑定到物体
floor_prim = stage.GetPrimAtPath("/World/Floor")
if floor_prim:
UsdShade.MaterialBindingAPI(floor_prim).Bind(material)
4.5 材质与外观的知识体系
为了让你更直观地理解材质系统的结构,我画了一张图。它展示了从材质创建到最终渲染的完整链路。
4.6 实战:给机器人换皮肤
光说不练假把式。我们结合之前章节的机器人模型,给它换一套金属质感的外观。
def apply_metallic_finish(robot_prim_path):
"""给机器人应用金属质感材质"""
stage = omni.usd.get_context().get_stage()
# 创建金属材质
mat_path = "/World/Materials/RobotMetal"
mat_prim = stage.DefinePrim(mat_path, "Material")
mat_prim.CreateAttribute("inputs:diffuse_color", Sdf.ValueTypeNames.Color3f).Set(Gf.Vec3f(0.8, 0.6, 0.2))
mat_prim.CreateAttribute("inputs:roughness", Sdf.ValueTypeNames.Float).Set(0.3)
mat_prim.CreateAttribute("inputs:metallic", Sdf.ValueTypeNames.Float).Set(1.0)
# 获取机器人所有子部件
robot_prim = stage.GetPrimAtPath(robot_prim_path)
if not robot_prim:
print("机器人不存在")
return
# 遍历所有子 prim 并绑定材质
for child in robot_prim.GetChildren():
if child.IsA(UsdGeom.Mesh):
UsdShade.MaterialBindingAPI(child).Bind(
UsdShade.Material(mat_prim)
)
print(f"已为 {robot_prim_path} 应用金属质感")
# 调用函数
apply_metallic_finish("/World/Robot")
4.7 常见问题与避坑
- 材质不显示? 检查材质路径是否正确,属性名称是否拼写正确。我经常犯的错误是把
diffuse_color写成diffuseColor。 - 纹理加载失败? 确认图片路径是相对于当前工作目录的,或者使用路径。纹理文件不要放在中文目录下。
- 物体看起来太亮或太暗? 调整粗糙度。粗糙度越低,反射越强,看起来越亮。金属度高的物体对光照更敏感。
- 多个物体共享材质? 没问题。同一个材质 prim 可以绑定到多个物体上,修改材质属性时所有物体都会同步变化。
好了,这一章的内容就到这里。材质系统是 Isaac Sim 里比较直观的部分,但也是让仿真效果“以假乱真”的关键。多动手试试不同的参数组合,你会很快找到感觉。