有些画面之所以让人印象深刻,并不只是“好看”两个字能概括。最近不少玩家在看《明日方舟:终末地》相关演示时,都会提到“潜行视角拉近之后,连布料纹理、地面接缝、金属刮痕都看得清清楚楚”。弹幕里有人调侃,有人惊叹,但真正值得聊的是背后的技术问题:为什么这个画面能“禁得住贴近看”?
如果只看表面,很容易误以为这只是美术团队“画得细”。但从技术角度看,玩家感知到的纹理细节,其实是 PBR 材质流程、贴图种类、纹理过滤、光照环境和渲染管线的综合结果。换句话说,“一潜图连纹理都画出来了”不是单纯的工作量堆砌,而是一整套标准化材质工作流跑通了。
这篇文章不打算复刻游戏本身,而是想拆解这套流程:纹理贴图到底在模拟什么,角色服装材质为什么会有“黑丝、皮衣、金属、布料”完全不同的观感,以及你自己在 Blender、Unity 或 glTF 工具链里,怎么动手搭出一个能“贴近看”的 PBR 材质。读完你会明白,镜头拉近之后画面不穿帮,不是玄学,而是工程问题。
1. 这篇文章真正要解决的问题
很多开发者会把“纹理清晰”简单等同于“贴图分辨率高”。这个理解不能说错,但在实际项目中,决定观感的核心往往不是单一贴图的像素数,而是整套材质参数是否匹配。
举个例子:同样是 2048 分辨率的贴图,如果颜色空间设置错误、法线方向反了、粗糙度贴图没有正确采样,最终画面上看起来可能还不如一张 1024 分辨率的正确材质。尤其当镜头贴近物体时,高光分布、凹凸感、接触阴影这些细节会立刻暴露问题。
这篇文章的主要价值,是帮你建立一套判断画面问题的能力:
- 为什么潜行镜头拉近时,布料织物、地面沙砾、金属刮痕依然有体积感?
- 为什么不同角色服装材质对光照的反应完全不同?
- 为什么有些游戏放大看是“糊成一片”,而有些游戏放大看仍然有层次?
这三个问题的答案,都指向同一个底层逻辑:材质系统不是“贴图 + 颜色”那么简单,而是通过多张贴图,配合光照模型,模拟真实物体的物理属性。
如果你正在做 Unity、Unreal、Godot 或任何支持 PBR 的渲染流程,或者你是刚入行的技术美术、客户端开发,这篇文章会比较适合你。它不会让你立刻成为渲染专家,但能让你理解材质节点中的每一张贴图到底在干什么。
2. 基础概念:PBR 材质与纹理贴图
PBR 是 Physically Based Rendering 的缩写,意思是“基于物理的渲染”。它并不是指画面一定像照片,而是指材质参数遵循统一的光照模型,在不同环境、不同光照角度下,表现保持一致。
传统的 Blinn-Phong 光照模型用高光颜色和光泽度模拟表面,参数直观但不够物理。PBR 则引入了两个核心维度:金属度(Metallic)和粗糙度(Roughness),配合基础颜色、法线、环境光遮蔽等信息,让材质表现更接近真实。
在 PBR 流程里,一张“材质”通常由多张贴图组合而成。下面这张表解释了最常用的贴图类型:
| 贴图类型 | 常见名称后缀 | 作用 | 颜色空间 |
|---|---|---|---|
| 基础颜色贴图 | Albedo / BaseColor / Diffuse | 记录物体表面本身颜色,不含光照信息 | sRGB |
| 法线贴图 | Normal / NormalMap | 通过改变法线方向,让低模表面产生高模的凹凸细节 | Non-Color |
| 粗糙度贴图 | Roughness / Glossiness | 控制高光扩散程度,越粗糙高光越分散 | Non-Color |
| 金属度贴图 | Metallic / Metalness | 区分金属和非金属,金属反射更强 | Non-Color |
| 环境光遮蔽贴图 | AO / Occlusion | 记录物体缝隙间的遮蔽阴影,增加接触感 | Non-Color |
| 高度贴图 | Height / Displacement | 真正改变表面顶点位置,或配合视差贴图模拟凹凸 | Non-Color |
| 透明贴图 | Opacity / AlphaMask | 控制物体哪些区域透明,用于布料网格、头发等 | sRGB 或 Non-Color |
这里最容易误解的是法线贴图。法线贴图并不会真的改变网格形状,它只是在像素着色阶段“欺骗”光照,让平面看起来有凹凸起伏。所以即使模型面数不高,只要法线贴图信息足够,贴近看也能产生明显的材质细节感。
PBR 材质在二次元风格游戏中同样适用,只是往往和 NPR(非真实感渲染)做混合。角色皮肤、头发、服装、金属饰品会使用不同的参数组合:金属高光滑度较高,布料粗糙度较高,头发则可能使用各向异性高光。这也是为什么同样一个角色模型,不同材质区域看起来“质感完全不同”。
3. 角色服装材质:为什么镜头贴近后细节最明显
玩家对角色服装材质的关注度一直很高。尤其是深色织物、皮衣、金属配饰这类材质,在传统 Blinn-Phong 流程下很容易“糊成一坨黑色”,但在 PBR 流程下,粗糙度、高光、法线和 AO 共同作用,让深色材质也能分清亮面、哑光和阴影层次。
以常见的服装材质为例:
- 黑色织物或丝袜:本身反照率很低,几乎不反射环境,但表面有编织纹理和半透明感。为了让它在暗光下还能被看清,通常需要较强的法线贴图细节,配合较高的粗糙度和一定程度的透射效果。
- 皮衣:表面光滑,粗糙度较低,高光范围窄而亮。皮面上的褶皱和磨损痕迹需要法线贴图来增强,否则接近看会显得塑料感过重。
- 金属饰品:金属度通常接近 1,粗糙度较低,会强烈反射环境。如果场景中缺少反射源,金属材质就会显得黑、脏,这是很多新手项目常见的问题。
- 普通布料:编织纹理、缝线、污渍磨损等信息,主要靠 Albedo 贴图画出来,再配合粗糙度贴图区分哑光和反光区域。
为什么“潜行镜头拉近”特别容易暴露材质问题?因为近距离观察时,屏幕上的像素可能只覆盖物体一小块区域,纹理被放大后,贴图过滤方式和 Mipmap 策略会直接影响清晰度。很多引擎默认开启了三线性过滤,但各向异性过滤等级不足时,斜着看地面或衣物会明显发糊。
所以“连纹理都画出来了”这句话,从技术角度翻译就是:这套材质同时满足了高分辨率贴图、正确的法线表现、匹配的高光参数、以及足够好的纹理过滤策略。四个条件缺一个,观感都会下降。
4. 环境准备与工具选择
如果你想自己动手验证 PBR 材质效果,不需要内测资格,也不需要高端显卡。下面这套环境足够跑通流程。
建议工具组合:
- Blender 3.x 及以上:免费开源,节点编辑器适合做 PBR 材质验证。
- Unity 2021 LTS 及以上:方便模拟游戏内效果,也可以直接看材质球。
- Godot 4 或 Unreal Engine 5:都可以,但本文示例以 Blender 和 Unity 为主。
- 贴图工具:Substance Painter 是商用工具,免费替代品可以考虑 Quixel Mixer、ArmorLab,或者直接使用网上免费 PBR 贴图资源。
硬件方面,能正常跑 OpenGL 3.3 或 DirectX 11 的显卡就行。如果你要做 4K 贴图预览,建议显存不小于 4GB,但不强制。
需要注意,版本号不要盲目追求最新。Blender 不同版本之间 Python API 可能有差异,Unity 不同管线之间 Shader 写法也不同。本文提供的代码是通用思路,你实际使用时应以当前项目版本为准。
4.1 环境检查清单
在开始之前,先检查这几项:
- 显卡驱动已更新到较新版本,避免 OpenGL 或 DX 反馈异常。
- Blender 中确认渲染引擎是 Eevee 或 Cycles,两种引擎光照差异很大。
- Unity 项目确认使用内置渲染管线还是 URP,这会决定 Shader 是否能直接运行。
- 准备一套完整 PBR 贴图资源,建议包含 Albedo、Normal、Roughness、Metallic、AO 五张贴图。
5. 核心流程拆解:从贴图到完整材质
下面用一个最小案例,演示如何把一套 PBR 贴图组合成可用的材质。这个流程的核心不是“记住按钮”,而是理解每个节点的作用。
5.1 第一步:准备贴图资源
不论在哪个工具里做材质,第一步都是准备贴图。建议命名规则如下:
character_dress_albedo.png character_dress_normal.png character_dress_roughness.png character_dress_metallic.png character_dress_ao.png命名最好包含“模型名_部件名_贴图类型”三段信息。否则项目变大之后,你根本分不清哪张贴图属于哪个材质。
这里强调一个关键点:Albedo 贴图通常保存在 sRGB 颜色空间,Normal、Roughness、Metallic、AO 全部要标记为 Non-Color(数据纹理)。如果颜色空间设置错误,画面会出现发灰、高光过曝等问题。
5.2 第二步:在 Blender 中创建 PBR 材质
在 Blender 中新建材质后,打开 Shader Editor。核心思路是把 Principled BSDF 节点的输入连接到对应贴图。
Principled BSDF 是 Blender 内置的 PBR 着色器,它把金属度、粗糙度、高光等参数集合在一个节点里,比逐节点手连更方便。它的金属粗糙工作流和 Unity、Unreal 的 PBR 模型基本一致。
5.3 第三步:连接贴图并检查参数
连接顺序如下:
- Albedo 贴图连接 Base Color 输入。
- Normal 贴图先连接 Normal Map 节点,再连接 Normal 输入。
- Roughness 贴图连接 Roughness 输入。
- Metallic 贴图连接 Metallic 输入。
- AO 贴图可以连接 Specular 或单独作为混合因子,也可以在 Eevee 中用于增强接触阴影。
连接完成后,先不要急着进引擎。在 Blender 里的主要目的是确认贴图本身没有方向错误、颜色空间错误、UV 拉伸等基础问题。
5.4 第四步:在引擎中预览
如果你最终目标是游戏,导入 Unity 或 Unreal 后,还需要调整导入设置:
- 贴图分辨率是否超过实际需要的精度?
- 是否生成 Mipmap?
- 各向异性过滤等级设置多少?
- 压缩格式是否适合目标平台?
这四步做完,一个基础 PBR 材质就算跑通了。下面进入代码示例部分,看看具体怎么写。
6. 完整示例代码实现
这一节会给出三个代码示例,分别覆盖 Unity Shader、Blender Python 脚本和 glTF 材质配置。三者服务于同一个目的:用最小代码把 PBR 材质参数落到实际资产上。
6.1 Unity ShaderLab 示例
下面是一个基于内置渲染管线的 Surface Shader,展示了 PBR 材质的常见写法。如果你用的是 URP,建议改用 Shader Graph 或手写 HLSL,但这个示例能帮你理解 PBR 参数映射逻辑。
// 文件路径:Assets/Shaders/ST_PBR.shader Shader "CSDN/ST_PBR" { Properties { _Color ("Base Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _BumpMap ("Normal Map", 2D) = "bump" {} _MetallicGlossMap ("Metallic(R) AO(G) Smoothness(A)", 2D) = "white" {} _Metallic ("Metallic", Range(0,1)) = 0.5 _Glossiness ("Smoothness", Range(0,1)) = 0.5 _AO ("AO Strength", Range(0,1)) = 1.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; sampler2D _BumpMap; sampler2D _MetallicGlossMap; float4 _Color; float _Metallic; float _Glossiness; float _AO; struct Input { float2 uv_MainTex; float2 uv_BumpMap; }; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; fixed4 mg = tex2D (_MetallicGlossMap, IN.uv_MainTex); o.Albedo = c.rgb; o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); o.Metallic = mg.r * _Metallic; o.Smoothness = mg.a * _Glossiness; o.Occlusion = mg.g * _AO; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }这段代码最核心的地方,是把一张 MetallicGloss 贴图的不同通道拆开使用:R 通道管金属度,G 通道管 AO,A 通道管光滑度。这是很多游戏项目里节省贴图采样次数的常用做法。你不需要为每个参数单独准备一张高精度贴图,而是把互不干扰的信息打包到同一张贴图的不同通道里。
注意:这个 Shader 基于内置渲染管线。如果你使用 URP,Surface Shader 不再适用,但“拆通道”的思路完全一样。
6.2 Blender Python 批量创建材质示例
在资产量较大的项目中,手动连接节点效率太低。现在很多团队会写脚本批量创建材质。下面这个 Python 脚本可以在 Blender 中根据贴图路径自动创建 PBR 材质。
# 文件路径:create_pbr_material.py import bpy def create_pbr_material(mat_name, albedo_path, normal_path, roughness_path, metallic_path, ao_path=None): mat = bpy.data.materials.new(mat_name) mat.use_nodes = True nodes = mat.node_tree.nodes links = mat.node_tree.links nodes.clear() output = nodes.new(type="ShaderNodeOutputMaterial") bsdf = nodes.new(type="ShaderNodeBsdfPrincipled") links.new(bsdf.outputs["BSDF"], output.inputs["Surface"]) texture_configs = [ ("Albedo", albedo_path, "Base Color", False), ("Normal", normal_path, "Normal", True), ("Roughness", roughness_path, "Roughness", False), ("Metallic", metallic_path, "Metallic", False), ] if ao_path: texture_configs.append(("AO", ao_path, "Specular", False)) for name, path, socket_name, is_normal in texture_configs: tex = nodes.new(type="ShaderNodeTexImage") tex.image = bpy.data.images.load(path) tex.label = name links.new(tex.outputs["Color"], bsdf.inputs[socket_name]) if is_normal: normal_map = nodes.new(type="ShaderNodeNormalMap") normal_map.inputs["Strength"].default_value = 1.0 links.new(tex.outputs["Color"], normal_map.inputs["Color"]) # 重新连接:Normal 贴图需要经过 Normal Map 节点 links.new(normal_map.outputs["Normal"], bsdf.inputs["Normal"]) return mat if __name__ == "__main__": create_pbr_material( mat_name="ST_TestMaterial", albedo_path="//textures/character_dress_albedo.png", normal_path="//textures/character_dress_normal.png", roughness_path="//textures/character_dress_roughness.png", metallic_path="//textures/character_dress_metallic.png", ao_path="//textures/character_dress_ao.png", )这里有一个常见误区:法线贴图不能直接接到 Principled BSDF 的 Normal 输入,中间必须经过 Normal Map 节点。因为法线贴图存储的是切线空间方向,需要转换为着色器需要的世界空间或切线空间方向。省略这一步,法线方向可能出错,表现就是凹凸感时有时无,甚至阴影扭曲。
6.3 glTF 材质配置示例
如果你想在引擎之外快速验证材质,最常见的跨平台格式是 glTF。下面是一个 PBR 材质 JSON 配置示例:
{ "materials": [ { "name": "CharacterCostume", "pbrMetallicRoughness": { "baseColorTexture": { "index": 0 }, "metallicRoughnessTexture": { "index": 1 }, "baseColorFactor": [1.0, 1.0, 1.0, 1.0], "metallicFactor": 0.0, "roughnessFactor": 0.6 }, "normalTexture": { "index": 2, "scale": 1.0 }, "occlusionTexture": { "index": 3, "strength": 0.8 }, "doubleSided": false } ] }glTF 的金属粗糙度模型和 Blender 的 Principled BSDF、Unity 的 Standard Shader 是同一个体系。这也意味着你在 Blender 里调好的材质参数,导出 glTF 后可以被其他支持 PBR 的工具识别。
7. 运行结果与效果验证
代码写完之后,必须知道“跑通了”是什么状态。
7.1 Unity 中的验证方法
在 Unity 中新建一个材质球,把上面 Shader 拖到材质上,再拖给一个测试模型。打开 Scene 视图后,观察以下几点:
- Albedo 贴图是否让模型显示正确颜色。
- 切换法线贴图开关时,表面是否有明显的凹凸感变化。
- Roughness 和 Metallic 参数从 0 调到 1,高光是否从“扩散”变为“锐利”。
- AO 强度增加时,缝隙和接触面是否出现更明显的阴影。
一个比较实用的验证技巧,是在场景里放置一个高光较强的方向光,然后旋转模型。如果金属部分有清晰的环境反射变化,说明 Metallic 通道生效;如果布料部分在没有光照直射时仍然能看到纹理层次,说明 AO 和法线在起作用。
7.2 Blender 中的验证方法
运行 Python 脚本后,在 Shader Editor 中应该能看到节点已经自动连好。如果节点没有自动连接,优先检查贴图路径是否写错。Blender 的//开头表示相对路径,如果你的脚本放在项目根目录,但贴图在子目录,相对路径可能解析失败。
验证材质是否生效,可以直接切换到 Material Preview 模式,让 Blender 提供一套固定 HDR 环境光,这样不需要手动搭灯光就能看到反射效果。如果画面比预期的暗很多,大概率是 Roughness 贴图输入了 sRGB 颜色空间,导致数据被非线性化处理,粗糙度数值整体偏大。
7.3 判断成功的标准
一套“能贴近看”的 PBR 材质,在验证时至少应满足以下条件:
- 光照变化时,高光的衰减符合材质属性,金属不是“塑料反光”。
- 法线贴图没有导致接缝处出现明显方向错误。
- AO 让接触区域有明确阴影,但不会整块变黑。
- 拉近摄像机后,纹理清晰度仍然能接受,没有出现严重模糊或摩尔纹。
如果失败,第一步不是改参数,而是看控制台报错和贴图导入设置。大多数材质问题都出在颜色空间、通道映射和 UV 接缝这三点上。
8. 常见问题与排查思路
下面这张表整理了 PBR 材质制作中的高频问题,你可以先用它做自检。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 材质整体发灰,颜色不鲜艳 | Albedo 贴图被当成非颜色数据,或贴图颜色空间错误 | 检查贴图导入设置,确认 Albedo 使用 sRGB | 将 Albedo 改为 sRGB 颜色空间 |
| 表面没有凹凸感 | 法线贴图未连接,或贴图采样数据为 0 | 在 Shader Editor 中查看 Normal 节点连接关系 | 确保 Normal 经过 Normal Map 节点并连接 Normal 输入 |
| 法线凹凸方向反了,呈现内凹效果 | 法线贴图 Y 通道方向不一致,或引擎导入设置翻转 | 在引擎中反转法线贴图 Green 通道 | 修改导入设置的 Flip Green Y 选项,或在贴图工具中重新导出 |
| 高光太亮或一片白 | Roughness 贴图没有生效,或贴图颜色空间错误 | 检查 Roughness 是否使用 Non-Color | 将 Roughness 设为 Non-Color,并检查贴图通道 |
| 金属看起来像脏塑料 | Metallic 或 Roughness 参数不匹配,缺少反射环境 | 在场景中增加反射探针或 HDR 环境光 | 合理设置 Metallic,接近金属材质时 Roughness 应较低 |
| 远处纹理闪烁,镜头拉近又模糊 | Mipmap 设置不当或各向异性过滤等级不足 | 查看引擎纹理导入设置 | 开启 Generate Mipmap,设置合适 Aniso Level |
| 镜头拉近后接缝明显 | UV 接缝处理不足,贴图绘制未跨接缝 | 在贴图工具中开启 UV padding,或调整接缝位置 | 使用 Dilation/Inset 处理贴图边框,隐藏接缝 |
排查时最重要的原则是“一次只改一个变量”。不要同时调整颜色空间、粗糙度和金属度,否则你根本不知道哪个改动解决了问题。记录每一步的改动,会让排错效率明显提升。
9. 最佳实践与工程建议
PBR 材质看起来简单,但在项目工程化之后,很多隐藏成本才会暴露。下面这些建议来自常见项目实践,可以直接用于团队协作和个人规范。
9.1 贴图命名与版本管理
材质资产命名一定要结构化。推荐格式:
[模型名]_[部件名]_[贴图类型]_[分辨率] 例如: guard_body_albedo_2k.png guard_body_normal_2k.png guard_body_roughness_2k.png还要用版本管理工具管理贴图资源。盲目保存多个“最终版”“最终版2”会导致贴图引用错乱。建议将贴图名称和材质名称一一对应,任何修改走版本提交,而不是在文件名里加后缀。
9.2 颜色空间规范
在项目初期就定好规则:
- Albedo:sRGB。
- Normal:Non-Color。
- Roughness:Non-Color。
- Metallic:Non-Color。
- AO:Non-Color。
- Opacity:Non-Color 或 sRGB 视具体引擎而定。
这条规则如果不统一,不同美术各自导出的贴图混用后,画面风格会立刻分裂,而且很难回溯。
9.3 性能优化思路
“连纹理都画出来了”不能成为无限堆分辨率的理由。移动端常见做法是:
- 根据物体在画面中的占比动态选择贴图精度。
- 使用 ASTC 或 ETC2 压缩格式减少显存占用。
- 为远处的物体生成更小的 Mipmap,避免高频纹理闪烁。
- 使用纹理串流(Texture Streaming)只加载当前需要的高精度贴图。
在靠近摄像机时显示更高精度贴图,在远处自动切换低精度贴图,才是“贴近看依然清楚,整体跑起来不卡”的工程答案。
9.4 合法使用贴图资产
这是一个容易被忽视的问题:不要直接从其他游戏安装包里提取贴图,也不要在商业项目中使用未经授权的 PBR 贴图。Substance Painter、Quixel、Poly Haven 等平台提供合法贴图资源,使用时注意许可证条款。技术能力再强,版权合规这条线不能碰。
10. 总结与后续学习方向
“一潜图连纹理都画出来了”这句话,从技术视角来看,是一次完整材质工作流的胜利。它包含了高分辨率贴图、PBR 参数匹配、法线贴图表现、AO 接触阴影、纹理过滤和光照环境的协同作用。任何一个环节掉链子,贴近看都会露馅。
如果你刚接触这个方向,下一步可以这样做:打开 Blender,新建一个球体,找一套免费 PBR 贴图,按照第 5 节的流程把节点连起来。先看 Blender 里的效果,再导出 glTF 到任意支持 PBR 的查看器里对比。跑通之后,再尝试把第 6 节里的 Unity Shader 用起来,调整不同参数,观察高光和粗糙度的变化。
再往深走,可以学习 Substrate、角色皮肤次表面散射、头发各向异性高光、移动端纹理压缩这些方向。技术美术这个岗位的价值,恰恰就体现在“能理解材质为什么好看,也能把资源成本控制住”的平衡能力上。这个方向没有捷径,但只要你愿意从一张贴图一个节点开始验证,进步会比自己想象中快很多。