跳到主要内容
腾讯题库 · Q21

原题 Q21 · 当前收录 20 / 49

学习状态 未开始

PBR 贴图命名规范

工具与管线 tooling · naming 对应课程:资产与工具链

面试官问

PDF 第 46 页

如何用 MaxScript 或 Python 编写一个脚本,自动为角色模型生成标准的 PBR 贴图命名规范?(常问题,结合经历)

学习进度

0/9 已勾

只保存在这台浏览器;勾满 9 项即视为本题完成。

先把题目说成人话

一个角色往往带着许多外观图片:有的负责皮肤颜色,有的负责凹凸,有的只控制头发哪里透明。若文件都叫“最终版”“新最终版”,人和引擎都无法稳定判断它属于谁、贴在哪个部位、负责什么。

这题要写的脚本不是生成贴图,而是先识别现有贴图的身份,再生成规范名称。模型上挂一套外观的位置叫材质槽(Material Slot),一张图具体负责什么叫贴图用途(Map Type)。真正执行改名时,磁盘文件和所有仍在寻找旧路径的材质引用必须一起改变,否则角色会立刻丢图。

这是虚构教学故事,不是个人项目经历。

场景:先让问题变得看得见

制作软件(Blender)里有一个角色,身体、头发和盔甲分别使用不同材质。图片却叫“最终版”“新最终版”和“未命名三号”,几张灰色缩略图看起来几乎一样。

(右脑)画面:小明按照旧文件名里的几个字母批量改名。一张名字像法线的图片实际连在粗糙度上,结果被贴错身份;另一张“最终版”完全看不出用途。小红不再猜名字,而是顺着图片后面的连线观察它最终接到材质的哪一个入口:进入颜色入口的叫基础颜色,经过法线转换后进入表面方向入口的叫法线,进入粗糙程度入口的叫粗糙度。工具先铺开一张“旧名字、连接位置、候选新名字”的清单;用途看不清的停下来,两张图想占同一个名字的也停下来。确认以后,一份真实图片只搬动一次,而所有牵着它的材质连线同时跟到新位置。角色重新打开时,脸、头发和盔甲仍然各自找到原来的图片。

(左脑)对表:名称可由 {asset}_{material}_{mapType}[.{UDIM}].ext 组成。用途优先从材质节点连接判断,旧文件后缀只作低可信提示;混合节点、节点组和通道打包无法证明时进入人工复核。同一物理文件可能被多个节点引用,必须先按规范化路径聚合,再生成唯一目标并更新全部引用。执行前检查多源同目标、目标已存在、权限、大小写和跨盘风险,完成后重新打开 DCC,并导入 Unity 验证贴图类型与颜色空间。

小明:先形成一个完整猜测

小明直接用正则把 diff 替换成 basecolor、把 nrm 替换成 normal。结果一张命名错误的粗糙度图被继续认错,HeroA_Body_diff.1001.exr 又与另一张候选基础颜色贴图撞名。文件虽然改了,材质节点仍指向旧路径。失败说明:旧名字不能证明语义,改名也不是一次孤立的字符串替换。

小红:把猜测放回现实检查

小红先让 Blender Python 脚本停在“只分类、不改名”。它真的遍历当前选中模型、材质槽与 Image Texture 节点;用途优先从节点连接判断,无法判定就进入人工确认:

import bpy
from pathlib import Path

SOCKET_TYPES = {
    "Base Color": "basecolor",
    "Roughness": "roughness",
    "Metallic": "metallic",
    "Alpha": "opacity",
}

def linked_map_type(tex_node):
    for output in tex_node.outputs:
        for link in output.links:
            target = link.to_node
            if target.type == "NORMAL_MAP":
                return "normal"
            if target.type == "BSDF_PRINCIPLED":
                return SOCKET_TYPES.get(link.to_socket.name)
    return None  # 间接节点链不能靠文件后缀冒猜

plan = []
for obj in bpy.context.selected_objects:
    asset = obj.get("asset_id") or obj.name
    for slot in obj.material_slots:
        mat = slot.material
        if not mat or not mat.use_nodes:
            continue
        for node in mat.node_tree.nodes:
            if node.type != "TEX_IMAGE" or not node.image:
                continue
            map_type = linked_map_type(node)
            src = Path(bpy.path.abspath(node.image.filepath))
            if not map_type:
                plan.append({"status": "review", "image": node.image, "src": src})
                continue
            udim = ".<UDIM>" if node.image.source == "TILED" else ""
            dst = src.with_name(
                f"{asset}_{mat.name}_{map_type}{udim}{src.suffix.lower()}")
            plan.append({"status": "ready", "image": node.image,
                         "src": src, "dst": dst})

这段代码已经落到 DCC API,但得到的 plan 仍只是“每个节点一行”的发现结果,还故意没有移动文件。执行前先把相对路径、分隔符和路径片段规范化;是否折叠大小写必须服从目标文件系统,符号链接也要有固定解析策略。普通图片以规范化物理路径为键;平铺图片则先展开并校验全部 tile,再以规范化 UDIM 模板和完整 tile 清单组成一个集合键。随后按键合并重复行,聚合所有 Image 数据块、纹理节点、材质和对象引用者。若同一物理文件或 UDIM 集因不同材质语义算出多个目标名,只能进入 review,不能执行多次改名。

聚合结果写入 manifest 后,才检查多个物理来源映射同一目标、目标已存在、只读文件、跨盘移动与所有 review 项。通过后一组物理文件只执行一次“源路径 → 唯一临时路径 → 最终路径”的迁移,并把该组所有 image.filepath 一并更新;UDIM 的每个 tile 分别记录源、目标和哈希,但整组共同提交。任一步失败时,每个物理文件只逆向搬一次,再恢复全部聚合引用。角色名来自资产标识,材质名来自绑定槽,UDIM 占位与扩展名被保留。

我:用检查结果修正模型

我把执行做成两阶段。第一阶段按“规范化物理文件或完整 UDIM 集”聚合所有引用,生成 manifest,并阻断未知用途、同一来源产生多种语义、多源映射同一目标和目标已经存在等冲突。确认后,每组物理文件先搬到唯一临时名,再搬到最终名,避免名称互换或大小写变化造成覆盖;所有引用者同时更新。任一步失败,就按 manifest 反向恢复物理路径和引用。

完整流程是:遍历所选模型的材质槽 → 找纹理节点 → 分类用途 → 规范化物理路径并展开 UDIM 集 → 按物理键聚合引用 → 生成候选名 → 全局检测冲突 → 预览 → 每组一次临时改名 → 更新全部引用 → 每组一次最终改名 → 保存副本并重新打开验证。改名完成后还要把产物导入目标 Unity 项目,确认规则确实触发了正确的色彩空间和材质槽。示例只处理直接连接;经过 Mix、Separate Color 或节点组的图必须扩展图遍历,不能让这段最小代码假装覆盖所有材质。同一文件若把粗糙度、金属度和遮罩装进不同通道,就应识别成一份带通道契约的打包贴图,不能为每条连接各改成一个互相冲突的单用途名字;UDIM 也要枚举、逐 tile 计算哈希并作为整组校验。

目前没有证据证明你写过这款自动命名脚本。本页不能替你补写真实工具经历;它只能作为一套 Blender Python 或 MaxScript 的设计方案。

(右脑)画面修正:再看那几张灰扑扑的图片,改名不再是撕掉旧标签、贴上新标签就走。小红先确认每张图现在连着角色的哪个部位,再把新名字和新的去处一起预演出来。遇到两张图想占同一个名字,她先让它们各自停在不同的临时位置;遇到看不出用途的图,她宁可留下待确认,也不替美术猜。角色重新打开时,脸和盔甲都仍然接着自己的图片。

(左脑)严格对表:严格流程是:遍历选中对象与材质槽 → 沿已支持的节点连接确定单用途或通道打包契约 → 无法证明的用途标记 review → 将普通文件规范化成物理路径键、将平铺图片展开成“规范化模板 + 完整 tile 清单”的 UDIM 集键 → 按键聚合所有 Image 数据块、节点、材质与对象引用 → 用 asset、material、mapType、UDIM、扩展名生成候选路径 → 同一键出现多种语义或目标时转人工,拒绝多源同目标、已存在目标、权限和跨盘风险 → 写出含逐物理文件哈希与全部引用者的 manifest → 每组源文件只改一次唯一临时名并同步全部引用 → 再改最终名 → 保存副本、重新打开并导入 Unity 验证。任一步失败时每个物理文件只逆向恢复一次,再恢复全部引用;只有文件、DCC 引用和 Unity 语义三者一致才通过,真实项目规模与成绩不能由这段教学算法代填。

老师解惑

  • 命名契约:推荐形式可为 {asset}_{material}_{mapType}[.{UDIM}].ext,但项目必须定义唯一词典。
  • 语义来源:节点连到了 Base Color 还是 Normal,比文件原名更可靠;无法确定时要停下来。
  • 两阶段改名:先到临时唯一名,再到最终名,避免交换名称与大小写差异造成覆盖。
  • manifest:记录源、目标、引用者、哈希、规则版本和状态,是恢复与审计依据。

工具分工

  • MaxScript/Python DCC API:读取选中模型、材质槽、Bitmap/File 节点并更新路径。
  • 文件系统层:检查存在性、大小写、权限与跨盘移动;不允许静默覆盖。
  • 规则配置:保存别名字典、目录规则、UDIM 和通道打包约定。
  • Unity 导入检查:验证命名规则是否正确触发 sRGB、Normal Map 等设置。

记忆钩子

先从材质连接认出“它是谁”,再生成名字;改文件和改引用必须是一件可回滚的事。

这是教学场景,不是目标项目的真实捕获或个人经历。

下一步:验证这条因果

贴图旧路径怎样被解析为材质与通道,再生成可审查的新路径、冲突和回滚标识。

教学边界:它不读取真实像素、不验证颜色空间,也不会真的调用 Unity AssetDatabase 写盘。

进入教学 Lab:「TA Tools:把检查变成团队可以信任的反馈」