ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

3D角色资源制作规范从文档到资产管线的自动化落地实践

3D角色资源制作规范从文档到资产管线的自动化落地实践 简介这是面向3D游戏与动画制作团队的角色资源制作规范文档以《死神》角色为例梳理从模型创建到后期优化的完整标准流程适合3D建模师、角色美术及项目管理者参考。文档涵盖模型面数控制普通角色1500-2000面、BOSS 3000-5000面、UV展开与PSD选区处理、贴图规格tga格式、512*512与共享贴图策略以及角色模型和贴图的拼音命名规则。后期检查部分还涉及单位设置、布线优化、多余点焊接、材质清理、坐标轴归零和光滑组设定等实操细节能帮助团队减少沟通成本、统一交付标准。资源为1个docx文档压缩包大小4.89MB内容结构清晰可直接作为内部培训或流程参考。目前已吸引109人学习适合正在建立或优化3D角色资产管线的团队。1. 《3D角色资源制作规范.docx》不是文档是可执行的资产契约一份写满了规则的角色资源规范最后多半躺在共享盘里没人看。真正能救管线的是让每一行规则都变成软件里跑得起来的断言而不是只能靠人工对照的说教。外包交来的模型命名五花八门、进引擎后角色横躺或者缩放不对、贴图通道错乱导致渲染发黑——这些都不是工具差而是规范没有落到文件、目录、坐标轴、材质路径这些能被机器检查的实体上。下面的拆解从可落地的角度出发把3D角色资源制作规范拆成六块单位与坐标这类硬约束、3ds Max 里的场景检查脚本、命令行与 CI 自动校验、外包验收与进引擎排错最后落到 glTF 检查和模板化沉淀。适合正在建资源库、对接外包、搭中台的工程师也适合想给团队立规矩但不想只靠文档的人。2. 3D角色资源规范里的硬约束单位、坐标、命名与目录结构2.1 单位制和坐标轴先锁死后面导出才不会翻车规范文档里废话最多的三个地方通常是单位、坐标和命名因为它们写出来只有一行但错了影响整个管线。3ds Max 默认的单位体系是英寸很多美术从 Maya 或 Blender 导模型进来时没有先统一 System Unit结果一个 170cm 的角色在引擎里变成 4 米高或者导入时被自动缩放 25.4 倍。这类问题在验收阶段最难查因为模型在 DCC 里看是正常的。我一般会把单位制和坐标轴写成两条死规则源文件统一用厘米1 unit 1 cm角色原点放在脚底中心坐标轴则按“源文件跟随 DCC 习惯、交付物跟随目标引擎”来处理。原因很简单3ds Max、Blender、Unreal 都是 Z-up而 Maya 和 Unity 是 Y-up强行让所有软件用一个轴向只会让绑定和动画工具链更别扭。真正要管的是导出那一环。工具 / 引擎默认 Up Axis导入导出时的常见处理3ds MaxZ-up导出 FBX 时按目标引擎设置 Up AxisMayaY-up角色源文件常按 Y-up 制作BlenderZ-up导出 glTF 会自动转成 Y-upUnityY-up导入 FBX 时可勾选坐标转换选项UnrealZ-up更希望源文件直接以 Z-up 交付容易忽略的点是glTF 内部固定是 Y-up 右手系而 FBX 内部默认也是 Y-up3ds Max 导出时如果不显式选 Up Axis很多版本会按场景设置写。规范里最好不要写“角色面向 Z 轴正方向”这种一刀切的话因为每个引擎的摄像机朝向约定不同。把这条留给导出配置去管DCC 源文件反而稳定。2.2 根目录与文件夹层级一条路径就能看懂角色角色资产不是一堆散文件而是一个有结构的目录。目录结构的作用是让脚本能找到模型、贴图、骨架和校验报告而不是靠人肉去翻。下面是我常用的模板assets/characters/CH001_Elf/ ├── source/ │ ├── CH001_Elf_v01.max │ └── CH001_Elf_Tpose.fbx ├── meshes/ │ ├── CH001_Elf_LOD0.fbx │ └── CH001_Elf_LOD1.fbx ├── textures/ │ ├── CH001_Elf_BaseColor_2048.png │ ├── CH001_Elf_Normal_2048.png │ └── CH001_Elf_ORM_2048.png ├── rig/ │ └── CH001_Elf_Skeleton.fbx └── meta/ └── CH001_Elf_check.jsonsource 只放 DCC 源文件meshes 放导出网格textures 放最终贴图rig 放绑定和骨架meta 放脚本产生的校验输出。角色 ID这里是 CH001全局唯一所有命名都挂在它后面外包交付后按这个结构搬进来脚本才能把贴图和模型自动配对。注意 source 目录不要出现 v02_final_2024 这种文件名版本交给版本库管文件名保持稳定。2.3 命名规范从文件名就能算出 LOD 和用途命名规范最容易写成纸上谈兵。真正有用的命名是能从文件名反推出角色 ID、平台、LOD 级别和贴图用途的。下面这个规则在 glTF 和 FBX 资产里通用片段取值示例说明角色前缀CH_Character 统一前缀角色 ID001全局唯一分配给每个角色人物类型F / M / Cre女 / 男 / 生物LOD 级别LOD0 / LOD1高模、中模、低模部件Body / Head / Hair影响贴图与蒙皮权重区块贴图用途BaseColor / Normal / ORM对应 PBR 材质通道尺寸2048贴图宽高像素整个文件名拼出来是CH_001_F_LOD0_Body_BaseColor_2048.png。配合正则可以直接让机器检查import re NAME_RE re.compile( r^CH_\d{3}_[FM]_LOD[0-3]_\w_ r(BaseColor|Normal|ORM)_\d{4}\.png$ ) print(bool(NAME_RE.match(CH_001_F_LOD0_Body_BaseColor_2048.png)))这个正则里\d{3}锁死角色 ID 是三位数[FM]限定人物类型LOD[0-3]对应四级 LOD最后一段限定贴图通道和尺寸。写规范时把这段正直接在文档里美术和外包自己就能预检不用等上传后被打回。文件名统一全小写下划线禁止空格和中文字符这是为了避免跨平台和跨引擎的编码问题。2.4 多边形预算分平台定LOD 不是美术随缘是配额3d 建模团队最常问的问题就是三角面预算。预算不是拍脑袋的数值它是根据目标帧率和渲染负担算出来的配额。角色模型的核心原则是LOD0 是质量上限LOD2 是移动端或低配机的兜底中间按比例递减。平台LOD0 三角面LOD1LOD2单张贴图上限移动端25k - 40k40%20%1024桌面端60k - 120k50%20%2048VR / AR50k - 90k40%15%2048影视预渲染不限需面数报告——4096这些数值是常见经验值具体项目可以浮动。关键是预算要写成分级的、可查的表格而不是一句话。影视预渲染虽然不限面数但依然要报告否则下游的灯光和渲染合成没法估算资源。预算表最终会变成第 4 章 rules.json 里的阈值所以从规范成立第一天起就要让数值是机器可读的。3. 把3D角色资源规范写进3ds Max场景检查脚本与导出参数3.1 一个检查脚本单位、命名、网格数量一次跑完在 3ds Max也就是常说的 3d max里建规范最省事的方式是写一个运行在场景里的检查脚本。美术不用记规则跑一遍就知道哪里不合格。下面这段用 pymxs 写的脚本检查三个硬性指标系统单位是否是厘米、可渲染网格的命名是否符合正则、场景里有多少个可渲染体。from pymxs import runtime as rt import re NAME_PATTERN re.compile( r^CH_\d{3}_[FM]_LOD[0-3]_\w_[A-Za-z]$ ) UNIT_OK Centimeters def check_scene(): errors [] unit_type rt.Units.SystemUnitType if str(unit_type) ! UNIT_OK: errors.append(fsystem unit {unit_type}, expects {UNIT_OK}) mesh_count 0 for node in rt.rootNode.Children: if rt.isKindOf(node, rt.GeometryClass) and node.renderable: mesh_count 1 if not NAME_PATTERN.match(node.Name): errors.append(fbad name: {node.Name}) return errors, mesh_count if __name__ __main__: errors, count check_scene() print(frenderable meshes: {count}) for e in errors: print([FAIL], e)代码逻辑分三层第一层通过rt.Units.SystemUnitType读取系统单位第二层遍历rt.rootNode.Children顶层节点第三层用isKindOf(node, rt.GeometryClass)配合renderable属性过滤出真正会渲染的网格。命名正则的规则和 2.3 节一致。需要注意SystemUnitType的返回值在不同语言版本的 3ds Max 里可能显示为中文比如“厘米”所以规范里最好做一个别名映射把“Centimeters”、“#Centimeters”、“厘米”都视为通过否则非英文版的美术会被误报。3.2 UV 通道和贴图坐标防止多余的通道残留“3d max 删除 uv”是搜索量很高的问题原因很现实从高模烘焙进低模时DCC 会自动生成一堆额外 UV 通道导出到游戏引擎或 glTF 后通道映射就乱了。实时角色资产一般只留 UV0如果需要 Lightmap 才允许保留 UV1其余通道必须清理。检查脚本里可以加这样一段def check_uv_channels(node): num_maps rt.meshop.getNumMaps(node.mesh) extra [i for i in range(1, num_maps) if i ! 1] if extra: return f{node.Name}: extra uv channels {extra} return None这里meshop.getNumMaps返回该网格的贴图通道数量通道 1 通常是 UV0不同 DCC 的索引习惯略有差异。如果角色不需要 Lightmapextra里列出的通道都应该删掉。删除时先在修改面板里把 UV 修改器塌陷再一次性移除不然有些版本会在塌陷时把通道重新生成回来。规范里要把这条写成白名单制默认只有 UV0额外通道需要负责人确认用途。这条规则特别适合放进自动检查因为人工在 3ds Max 里看不出多余通道只有导出后才出问题。3.3 贴图资源规范尺寸、色彩空间和文件名后缀贴图规范管三件事尺寸、色彩空间、格式。角色贴图通常有 BaseColor、Normal、ORMAO/Roughness/Metalness三种核心通道后缀不一致会导致 glTF 的材质解析错位。贴图默认尺寸色彩空间后缀示例BaseColor2048sRGB_BaseColor_2048.pngNormal2048Linear_Normal_2048.pngORM2048Linear_ORM_2048.png细节/次表面1024sRGB 或 Linear_Detail_1024.png色彩空间写错是渲染发灰的头号原因Normal 和 ORM 是线性数据放进 sRGB 通道会被自动提亮。检查贴图尺寸最直接的方式是用 ImageMagick 批量扫identify -format %f %wx%h %[channels]\n \ CH001_Elf_BaseColor_2048.png正常输出是CH001_Elf_BaseColor_2048.png 2048x2048 srgba。如果 BaseColor 贴图多出 alpha 通道而角色不需要透贴应该导出时去掉否则某些移动端引擎会按带透明通道的格式分配额外内存。格式层面我建议统一 PNG避免 TGA 和 TIF 进入资产库因为 glTF 和 Web 管线对 PNG 的支持最稳定。3.4 导出 FBX 与 glTF在导出面板上把规范按住导出参数的坑比建模更隐蔽。同一份角色资源在 3ds Max 里正常导出到 Unity 就横躺导出到 Unreal 就缩小导成 glTF 进 three.js 又反转。问题几乎都出在 Up Axis 和单位换算上。我一般会在规范里附一张参数对照表导出项Unity 目标Unreal 目标glTF 目标Up AxisY-upZ-upY-up单位基准CentimetersCentimetersMeters动画烘焙有动画才勾有动画才勾有动画才勾嵌入贴图不嵌入不嵌入建议外挂 PNGglTF 的特殊性在于格式内部固定使用米作为单位。3ds Max 场景里角色是 170cm导出 glTF 时如果导出器不做换算进 three.js 后模型尺寸就会和预期差 100 倍很多免费的 3D 人物模型 glTF 文件打开后奇大或奇小根因就在这里。规范里要写清楚源文件统一保持厘米导出 glTF 前先缩放到米或者在导出器里设置好单位换算。这条不写后续每个用 Web 展示的环节都会踩一遍。4. 用命令行和 CI 把3D角色资源检查跑起来4.1 先看现状assimp info 快速读一个 FBX做自动校验的第一步是不打开 3ds Max 就能看到资源的关键信息。assimp 是现在最常用的模型加载库自带的 info 命令可以快速列出 FBX/OBJ/glTF 内部的网格数、顶点数、材质数assimp info assets/characters/CH001_Elf/meshes/CH001_Elf_LOD0.fbx输出里会包含类似这样的字段Mesh count、Vertex count、Face count、Material count、Texture 列表。把 Texture 路径和你规范的目录结构一对比缺失贴图立刻能查出来。assimp 的优势是零 DCC 依赖任何操作系统都能跑用来做资源入库前的快速预检非常合适。缺点是它只读几何和基本材质读不出骨架动画细节骨骼相关校验还是要靠 Blender headless 或专用 SDK。4.2 写一个 Python 校验脚本命名、贴图引用、网格数量一起查assimp info 适合人眼看但规范要落地必须做成程序。下面这个脚本用 pygltflib 直接读 glTF把三角面数量、贴图是否存在、网格是否为空全部检查一遍import json, sys from pathlib import Path import pygltflib def load_rules(pathrules.json): with open(path, encodingutf-8) as f: return json.load(f) def check_gltf(filepath, rules): gltf pygltflib.GLTF2().load(filepath) errors [] if len(gltf.meshes) 0: errors.append(gltf has no meshes) total_tris 0 for mesh in gltf.meshes: for prim in mesh.primitives: if prim.indices is not None: count gltf.accessors[prim.indices].count total_tris count // 3 max_lod rules[max_tris_per_lod][0] if total_tris max_lod: errors.append(ftris {total_tris} {max_lod}) for img in gltf.images: uri img.uri or if not uri.startswith(data:): p Path(filepath).parent / uri if not p.exists(): errors.append(fmissing texture: {uri}) return errors, total_tris if __name__ __main__: errors, tris check_gltf(sys.argv[1], load_rules()) print(ftris{tris}) for e in errors: print([FAIL], e)prim.indices是索引缓冲accessors[prim.indices].count是索引总数除以 3 就是三角形数量。gltf.images里uri如果是相对路径就按当前文件所在目录去查找贴图如果是以data:开头说明贴图内嵌在 glb 里不参与外部文件检查。脚本不直接硬编码预算而是从 rules.json 里读取这是因为预算会随项目迭代调整改成配置文件后改预算不需要动代码。rules.json 长这样{ max_tris_per_lod: [80000, 32000, 16000, 6000], texture_sizes: [1024, 2048], naming_prefix: CH_ }把阈值从代码里拆出来还有一个好处美术在评审时改了预算git 历史里能看到每次调整的记录和理由。规范不再是静态文档而是一份跟随项目演进的配置文件。4.3 把校验挂进 Git 钩子或 CI脚本写好后把它挂进 CI 是最顺理成章的事。GitLab CI 配置可以写成asset-check: stage: test script: - pip install --quiet pygltflib - python scripts/check_asset.py --rules rules.json --input assets/characters artifacts: paths: - check_report.json expire_in: 1 week这样做的好处是角色资产在和主线合并之前就会被机器拦住不合格的提交。美术不需要记得跑脚本CI 会自动把[FAIL]信息反馈到合并请求里。对于还在用 Perforce 或者 SVN 的团队常见做法是在服务端加 pre-commit 触发器或者用定时任务扫描共享目录。触发器的维护成本比 CI 高但思路一样让机器最先看见问题。5. 3D角色资源验收实务外包交付、扫描资产与进引擎排错5.1 一张可直接发给外包的验收清单规范最终要面对外包所以验收清单必须能直接发出去并且让外包照着逐条自查。表里的每一条都要有明确通过标准不给“差不多”留余地检查项通过标准打包目录严格按资产目录模板不允许散文件模型命名匹配命名正则单位DCC 内 1 unit 1 cm角色原点在脚底三角面不超过预算LOD0-2 齐全UV 通道只保留 UV0或白名单内 UV1贴图全部 PNG尺寸和色彩空间符合规范材质命名与部件一致不出现 Material_001骨架骨骼前缀统一根骨骼唯一T-Pose交付 T-Pose 绑定文件动画单独发送这条清单本身就是规范文档的简化版外包在交付前跑一遍能省掉至少一轮返工。需要注意的是清单里不要写“模型质量要好”这种主观词每一项都要落到可检查的数值和命名上。5.2 骨架和 T-Pose绑定交付的硬指标骨架是角色资产里最容易被忽略的部分因为它在 DCC 里看不到明显问题进引擎后动画一播放就全暴露了。通常要锁死三条根骨骼唯一命名统一前缀T-Pose 作为绑定基准。移动端项目里骨骼数量最好控制在 60 根以内带手指的角色可以放宽到 80-100 根但必须写明上限。T-Pose 和 A-Pose 的选择一直是争议点但规范里必须选定一个。T-Pose 的双臂水平做动画匹配和IK解算更稳定A-Pose 适合蒙皮初期但对动画环节不友好。常见做法是要求外包交付 T-Pose 的绑定文件动画文件另外导出并且导出时不要勾选 “Bake Animation”否则绑定姿势会被烘焙进骨骼层级后续动画拼接会出现整体位移。5.3 结构光扫描与点云资产怎么并入规范角色资产不一定都从建模开始用 3d 结构光相机或激光扫描真实演员也是常见路径。扫描出来的原始数据先是 3d 点云要进动画管线必须经过一套固定流程第一去除飞点和孤立簇这些噪声会让后续重拓扑变得极不可控第二抽稀到接近目标面数的点云密度第三重拓扑成四边面把高模细节烘焙到 Normal 贴图第四清理非流形边和自交面模型必须水密。点云本身通常不进最终资产库最终资产必须有明确的拓扑、UV 和骨骼。另外现在流行的 3D 高斯泼溅也就是 3D Gaussian Splatting用来做展厅级预览效果很好但它不是动画资产不能进角色管线的绑定和动画环节规范里要单独把它划分为另一种交付物。这条边界不划清后续接手的绑定师会非常痛苦。5.4 进引擎后“坏资源”的快速定位角色进引擎后出现的问题绝大多数集中在四类现象。第一材质变紫或变粉这是贴图路径断了跑一遍 assimp info 看 Texture 路径就能定位第二角色横躺Up Axis 不对重新指定导入轴向第三脸黑或法线发暗Normal 贴图被当成 sRGB 导入改了色彩空间就恢复第四动画播放后人物整体飘移多半是根骨骼或 T-Pose 没有对齐。如果团队里没有 3ds Max 授权可以用 Blender headless 模式抽贴图信息blender -b scene.blend -P dump_textures.py在 dump_textures.py 里遍历bpy.data.images把贴图名和文件路径打印到 stdout。这条命令的好处是可以在没有 GUI 的服务器上跑适合把资产检查集成到 CI 里。诊断结果应该回流到规范文档的“常见问题”章节外包在交付前就能自己跑一遍问题不再只靠人肉抽查。6. 把规范沉淀成角色模板glTF 检查与运行时侧验证6.1 用 gltf-transform 做导出后的二次检查从 3ds Max 导出的 glb 文件在进资源库之前我习惯再用 gltf-transform 做一次独立检查。它不依赖 DCC 环境直接分析 glTF 二进制里面的网格、材质和纹理信息npx gltf-transform inspect assets/CH001_Elf_LOD0.glb npx gltf-transform optimize \ assets/CH001_Elf_LOD0.glb \ assets/CH001_Elf_LOD0.opt.glb \ --compress draco \ --texture-compress webpinspect 的输出会列出 glTF 里的网格数、accessor 数量、材质数量和纹理列表正好对应规范里的各类阈值。optimize 的--compress draco把顶点数据压缩--texture-compress webp进一步减小贴图体积适合移动端和 Web 端。需要注意不同版本的 gltf-transform 参数写法略有差异具体以npx gltf-transform --help为准。Normal 贴图尽量不要用有损压缩压得太狠否则光影过渡会出现明显的块状瑕疵。6.2 在 three.js 的 GLTFLoader 里挂一层健康检查资源进了引擎不代表万事大吉。在 three.js 的加载回调里加一道检查能把材质缺失这类问题在运行时立刻暴露出来import { GLTFLoader } from three/examples/jsm/loaders/GLTFLoader.js; const loader new GLTFLoader(); loader.load(/assets/CH001_Elf_LOD0.glb, (gltf) { const meshes gltf.scene.children.filter((c) c.isMesh); if (meshes.length 0) { console.warn([asset-check] no meshes in gltf); } const noMat meshes.filter((m) !m.material); if (noMat.length) { console.warn([asset-check] ${noMat.length} mesh with missing material); } console.log([asset-check] meshes${meshes.length} anims${gltf.animations.length}); });这段代码检查两个点场景里有没有空网格以及网格是否缺少材质。gltf.animations.length输出动画数量方便确认导出时动画是否完整。这类运行时日志比美术人工看渲染结果要快得多尤其适合在自动化的页面冒烟测试里收集资产健康度。6.3 把“规范模板”放进共享目录新角色一落地就校验规范最终级的落地方式是让新角色目录一创建就自动进入检查流程。常见做法是在资产共享目录上挂一个目录监听用 inotifywait 监控新文件落盘inotifywait -m -r -e moved_to assets/characters \ --format %w%f | while read file; do python scripts/check_asset.py --input $(dirname $file) done-e moved_to监听新文件被移动或复制进来的事件--format %w%f输出完整路径脚本收到路径后自动对所在角色目录执行校验结果追加写入meta/check_report.md。这样从外包交付到资产入库中间不再需要人工催着跑脚本每个角色目录里的报告文件本身就是验收凭证。规范文档里写的每一条规则到这里才算真正变成了管线的一部分。本文还有配套的精品资源点击获取
返回列表