ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Unity材质渲染进阶:遮罩控制凹凸高光技术详解

Unity材质渲染进阶:遮罩控制凹凸高光技术详解 1. 项目概述与核心价值在Unity3D的材质渲染世界里我们常常不满足于基础的颜色和纹理。当你想让一个生锈的铁门既有金属的光泽又有锈蚀的粗糙感或者想让一件皮夹克在特定区域比如缝线处有更强的反光而其他区域则保持哑光质感时一个单一的、均匀的高光强度就显得力不从心了。这正是“带遮罩的凹凸高光材质”大显身手的地方。简单来说它允许你通过一张额外的“遮罩”纹理像指挥家一样精确地控制模型表面不同区域的高光强度和凹凸细节的强弱从而实现极其丰富和真实的表面质感。这个技术点听起来有点专业但它的应用场景无处不在。无论是游戏开发中的角色装备比如磨损的盔甲、油污的机械、场景物件比如湿漉漉的砖墙、斑驳的油画还是VR/AR应用中需要高度真实感的模型都离不开它。它解决的就是“如何用最经济的资源一张遮罩图为模型表面赋予非均匀的、符合物理直觉的复杂视觉细节”这个核心问题。如果你正在为你的模型质感单一而苦恼或者想从“能看”提升到“好看”甚至“惊艳”的级别那么掌握这套材质实现方法将是你的必经之路。2. 核心思路与方案设计拆解2.1 技术方案选型Shader Graph vs 手写Shader实现带遮罩的凹凸高光效果主要有两条技术路径使用Unity官方的可视化工具Shader Graph或者直接编写Surface Shader或URP/Lit Shader。选择哪种取决于你的项目管线、团队技能和个人偏好。对于绝大多数开发者尤其是刚接触Shader或追求快速原型验证的团队Shader Graph是首选。它的优势在于直观、迭代快、不易出错。你通过节点连线就能构建复杂的材质逻辑实时预览效果无需编译等待。这对于实现我们“遮罩控制凹凸和高光”的需求来说简直是量身定做。你可以轻松地将法线贴图、高光遮罩贴图、平滑度遮罩贴图通过乘法或混合节点连接到主着色器的相应输入端口整个过程像搭积木一样清晰。而手写Shader通常是基于URP的Lit Shader或自定义Surface Shader则提供了无与伦比的灵活性和性能微调能力。你可以深入到每一个计算步骤实现一些Shader Graph暂时不支持或效率不高的特殊效果。例如你可能需要根据遮罩值进行非常复杂的数学变换或者将遮罩同时用于控制高光、自发光、边缘光等多个通道。如果你对HLSL/GLSL语言熟悉并且项目有极致的性能或效果要求这条路值得挑战。注意对于从SolidWorks等CAD软件导入的模型其UV通常是为工程制图准备的可能不适合直接贴图。在Unity中需要重新展UV或使用三平面投影等技术来处理纹理否则你的遮罩图可能无法正确映射到模型表面。2.2 资源管线规划纹理的创建与优化无论选择哪种技术路径资源是基础。一个典型的带遮罩的凹凸高光材质包通常包含以下纹理Albedo (漫反射贴图)基础颜色和图案。Normal Map (法线贴图)提供表面凹凸细节的视觉信息。Mask Map (遮罩贴图)这是核心。通常是一张RGBA四通道图每个通道存储不同的遮罩信息例如R通道控制高光强度G通道控制平滑度B通道控制凹凸强度A通道可能控制自发光或细节遮罩。为了节省资源也常使用灰度图作为单一功能的遮罩。可选的Specular/Metallic Map在高光工作流或金属工作流中定义表面的高光颜色或金属度。在制作遮罩图时通道复用是关键技巧。不要为每一个属性都单独制作一张纹理。例如你可以用Photoshop或Substance Painter将高光区域如金属亮边画在红色通道将需要高度平滑的区域如漆面画在绿色通道然后输出为一张.png或.tga格式的RGB图。在Shader中通过采样这张图并分离其.r、.g、.b分量就能分别控制不同属性。实操心得在Substance Painter中绘制遮罩是最佳实践。你可以利用智能材质和生成器基于模型的曲率、环境光遮蔽、位置等信息自动生成高质量的遮罩效率远高于手绘。导出时务必在Unity的纹理导入设置中根据用途正确设置纹理类型如法线贴图、默认等和压缩格式以平衡画质和内存占用。3. 使用Shader Graph实现核心材质3.1 创建基础材质框架首先在Unity中创建一个新的Shader GraphURP环境下。初始的PBR Master节点已经为我们提供了大部分所需的输入接口Base Color, Normal, Metallic, Smoothness等。我们的任务就是将遮罩纹理的信息正确地“注入”到这些输入中。创建纹理属性在Blackboard中创建三个Texture2D类型的属性分别命名为_Albedo、_NormalMap、_MaskMap。将_MaskMap的默认值设为你准备好的遮罩纹理。采样纹理在图形区域创建三个Sample Texture 2D节点分别连接到这三个属性。确保_NormalMap的采样节点其Type设置为“Normal”这样Unity会自动进行正确的解包计算。基础连接将_Albedo的采样结果直接连接到Master节点的Base Color。将_NormalMap的采样结果连接到Master节点的Normal。至此一个基础的、带法线贴图的材质框架就搭建好了。接下来是融入遮罩控制的关键步骤。3.2 集成遮罩控制高光与平滑度假设我们的遮罩图_MaskMap使用红色通道(R)控制高光强度绿色通道(G)控制平滑度。这意味着遮罩图中越亮值越接近1的红色区域高光越强越亮的绿色区域表面越光滑。分离通道从_MaskMap的采样节点后连接一个Split节点。这个节点会将RGBA四个通道的值分别输出。控制高光Specular在URP的Lit着色模型中高光强度通常由Metallic或Specular输入与一个强度系数共同决定。我们可以创建一个Vector1属性_SpecularIntensity作为全局高光强度调节器。然后进行如下计算使用一个Multiply节点将Split节点输出的R高光遮罩与_SpecularIntensity相乘。这样全局强度被遮罩图局部调制。将相乘的结果连接到Master节点的Metallic输入如果你使用金属工作流或用于计算Specular Color如果你使用高光工作流。对于初学者连接到Metallic是一个简单有效的起点因为金属度直接影响高光反射的强度。控制平滑度Smoothness平滑度控制高光区域的大小和锐利度。同样创建一个_SmoothnessIntensity属性。使用Multiply节点将Split节点的G平滑度遮罩与_SmoothnessIntensity相乘。将结果连接到Master节点的Smoothness输入。通过以上连接你的材质就已经实现了通过一张纹理的两个通道分别独立控制模型表面各处的高光强度和平滑度。你可以通过调节_SpecularIntensity和_SmoothnessIntensity滑块快速预览不同整体效果。3.3 使用遮罩控制凹凸强度有时你希望模型某些区域如干净区域的凹凸感弱一些而另一些区域如污损区域的凹凸感强一些。这可以通过遮罩控制法线贴图的强度来实现。法线强度调节法线贴图采样出来的是一个三维向量。直接乘以一个标量强度值是不规范的。正确的方法是使用Normal Strength节点在Shader Graph中搜索。连接遮罩创建一个_BumpIntensity属性作为基础凹凸强度。将Split节点的B通道假设蓝色通道用于凹凸遮罩与_BumpIntensity相乘得到一个受遮罩影响的实时强度值。应用强度将_NormalMap的采样结果连接到Normal Strength节点的In输入将上一步计算出的实时强度值连接到Strength输入。最后将Normal Strength节点的输出连接到Master节点的Normal输入。注意事项Normal Strength节点的Strength输入为0时表示无法线效果平面为1时表示原始法线贴图强度。使用遮罩控制时要确保遮罩图B通道的值范围0到1与你期望的强度变化范围匹配。你可以通过一个Remap节点来重新映射这个范围例如将 [0, 1] 映射到 [0.2, 1]以保证即使在没有遮罩的区域也有最基础的凹凸感。4. 手写ShaderURP实现详解对于需要更深层次控制或学习Shader原理的开发者我们来看一下在URP框架下如何通过手写Shader代码实现上述功能。我们将创建一个简单的URP Lit Shader。4.1 Shader属性与变量声明Shader Custom/MaskedBumpSpecular { Properties { [MainTexture] _BaseMap(Albedo, 2D) white {} [MainColor] _BaseColor(Base Color, Color) (1,1,1,1) _NormalMap(Normal Map, 2D) bump {} _NormalScale(Normal Scale, Range(0, 2)) 1.0 _MaskMap(Mask Map (R:SpecIntensity, G:Smoothness, B:BumpMask), 2D) white {} _SpecularIntensity(Specular Intensity, Range(0, 2)) 1.0 _SmoothnessIntensity(Smoothness Intensity, Range(0, 1)) 0.5 // 其他URP需要的属性如_Cull, _Blend等可根据需要添加 } SubShader { Tags { RenderTypeOpaque RenderPipelineUniversalPipeline } LOD 300 Pass { Name ForwardLit Tags { LightModeUniversalForward } HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _ADDITIONAL_LIGHTS #include Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl #include Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float2 texcoord : TEXCOORD0; }; struct Varyings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; float3 normalWS : TEXCOORD1; float3 tangentWS : TEXCOORD2; float3 bitangentWS : TEXCOORD3; float3 positionWS : TEXCOORD4; }; TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); TEXTURE2D(_MaskMap); SAMPLER(sampler_MaskMap); CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; half4 _BaseColor; half _NormalScale; half _SpecularIntensity; half _SmoothnessIntensity; CBUFFER_END这部分代码定义了Shader在材质面板上显示的属性并声明了对应的变量。注意_MaskMap的注释明确了每个通道的用途这是一个好习惯。4.2 顶点着色器与法线变换顶点着色器负责坐标变换和计算世界空间的切线空间基向量TBN矩阵这是正确采样和应用法线贴图的基础。Varyings vert(Attributes IN) { Varyings OUT; VertexPositionInputs vertexInput GetVertexPositionInputs(IN.positionOS.xyz); VertexNormalInputs normalInput GetVertexNormalInputs(IN.normalOS, IN.tangentOS); OUT.positionHCS vertexInput.positionCS; OUT.positionWS vertexInput.positionWS; OUT.uv TRANSFORM_TEX(IN.texcoord, _BaseMap); // 构建TBN矩阵的向量 OUT.normalWS normalInput.normalWS; OUT.tangentWS normalInput.tangentWS; OUT.bitangentWS normalInput.bitangentWS; return OUT; }4.3 片元着色器核心逻辑实现片元着色器是魔法发生的地方。在这里我们采样所有纹理应用遮罩并计算最终的光照。half4 frag(Varyings IN) : SV_Target { // 1. 采样基础纹理 half4 albedoAlpha SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor; half4 mask SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, IN.uv); half3 normalTS UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv), _NormalScale); // 2. 应用凹凸遮罩到法线强度 (通过lerp模拟强度变化) // 假设B通道为凹凸遮罩值越大凹凸越强。这里简化处理直接与NormalScale相乘。 // 更精确的做法可以lerp一个弱法线和强法线这里用强度系数近似。 half bumpMask mask.b; half finalNormalScale _NormalScale * (0.5 bumpMask * 0.5); // 示例将遮罩映射到[0.5, 1]倍率 normalTS UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv), finalNormalScale); // 3. 将切线空间法线转换到世界空间 float3x3 tbn float3x3(normalize(IN.tangentWS), normalize(IN.bitangentWS), normalize(IN.normalWS)); half3 normalWS mul(normalTS, tbn); normalWS normalize(normalWS); // 4. 准备表面数据 (SurfaceData) 和输入数据 (InputData) 供URP光照函数使用 SurfaceData surfaceData; surfaceData.albedo albedoAlpha.rgb; surfaceData.alpha albedoAlpha.a; surfaceData.metallic 0.0; // 非金属工作流或从遮罩/纹理读取 surfaceData.specular half3(mask.r * _SpecularIntensity, mask.r * _SpecularIntensity, mask.r * _SpecularIntensity); // 用遮罩R通道控制高光颜色强度 surfaceData.smoothness mask.g * _SmoothnessIntensity; // 用遮罩G通道控制平滑度 surfaceData.normalTS normalTS; // 对于Built-in管线这里通常用normalWS。URP的Lighting.hlsl内部会处理。 surfaceData.occlusion 1.0; surfaceData.emission half3(0,0,0); InputData inputData; InitializeInputData(IN, normalWS, inputData); // 5. 计算主光源和附加光源光照 half4 color UniversalFragmentPBR(inputData, surfaceData); return color; } ENDHLSL } } FallBack Universal Render Pipeline/Lit }这段片元着色器代码清晰地展示了流程采样所有纹理。用遮罩图的B通道调整法线贴图的强度系数实现凹凸强弱控制。将法线从切线空间变换到世界空间。构建SurfaceData其中关键的一步是specular强度由遮罩R通道乘以全局强度得到smoothness由遮罩G通道乘以全局强度得到。这里specular被设置为一个灰度值你也可以从另一张纹理读取高光颜色。调用URP的UniversalFragmentPBR函数完成基于物理的渲染计算。重要提示以上手写Shader是一个简化示例用于阐明原理。在实际URP项目中更规范的做法是继承Lit.shader并重写相关方法或者使用Surface ShaderBuilt-in RP的语法。直接编写完整的Forward Lit Pass需要处理更多细节如阴影、光照贴图、延迟渲染等。建议初学者先从修改URP提供的Lit Shader模板开始。5. 常见问题、排查技巧与性能优化5.1 效果不显示或显示错误的排查流程当你精心制作的材质在场景中看起来一片漆黑、高光不对或者遮罩没起作用时可以按照以下步骤排查检查纹理导入设置这是最常见的问题。确保_MaskMap的纹理类型不是“Normal map”而是“Default”或“Advanced”并且sRGB (Color Texture) 选项根据你的遮罩图内容正确选择通常灰度图/遮罩图需要关闭sRGB。法线贴图必须将类型设置为“Normal map”。检查UV模型UV是否正确在Scene视图的“Shaded”模式下选择“UV Checker”或“Texture Coordinate”可视化模式查看纹理是否被拉伸或扭曲。对于导入的模型如SolidWorks模型务必在3D软件或Unity中处理好UV。检查Shader编译错误对于手写Shader查看Console窗口是否有编译错误或警告。一个缺失的分号或拼写错误的变量名都会导致Shader失效。逐步简化调试在Shader Graph中可以暂时断开遮罩的连接直接将一个常量值如0.5连接到高光和平滑度输入看基础光照是否正常。然后逐步接入遮罩的R、G通道定位是哪个环节出了问题。检查通道分离在Shader Graph中使用Preview节点单独预览Split节点输出的R、G、B值确保遮罩图各个通道的信息符合你的预期。在手写Shader中可以临时将mask.r或mask.g直接输出为颜色在屏幕上查看其分布。5.2 性能优化要点纹理压缩与尺寸遮罩图通常是灰度或简单色彩可以使用更高的压缩比。在移动平台考虑使用ASTC格式。纹理尺寸不要盲目追求2048x2048根据模型在屏幕上的显示大小选择512x512或1024x1024通常足够。Shader变体管理如果你使用手写Shader并使用了大量的#pragma multi_compile会导致Shader变体数量激增增加编译时间和包体。URP的Shader Graph在这方面管理得更好。对于手写Shader要精简特性开关。减少纹理采样我们的方案采样了3张纹理Albedo, Normal, Mask。这已经是比较经济的配置。避免在同一Shader中采样过多纹理如果可能将一些遮罩信息打包到Albedo或Normal贴图的Alpha通道中例如将平滑度遮罩存到Albedo的Alpha通道。针对平台的简化对于低端移动设备可以考虑制作一个简化版本的Shader例如去掉法线贴图或使用更简单的光照模型如Blin-Phong代替PBR并通过遮罩控制一个统一的高光强度而不是完整的PBR参数。5.3 与其他工作流的结合与顶点色结合遮罩纹理是逐像素控制的精度高但资源消耗也高。对于大面积的、渐变的效果如泥土从下往上堆积可以结合使用顶点颜色Vertex Color作为遮罩。在Shader中采样顶点色并与纹理遮罩相乘或叠加可以实现更动态、更节省资源的效果。与细节贴图Detail Map结合在使用了遮罩控制主材质属性后你还可以叠加第二套细节纹理Detail Albedo/Normal。这时遮罩图可以额外分出一个通道如A通道来控制细节纹理的混合强度在磨损边缘露出细节纹理模拟掉漆后露出底层材质的效果。与视差贴图Parallax Mapping或曲面细分Tessellation结合对于需要更强立体感的表面凹凸法线贴图可能不够。你可以使用遮罩图的另一个通道来控制视差偏移的强度或曲面细分的程度在特定区域如裂缝、弹孔产生真正的几何深度错觉。实现一个高质量的带遮罩的凹凸高光材质是连接美术资产与引擎渲染的重要桥梁。它要求开发者不仅理解Shader的基本原理还要具备一定的美术审美和资源管理意识。从Shader Graph快速搭建原型到手写Shader深度定制这条路径充满了挑战和乐趣。最关键的是多动手尝试将理论应用于一个个具体的模型上观察光影如何随着你的遮罩而变化那种对材质表现力掌控感正是图形编程的魅力所在。
返回列表