ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

UE5 DBuffer Decal:解决贴花叠加发黑与法线覆盖问题

2026/10/3 14:20:03 拓冰建站 浏览量
UE5 DBuffer Decal:解决贴花叠加发黑与法线覆盖问题 1. Decal混合的基础认知默认方案里藏着的两个大坑1.1 贴花叠加为什么会发黑做UE5场景细节的时候Decal基本是必备工具。地面弹孔、墙上的污渍、血迹、路标、裂缝全都要靠贴花堆上去。但只要你贴花一多就会遇到一个非常经典的问题明明单张贴花看过去没问题叠了三层以上就开始发黑像蒙了一层脏东西。这个问题的根源在于默认的Decal混合模式。引擎里新建一个Deferred Decal材质默认的Blend Mode是Translucent它本质上还是透明度混合颜色输出会在已有颜色上做alpha插值。两张半透明贴花叠在一起每叠一张底下的颜色就会被稀释一次。如果贴花本身的alpha贴图不是纯白叠个四五层之后叠加区域的明度会明显下沉看起来又灰又脏。我最早做射击游戏靶场场景时墙上弹孔、火药灼烧痕迹、裂痕三张贴花叠在同一面墙上效果惨不忍睹。后来我研究了半天发现一个很关键的点如果用Translucent模式去处理Decal引擎是按照“从后往前”的顺序逐层绘制Decal的每一层都在上一层结果上继续混合。多层Decal之间存在持续的“染色”过程只要混合权重不是1颜色就一定会发黑。这个问题不是调调贴图亮度能完全解决的需要换一个底层方案。1.2 Normal混合的困境后画的贴花会“吃掉”先画的比颜色发黑更头疼的是法线问题。假设你在墙面上贴了一张砖墙法线贴花用来表现墙面凹凸细节然后在上面再叠一张弹孔贴花弹孔的边缘会产生凹陷和金属撕裂感。按理说两张贴花应该“各管各的”砖墙的凹凸还在弹孔的凹陷也显示出来。但实际效果往往是第二张贴花一叠上去第一张贴花的Normal就被覆盖了。砖墙变平了只剩下弹孔自己的法线。如果再叠第三张、第四张最终显示的就只有最上面那张的法线所有细节全被吃掉。这就是传统Decal混合方案在Normal通道上的致命伤。Decal渲染到一个Gbuffer上时Normal是“覆盖写”的后绘制的贴花会把之前的法线结果覆盖掉而不是“融合”进去。更麻烦的是覆盖过程还会破坏法线的单位长度导致光照计算出现黑面、硬边画面看起来像低质量游戏的光照。1.3 DBuffer思路是什么多一层“草稿纸”统一算为了解决这两个问题引擎提供了DBuffer这个方案。我第一次理解DBuffer的时候把它理解成“额外的一层草稿纸”。普通Decal的做法是直接在成稿上涂改后面涂的盖前面涂的还因为叠加导致颜色越来越脏。DBuffer的做法是准备了一张“独立草稿纸”所有Decal数据先记录到这张草稿纸上等所有Decal都画完了最后统一“按规则合成”到最终画面上。合成规则里可以指定颜色怎么混合、粗糙度怎么取、法线怎么插值每张贴花都能保留自己的贡献权重。DBuffer实际上是把Decal的数据先写进了三个专用缓冲区DBufferA存BaseColor和OpacityDBufferB存NormalDBufferC存Roughness和Metallic。每个材质在写DBuffer时可以选择写到哪个缓冲区、怎么写。最终光照前这些数据再统一合成到Gbuffer里。这个方案天然解决了两个问题一Decal之间的颜色叠加方式可以自定义不再是无脑alpha累乘二Normal之间可以真正做插值融合而不是后画的覆盖先画的。2. 材质配置流程从零搭一个DBuffer Decal2.1 关键第一步材质域与混合模式的选择DBuffer方案的第一步是在材质编辑器里把Material Domain设为Deferred Decal。这一步大部分人都会但接下来的Blend Mode很多人选错了。在材质细节面板里找到Blend Mode你会看到几个选项Translucent、DBuffer Translucent、DBuffer Color、DBuffer Color Normal Roughness。这几个选项是不同年代的产物功能强度不一样。我实际项目里最常用的组合是Material Domain选Deferred DecalBlend Mode选DBuffer Translucent。这个模式会完整写入DBuffer的三个通道BaseColor、Normal、Roughness全部可以参与后续合成。如果你的贴花只需要颜色选DBuffer Color也可以性能上更省。但如果你需要法线细节至少要用DBuffer Color Normal Roughness这个模式否则材质编辑器里根本不会暴露Normal相关的输出引脚。这里有一个很容易忽略的细节材质编辑器里必须把Shading Model设为Default Lit或者Clear Coat才会出现Normal引脚。如果你选的是Unlit那输出节点上根本没有NormalDBufferB缓冲区也不会被写入。做车漆、皮肤、玻璃这类有高光的Decal时我建议用Clear Coat因为DBuffer方案和Clear Coat配合得更好高光层的法线响应更自然。2.2 工程设置开DBuffer之前先确认这些开关材质端改完之后工程端还需要确认几个地方。这个坑我踩过改了材质但画面一点反应没有排查了半天。首先是项目设置里需要把Decal响应DBuffer的支持打开。在Project Settings的Rendering部分确保DBuffer Decals是启用的。这个选项在默认工程里一般是开启的但如果你是从某些模板工程改过来的需要去确认一下。其次是渲染特性等级。DBuffer是延迟渲染路径的能力如果你的项目在Forward渲染模式下运行DBuffer Decal是不生效的。UE5的Forward Shading模式默认不开DBuffer这也意味着你的项目如果在移动端用Forward渲染那DBuffer方案基本就是白搭。移动端通常用的是简化方案。再有一个细节是Scene里的Post Process Volume。某些后期材质如果强制写了GBuffer或者Decal相关数据会干扰DBuffer的合成。如果你发现开了DBuffer之后画面胶状感很重试着把Scene里的后期材质关掉看是否恢复正常。2.3 材质节点链路从贴图采样到输出引脚现在开始在材质图里搭节点。假设我们做一张带弹孔凹陷的地面贴花需求是弹孔本身有Normal细节同时底下的地面材质Normal也要保留两者做插值权重由贴花权重大小控制。节点链路如下Texture Sample节点采样BaseColor贴图RGB连到BaseColor引脚Alpha连到Opacity引脚。Texture Sample节点采样Roughness贴图R连到Roughness引脚。Texture Sample节点采样Normal贴图连到Normal引脚。把Normal引脚从默认的“直接输入”改成“自定义输入”在中间插一个Custom节点做插值运算。如果你在材质图里直接采样一份Normal贴图输入到Normal引脚默认行为是用这张Normal替换掉DBuffer里的Normal。这其实不是我们要的。我们要的是“当前DBuffer里已存的地面Normal”和我们贴花自带的Normal做一个融合。这里的关键来了要读取DBuffer里已存的Normal需要用到一个特殊的节点——Decal DBuffer Texture Sample。在材质编辑器里按D搜索这个节点把它的Texture Type设为Normal输出就是当前DBuffer里还没有合入我们贴花之前的那个Normal。第一次用这个节点时我愣了一下原来引擎早就准备好了读取DBuffer内容的接口。3. 核心难点带自定义权重的Normal插值3.1 为什么直接Lerp两个Normal会崩很多人拿到DBuffer之后想当然地认为“两个Normal做Lerp不就行了”。我在自己电脑上试过结果光照出现了明显的硬边甚至半边亮半边暗像被一个手电筒从侧面打光。这个现象的原因其实很典型。法线贴图里存的不是RGB颜色而是经过映射的向量坐标。从贴图采样出来的值在[0,1]范围而实际法线向量的分量在[-1,1]范围。材质系统内部会自动做一次“解包”unpack把贴图值映射回向量值。如果你在Custom节点里直接拿两个解包后的Normal做Lerp结果向量的长度会偏离1光照计算时如果没有重新归一化就会产生不正确的光照结果。更麻烦的是法线的视觉意义在于方向而不在于长度。两个方向相近的法线在Lerp后方向和长度都会偏移方向偏移还能接受长度偏移会导致高光强度异常。画面看起来像在模型表面涂了一层薄薄的高光油很违和。3.2 三种主流Normal Blending算法业内做Normal混合有一套成熟的标准我推荐大家按项目需求选第一种白out混合Whiteout Blending公式是normalize(float3(n1.xy n2.xy, n1.z * n2.z))。这个算法实现简单适合两个法线都是“平地细节”的场景很多游戏引擎的默认混合就是它。缺点是当两个法线夹角很大时z方向会快速衰减出现压扁效果。第二种部分混合Partial Derivative Blending核心思想是混合法线在xy平面上的“斜率”再重新构建z。这个算法在两层法线都具有强方向性的时候效果最好贴花边缘过渡更自然。代价是需要多写几行代码而且对UV连续性的要求更高。第三种RNM混合Reoriented Normal Mapping公式是normalize(float3(n1.xy n2.xy, n1.z - dot(n1.xy, n2.xy)))。这个算法本质是把第二个法线重新定向到第一个法线的空间中混合结果更像“在表面上叠一层细节”而不是“把一个法线硬插到另一个法线上”。我个人最常用的是这个尤其在车漆、皮肤上叠加伤痕和污渍时过渡非常自然。3.3 在Custom节点里写Normal插值附可用代码打开材质图拖一个Custom节点把输出类型设为Float3然后在代码框里写HLSL。下面是我实际项目里在用的代码片段float3 n1 DecalNormal; // 从DBuffer里采样得到的法线 float3 n2 SurfaceNormal; // 贴花自带的法线 float w BlendWeight; // 自定义权重范围0到1 // 先将权重做一次smoothstep让过渡更柔和 w smoothstep(0.0, 1.0, w); // 使用RNM混合 float3 rnm float3(n1.xy n2.xy, n1.z - dot(n1.xy, n2.xy)); float3 blended normalize(lerp(n1, rnm, w)); return blended;这段代码的思路是权重w控制“地表法线”和“混合后法线”的比例。权重为0时结果就是原始地表法线完全不受贴花影响权重为1时结果是经过RNM混合后的法线细节最丰富。中间区域是平滑过渡。把Decal DBuffer Texture Sample的输出接到Custom节点的DecalNormal引脚把贴花自己的Normal贴图采样接到SurfaceNormal引脚再拖一个Scalar Parameter出来当BlendWeight。这里有一个容易搞混的点贴花自己的Normal贴图需要先经过Normal解包。在材质图里Texture Sample默认勾选了sRGB而法线贴图应该关闭sRGB并且使用Normal压缩格式。所以材质编辑器里要确保那个Texture Sample的sRGB是关闭的最好把压缩设置设为TC Normalmap。否则采样出来的数据会被当成颜色处理混出来的法线完全不对。3.4 用Mask控制混合权重的分布单一权重只能控制整张贴花的整体混合强度如果想让弹孔中央凹陷最强、边缘最弱就需要用Mask贴图来控制局部混合权重。做法也很简单再采样一张灰度图输出到BlendWeight利用灰度值控制法线混合权重的分布。我习惯把这张Mask叫做“法线影响力贴图”在制作弹孔时会用法线本身的Alpha通道或者高度图充当Mask这样只要做一张图就能同时控制颜色透明度和法线影响力。还有一种进阶玩法是让Mask参与时间动画。比如做燃烧场景时火焰贴花边缘的法线变化随时间推移扩散把Mask乘上一个Time节点就能实现一种“火焰缭绕”的视觉效果。这一步不复杂但非常吃美术功底。我在做动态破损墙壁的时候用这套方法实现了墙壁坍塌时裂纹逐渐扩散的效果光线在裂纹边缘产生的暗部变化特别真实。4. 常见问题与排查技巧实录4.1 DBuffer Decal没有反应或者显示黑块这个问题我遇到不下三次每次原因都不同。第一步检查材质的Blend Mode确认不是Translucent而是DBuffer相关的选项。如果材质是Translucent那后面所有步骤都是白搭。第二步检查Project Settings里的DBuffer Decal开关是不是被关掉了。很多版本升级或模板切换后这个选项会被重置。第三步检查场景里有没有加Post Process Material特别是有改GBuffer的后期材质。我遇到过一版色差后期材质会把DBuffer数据冲掉导致Decal完全不显示。这些检查都过了还是黑的那就查一下贴图本身的sRGB设置。如果BaseColor贴图勾选了sRGB而Alpha通道被当成sRGB处理混合出来的颜色会偏到发黑。记得把BaseColor以外的贴图sRGB全部关掉。4.2 Normal插值后光照出现硬边和暗斑这个问题在RNM混合方案里最容易出现。我一开始以为是参数问题后来发现是采样过来的法线没有被正确归一化。检查一下Custom节点里的输入法线是不是已经处于正确空间。很多情况下DBuffer里采样出来的法线是切线空间而贴花自带的法线也是切线空间但两个切线空间的方向可能不一致需要把贴花自带的法线经过Transform节点转换到与DBuffer相同的切线空间。第二个常见原因是权重贴图的对比度过高导致过渡区域非常窄法线变化剧烈。解决方法是把权重贴图的对比度压下来或者通过smoothstep增加过渡的平滑度。如果暗斑出现在贴花边缘多半是因为DBuffer的合成过程中边缘透明度低于某个阈值导致数据写入不完全。可以把Opacity Threshold调低一些让边缘区域也保留足够的数据。4.3 DBuffer在移动端的表现与性能考量桌面端DBuffer很香但移动端别直接照搬。DBuffer的代价是每帧多渲染三张全屏缓冲区每GBuffer对应增加读写带宽。对移动GPU来说这个开销不小掉帧非常明显。如果你的项目必须兼顾移动端我建议用简化方案非DBuffer的Decal但Normal混合只在单张贴花范围内做用材质里的Custom节点模拟一部分效果。或者干脆用两层Decal一层颜色一层法线法线层控制得当也能接近DBuffer的效果。一个折中的实践是只在PC端开启DBuffer移动端关闭用Feature Level Switch节点自动切换材质。这样两端都可以保持较好的效果和性能。4.4 问题速查表建议收藏现象可能原因处理办法Decal完全不显示Blend Mode没选DBuffer改成DBuffer Translucent或DBuffer ColorDecal显示但全黑贴图sRGB设置错误关闭法线、粗糙度贴图的sRGBNormal没有变化Shading Model选了Unlit改成Default Lit或Clear Coat光照出现硬边权重过渡过窄或法线未归一化调整权重贴图代码里加normalize高光异常油光Normal混合向量长度偏离1改用RNM或部分混合算法性能严重下降DBuffer带宽开销过大移动端关闭DBuffer用Feature Level Switch切换亲自试过这套流程之后我对DBuffer的理解比看文档时要深得多。强烈建议在项目里单独建一个场景把不同材质的Decal叠在一起用调试视图查看DBufferA、DBufferB、DBufferC三张缓冲区分别存了什么这样你才能直观理解每一层数据的作用。最后再分享一个小技巧在做大型场景时别把一张Decal的尺寸拉得太大尽量拆成多张中小型Decal去叠。这样不仅DBuffer的合成更可控以后替换美术资源时也灵活不会出现“改一张图动全场景”的尴尬情况。