
做 3D 建筑资产的时候真正拉开差距的往往不是建模速度而是拆分思路。同样是做一栋城市住宅楼有的人一次性建一整栋改一个窗户就要删半个墙面有的人把墙、柱、门、窗、屋顶拆成标准零件拿到场景里像拼积木一样组合换风格、改体量、出变体都快很多。这种差别就是模块化建筑资产要解决的问题。这篇文章不是教你怎么把一个单体建筑做得精美而是讲一套可以复用到多个项目的生产规则从模块怎么拆、尺寸怎么定、轴心点放哪到材质怎么规划、碰撞和 LOD 怎么做再到怎么用 Blender Python 脚本批量处理资产。无论你是准备入行场景建模的初学者还是已经在游戏项目里被建筑资源折腾过的开发者这套流程都能帮你减少“做完一栋楼改动量巨大”的尴尬。需要先说明一个判断模块化建筑资产的关键不是建模操作本身而是前期规则设计。如果一上来就建模型不考虑尺寸网格、命名、对齐方式和接口细节拆出来的所谓“模块”在引擎里拼起来一定会有裂缝、重叠、朝向混乱。下面从最容易被忽略的规则开始拆解。1. 模块化建筑资产真正解决的是什么问题模块化建筑资产简单说就是把一栋建筑拆成一组可重复使用、可相互拼接的“标准零件”。在游戏场景、VR 看房、影视预演、数字孪生项目中都很常见。它解决的核心问题有三个。第一是复用。城市里大量建筑由相似的墙体、窗户、阳台、屋顶组成。如果你为每一栋楼单独做全套模型不仅建模成本高贴图、碰撞、LOD、物理参数都要重复调试。模块化之后一套墙体零件可以在几十栋楼里共用材质和碰撞规则只需要维护一份。第二是组合。同一套模块可以通过不同的排列方式产出不同方案。比如设计一个街区的白模阶段用墙、门、窗、阳台模块搭建数版提案比直接手摆一个完整模型灵活得多。模块化思路在关卡白模和产品快速迭代阶段尤其有价值。第三是维护。在实际项目中建筑资产的修改非常频繁门窗尺寸变了、外立面风格调整了、层高要求变了。如果模型是一个整体任何改动都可能牵动整栋楼如果是模块化零件你只需要替换对应模块影响范围被限制在单个资产内。但模块化不是万能的。它适合重复度高的几何建筑、工业厂房、城市住宅、科幻基地这类规划感强的资产不适合独特角色雕像、大面积有机地形、需要无缝雕刻的一体化建筑。盲目把所有资产都拆成模块反而会制造大量“一次性零件”让资源管理更混乱。所以动手前先判断项目里到底有多少重复需求这是第一个要养成的习惯。2. 模块化设计的核心规则与思维模型理解模块化可以借用一套拼装玩具的思维零件接口必须严格一致才能在多个地方通用但零件本身又要有足够的差异度才能拼出丰富的外观。在建筑资产生产中最核心的规则是“网格对齐”。常见做法是设定 1 米或 2 米为基本网格所有模块的长、宽、高都取网格的整数倍。比如墙体宽度取 1m、2m、4m门洞高度取 2m窗台高度取 1m。这样做的好处是当你在引擎里摆放拼接时所有零件都能吸附到同一套坐标格点不会出现 0.01m 级别的误差累积。从实际项目角度看更推荐的网格体系是“基础模块 1m大件模块 2m/4m小件模块 0.5m/0.25m”。0.25m 以下的切分只在必要装饰件上使用否则会让资产数量失控。工业厂房类建筑可以放大到 2m 网格室内精装类可以缩小到 0.5m 网格具体取决于项目规模。轴心点也同样重要。每个模块的轴心点位置必须统一比如统一放在底面中心或者统一放在左下角。这个决定会直接影响引擎里的吸附、旋转和脚本化摆放。如果同一套模块里有的轴心在中心有的在角落程序化生成时会出现严重的偏移。接口设计是模块化最容易出错的地方。两段墙体拼接时不能只是两个平面贴在一起否则接缝处容易漏光、出现细裂缝。通常的做法是让接缝处产生一点点交叠结构比如把一侧边缘向外延伸 5mm~10mm或者在内部做一个插槽。另一个常用方案是故意在接缝处设计装饰条、立柱或遮缝板把接缝变成视觉元素。命名规范是资产复用和管理的地基。没有命名规范一个墙面零件传几手之后谁都不知道“Wall_04”到底代表什么。推荐使用“类型_尺寸_变体_版本”的结构例如名称含义Wall_2x4_A_V012m×4m 墙体A 变体V01 版本Door_1x2_Standard_V021m×2m 标准门V02 版本Window_1x1_Double_V011m×1m 双扇窗V01 版本Corner_Out_1x3_V011m×3m 外墙转角V01 版本这个规范要写进团队资产文档而不是靠个人记忆。只要有两个人参与项目命名规则就应该出现在项目启动会议上。3. 环境准备与软件选择模块化建筑资产的生产链路通常由三部分组成建模工具、材质工具、引擎平台。建模工具常见的是 Blender、3ds Max、Maya材质工具常见的是 Substance Painter、Designer、Quixel Mixer引擎平台常见的是 Unity、Unreal Engine。如果你是从零开始我建议优先用 Blender因为它免费、跨平台、Python 脚本生态成熟而且导出 FBX 的流程足够我们用。不同软件之间的操作习惯差异很大本文不会牵涉复杂版本具体版本请以你当前使用的稳定版为准。重点是理解流程而不是死记快捷键。进入 Blender 后先做三件基础设置。第一设置单位为公制米。默认情况下 Blender 可能使用米作为场景单位但仍然要确认一下Scene Properties Units Unit System选择MetricLength选择Meters。这样导出到 Unity 或 Unreal 时缩放比例不会莫名其妙地变成 100 倍。第二开启网格吸附和增量吸附。建模时按住 Ctrl 或使用工具架上的吸附菜单把绝对网格对齐打开让顶点始终落在整数坐标上。这样后续做脚本化组合时能减少大量的精度问题。第三规划目录结构。推荐在项目根目录下建立Assets/Modular/、Assets/Materials/、Assets/Textures/、Assets/Exports/四个目录。建模文件、贴图、导出文件分开存放避免一个文件夹里堆了几百个文件。环境准备结束后可以先用一个简单的配置脚本把单位、场景属性统一起来这样团队不同成员打开同一个项目时基础环境是一致的。下面是一个最小示例import bpy # 文件路径项目根目录/scripts/setup_scene.py # 在 Blender 的 Scripting 工作区运行 scene bpy.context.scene # 公制单位、米 scene.unit_settings.system METRIC scene.unit_settings.scale_length 1.0 scene.unit_settings.length_unit METERS # 将视图网格设为 1 米一格 for area in bpy.context.screen.areas: if area.type VIEW_3D: for space in area.spaces: if space.type VIEW_3D: # 开启网格显示 space.overlay.show_floor True space.overlay.show_axis_x True space.overlay.show_axis_y True space.overlay.grid_scale 1.0 space.overlay.grid_subdivisions 10 print(场景基础环境已设置)这个脚本不是必须的但它能提醒你一个观点环境统一是自动化生产的第一步。如果每个团队成员打开软件后都要手动调单位、改吸附迟早会出现“我这边的模块是 cm你那边是 m”的对接事故。4. 完整示例制作一面可复用的模块化墙体在模块化建筑里墙是最典型的零件。我们用一个 4m×3m、厚度 0.3m 的墙体作为示例带你走一遍从创建到可拼接的流程。目标不是做得精美而是把规则落地。打开 Blender删除默认立方体使用Add Mesh Cube新建一个立方体。在右侧Item面板里把它改成 4mX、3mZ、0.3mY。这里要注意长宽高数值必须正好是整数尤其是 X、Z 方向。4m 宽、3m 高意味着相邻模块可以沿 X 方向排列后每块墙都能对齐到 1m 网格。接着切换到编辑模式选中上下两个面添加一圈边框并不难但更推荐的做法是先保持立方体的整体比例正确再用“倒角”或“实体化”增加细节。墙体这种基础件最好让每个模块只有一个根物体内部可以有多个子物体但不要一个模块拆成几百个游离 Mesh。之后做碰撞体、导出和版本管理都会轻松很多。为了让插件建模我们可以再做一个门洞版墙体。先复制刚才的整面墙使用Boolean加减一个 1m×2m 的立方体得到门洞。注意布尔运算后一定要执行Apply并且清理多余顶点。这里最容易踩坑的是“布尔后顶点交错”和“门洞周围变成三角面黑洞”。解决方法是做完布尔后进入编辑模式用Mesh Clean Up Merge by Distance合并重叠点再用Normals Recalculate Outside重新计算法线。轴心点统一是模块化资产库里最难手动维护的部分。让我们把刚才做好的墙体底面中心作为轴心点。选中物体后在Object Set Origin里选择Origin to Geometry Bounds把原点放到包围盒中心再手动往下移动到底面即可。但如果有一百个模块这个动作就不该靠手动完成。因此下面的脚本会把所有选中网格物体的原点统一移动到包围盒底部中心。这是做模块资产时最常用的一个工具脚本import bpy # 文件路径项目根目录/scripts/set_origin_bottom_center.py # 使用方式在 3D 视图中选中多个需要调整的物体然后运行脚本 def set_origin_bottom_center(obj): 将物体的原点设置为包围盒底面中心物体视觉位置不变。 bpy.ops.object.select_all(actionDESELECT) obj.select_set(True) bpy.context.view_layer.objects.active obj # 先把原点临时放到几何中心 bpy.ops.object.origin_set(typeORIGIN_GEOMETRY, centerBOUNDS) # 此时原点在包围盒中心物体位置在 obj.location scene bpy.context.scene center obj.location.copy() size obj.dimensions.copy() # 计算底面中心的世界坐标 bottom_center center.copy() bottom_center.z - size.z / 2.0 # 用 3D 游标辅助设置原点 scene.cursor.location bottom_center bpy.ops.object.origin_set(typeORIGIN_CURSOR) # 恢复游标位置 scene.cursor.location (0, 0, 0) for obj in bpy.context.selected_objects: if obj.type MESH: set_origin_bottom_center(obj) print(已批量设置原点到底部中心)运行方式切换到 Blender 的Scripting工作区在Text Editor中新建文本粘贴以上代码点击Run Script。执行后每个选中物体都会以底面中心作为轴心点。这个脚本要放在团队插件库里后面导出、程序化摆放都会依赖它。如果脚本执行后没有反应优先检查是否真的有物体被选中以及当前是否处于对象模式。在编辑模式下运行针对对象的操作往往会报错这是初学者经常忽略的问题。设置完轴心点后还需要给模型一个统一的前方向。推荐的做法是让墙体的“正面”朝向世界坐标的 Y 方向或 Z 方向具体视引擎而定。Unreal 常用 X 为正前Unity 常用 Z 为正前。你不需要修改软件的世界轴但要保证所有模块的“正面”一致。比如统一将建筑立面朝向 Y那么在引擎里摆放时旋转 0° 就代表朝同一方向脚本化生成时不用反复试转角。5. 用 Python 脚本批量重命名与导出 FBX单个模块做完后最重复的工作是命名和导出。手工在 Outliner 里改一百个名字再一个个File Export FBX相当浪费时间还容易漏选或导错文件。重命名逻辑可以写进脚本。模块化资产命名遵循“类型_尺寸_变体_版本”脚本可以做两件事数量前缀检查、批量追加版本号。下面是一个更实用的重命名脚本它会把选中物体中的 Mesh 对象重命名并自动添加_V01后缀import bpy # 文件路径项目根目录/scripts/rename_module.py # 使用方式选中需要重命名的物体运行脚本后会在控制台输出新旧名称 for obj in bpy.context.selected_objects: if obj.type ! MESH: continue old_name obj.name # 已经带版本号则跳过 if old_name.endswith(_V01): continue # 生成新名称去掉特殊字符中的中划线避免引擎不兼容 new_name old_name.strip().replace( , _).replace(-, _) new_name f{new_name}_V01 obj.name new_name print(f{old_name} - {obj.name})有了命名脚本接下来就可以批量导出。在实际项目里导出的文件夹结构也要统一。推荐按模块类型分目录导出例如Exports/Wall/、Exports/Door/。下面的脚本会遍历选中物体把每个物体导出为一个单独的 FBX 文件import bpy import os # 文件路径项目根目录/scripts/export_fbx_batch.py # 使用方式选中所有需要导出的 Mesh 物体修改 out_dir 后运行 out_dir D:/Project/ModularBuildings/Exports os.makedirs(out_dir, exist_okTrue) selected [obj for obj in bpy.context.selected_objects if obj.type MESH] for obj in selected: # 只选中当前物体 bpy.ops.object.select_all(actionDESELECT) obj.select_set(True) bpy.context.view_layer.objects.active obj # 安全文件名 safe_name obj.name.replace( , _).replace(/, _) fbx_path os.path.join(out_dir, safe_name .fbx) bpy.ops.export_scene.fbx( filepathfbx_path, use_selectionTrue, apply_unit_scaleTrue, apply_scale_optionsFBX_SCALE_UNITS, object_types{MESH}, use_mesh_modifiersTrue, add_leaf_bonesFalse, path_modeAUTO ) print(已导出:, fbx_path) print(批量导出完成)这个脚本看起来简单实际使用时有两点需要注意。第一use_mesh_modifiersTrue会把当前应用到物体上的修改器结果烘焙进 FBX如果你的墙体上有倒角、实体化、布尔修改器导出时会自动应用。但这也意味着你必须在导出前确认修改器顺序正确否则错误的结果会被一并烘焙。第二apply_scale_optionsFBX_SCALE_UNITS会根据场景单位进行缩放换算避免进入引擎后模型变得巨大或微小。这个参数在不同 Blender 版本中的默认行为略有差异建议在第一次导出后到引擎里检查一遍比例。导出 FBX 前还要检查物体是否包含非 Mesh 数据灯光、相机、空物体。选中脚本已经使用object_types{MESH}过滤了只导出网格。但如果你需要导出空物体作为挂点比如门窗的开启轴、交互点就需要额外设置。这个属于进阶功能本文先不展开。6. 材质与 UV 规划模块化资产的重复利用模块化建筑的材质规划决定了你的资产是“能用”还是“好用”。一个典型的模块化墙体至少要有基础色、法线、粗糙度、金属度、AO 这几张贴图。把这些贴图组合成材质能让你在引擎里拼贴时保持视觉统一。UV 规划上推荐优先使用可平铺的 Tiling 纹理和一套共享纹理集。比如墙体基础纹理切成 2K 的 Tileable 贴图墙面模块只需要把 UV 对齐到 0~1 区间不需要刻意追求每面墙都占满整张 UV。这样贴图可以在不同墙体之间复用减少纹理数量。但只有 Tileable 贴图还不够建筑外立面通常需要把墙面、窗框、门框、柱子区分开。这时候可以在模型上烘焙一张 ID 图用不同颜色标注不同区域然后导入 Substance Painter 等工具通过 ID 图快速给不同区域分配材质参数。ID 图是黑白或彩色色块不直接出现在最终渲染里但它能极大提高后续材质调整效率。材质命名也要和模型命名保持一致。比如Wall_2x4_A_V01对应的材质命名为M_Wall_2x4_A_V01纹理命名为T_Wall_2x4_A_V01_BaseColor、T_Wall_2x4_A_V01_Normal。这套命名规则看着啰嗦但在资产库几百个文件时检索效率会比“材质1”“贴图2”高很多。在 Substance Painter 中导出输出配置时参数设置会影响后续引擎里的表现下面这些是模块化建筑材质导出时最常见的调整项建议保存为团队预设。如果你不希望每个模块手动调整一遍可以把这些参数保存为 Substance Painter 的导出预设后续所有墙体、门窗模块都调用同一个输出模板。参数项推荐设置原因贴图尺寸2048 或 4096墙面近景多2K 起步远景可用 1K格式PNG 或 TGA兼容性好UE/Unity 都支持颜色空间BaseColor/Emissive 用 sRGB其他用 Linear法线、粗糙度等线性数据不能被当颜色命名模板使用项目命名规范防止引擎导入后名称混乱分辨率分组按近景/中景/远景分组方便生成 LOD 时自动选择贴图如果你只给一套贴图做所有模块会出现远景纹理过重、近景纹理不够细的问题。更合理的方式是一套高分辨率贴图用于主角级建筑一套 1K 或 2K 贴图用于远景厂房。这个决策应该在生产前定好而不是等资产入库后再返工。7. 碰撞体积、LOD 与引擎导入设置模型和贴图做好后还不能直接算完。在游戏和交互场景里碰撞体积和 LOD 是资产能否安全进入引擎的关键。碰撞体积方面最基础的做法是把模块的简单几何体设置为碰撞体。UE 中可以在模型内建UCX_前缀的碰撞体比如UCX_Wall_2x4_A_V01Unity 则会自动生成或使用 MeshCollider。对于墙体、地面这种规则模块简单凸包碰撞就能满足大多数情况对于门窗、立柱等带镂空或起伏的造型可以拆成多个 Box 碰撞体组合避免使用过于复杂的 MeshCollider否则运行时物理计算开销会明显上升。LOD 方面一个模块可以准备三档LOD0 用于近景保留完整细节LOD1 删掉细小装饰如螺栓、凹槽LOD2 只保留大的体块轮廓。Blender 中可以使用Decimate修改器快速减面也可以手动删除小面片。制作完成后需要把 LOD0、LOD1、LOD2 按照引擎约定命名例如Wall_2x4_A_V01_LOD0、Wall_2x4_A_V01_LOD1、Wall_2x4_A_V01_LOD2。导入引擎时的 FBX 设置建议统一为设置项UnityUnreal单位1 Unit 1 Meter1 cmUE 默认但导入时按 FBX 单位换算轴导入后旋转 -90° 通常调到 Z 轴向上Z 轴向上材质勾选 Naming Convention 自动命名导入后重新指定材质不要小看单位设置。Blender 场景单位是米导出 FBX 时如果选了FBX_SCALE_UNITSUnity 中模型尺寸应该是真实米数。如果你发现模型进入 Unity 后放大了 100 倍先检查的是导出设置里是否关了apply_scale_options以及 Unity 的Model Scale Factor是否为 1。这些设置无法用一段代码完整覆盖因为每个引擎版本和项目模板不同。但可以按照“先测试一个模块确认比例和轴向正常再批量导入”的节奏推进不要一口气导入几百个模块才发现方向全乱了。8. 常见问题与排查思路模块化资产制作过程中有几个反复出现的问题下面整理成一张排查表问题现象可能原因排查方式解决方案拼接处出现裂缝接口没有重叠结构边缘刚好贴合在引擎里拉近视角检查接缝让一侧边缘延伸 5mm~10mm或使用遮缝条两段墙重叠闪烁网格处于同一平面深度冲突查看坐标是否完全相等让重叠面错开 1mm或使用 Decal 贴花模型导入引擎后尺寸变成 100 倍单位换算不一致检查 Blender 单位与 FBX 导出设置确认单位是米导出勾选 apply_scale_options轴心点位置不一致程序化摆放混乱建模时没有统一轴心规则用脚本检查所有模块的轴心坐标统一使用底面中心或左下角批量设置门洞布尔后出现黑色坏面法线错误或重叠点多检查法线方向和顶点合并情况重新计算法线Merge by Distance导入后材质是空的FBX 没有嵌入材质路径检查导出时的 path_mode 和材质命名使用统一路径导入或手动指定材质命名不一致导致资产重复缺少团队命名规范查看资产清单补规范使用脚本批量重命名LOD 切换时模型跳动LOD 减面比例过大在引擎里观察远近切换保持外形轮廓一致减少细碎几何移除排查问题时有一个通用顺序先看文件命名再看场景单位再看轴心点最后看材质路径。命名出问题会连锁导致材质、LOD、碰撞体全部错位单位和轴心点出问题会让资产在场景里看起来随机乱飞