UE5视差材质选型、参数标定与PDO避坑
发布时间:2026/11/5 4:04:12 锦皓数字建站

简介面向虚幻引擎5开发者与视觉美术的视差材质资源聚焦用高度图与法线贴图模拟表面微小起伏解决平面纹理缺乏立体深度的问题适用于游戏场景、建筑可视化与影视级材质练习。包内共478个文件以384个uasset材质与贴图资产、4个umap关卡为主辅以udd、ini、dat等配置与数据文件以及uproject工程入口解压后约918MB目录便于直接导入或对照研究。已有1127人学习下载说明其在UE5材质学习中具备一定参考热度。内容覆盖九宫格与单图版两种视差实现涉及高度图、视差偏移、视差UV、切线空间、法线混合等核心概念读者可据此观察节点连接、参数组织与贴图配置快速理解深度错觉的构建方式并将其迁移到自己的场景材质中。1. 一面砖墙省下几十万面UE5 视差材质到底解决什么问题做室内场景的人大概都撞过这堵墙一面清水砖墙要凑到三十厘米能看清砖缝的凹凸又要四米外跑满帧。真去雕砖缝一面墙轻松吃掉几十万面光照还容易炸纯法线贴图在掠射角下平得像贴纸。UE5 视差材质堵的就是这个缺口——用一张高度图在像素阶段把 UV 往里推让平面在视觉上凹下去。虚幻5材质系统里这条路有两个入口老的 BumpOffset 和 ParallaxOcclusionMapping 函数。这套东西值不值得下取决于你要的是一套能标定、能降级、能验收的参数流程还是一堆看着唬人的节点图。真正让视差材质在项目里站住脚的不是它能假凹凸而是它便宜得离谱一面 4m×4m 的墙几何方案是几万面加一张 4K 法线视差方案是一张高度图加一次额外采样。代价也很明确——它是屏幕空间的假象不写深度不参与真实遮挡视角一斜就露馅。所以整套流程的重心不在节点怎么连而在什么时候该用它、参数标到多少、哪里必须退回真几何。2. 三条技术路线选型BumpOffset、POM 与真几何的边界选型这一步不做后面参数怎么调都是瞎调。视差在 UE 里有三代做法代价和正确性差了一个数量级混着用是最常见的翻车起点。2.1 BumpOffset最便宜的那条路什么时候还该用它BumpOffset 节点内部做的是一次线性偏移公式大致是UV - (ViewDir.xy / ViewDir.z) * Height * HeightRatio只采样一次高度图不做任何步进搜索。它的输入只有四个Coordinates、Height、Height Ratio、Reference Plane输出一组偏移后的 UV直接喂给 Texture Sample 的 UVs 就行。代价低到什么程度一条指令级别的额外开销移动端、Nanite、VR 全都能跑材质指令数几乎不变。所以它在两个场景里依然是首选。第一是掠射角为主的大面积地面和走廊——你站在走廊这头看那头的地砖视角几乎是贴着地面的POM 在这种情况下步进次数不够就会抖成一锅粥反而不如 BumpOffset 稳定。第二是移动端和需要大量材质变体的项目POM 的循环在 ES3.1 上要么不支持要么得退化成固定次数展开变体数量直接翻倍。它的硬伤也很清楚没有遮挡判定。因为只偏移一次当高度差超过一个像素覆盖的范围砖缝的墙和底会互相穿插表现为边缘拉丝、纹理游泳。这不是参数没调好是算法本身只做了一次近似。经验值是 Height Ratio 别超过 0.05超过这个数拉丝肉眼可见如果你需要更强的立体感别硬怼参数换路线。还有一点容易忽略BumpOffset 的偏移是在切线空间里做的模型的切线必须正确。导入的墙面如果切线是自动生成的、UV 又是镜像平铺偏移方向会左右反。碰到左边凹右边凸的诡异现象先去看 UV 是不是负值缩放。2.2 ParallaxOcclusionMapping逐像素步进的真相引擎自带的 ParallaxOcclusionMapping 材质函数在 Engine Content 的 Functions 目录下本质是一次视线射线步进在切线空间里沿视线方向按固定步长往前走每走一步采样一次高度图比较当前层高和采样到的高度第一次穿模之后再做一次线性插值细化得到最终 UV。这就是它能产生真实遮挡关系的原因——砖缝的侧壁会盖住后面的砖面而不是简单平移。它的输入参数比 BumpOffset 多得多也是大部分人卡住的地方参数作用典型取值调错的表现Heightmap Texture高度图纹理对象单通道灰度图空值直接不偏移Height Ratio整体视差深度0.020.12太大撕裂太小没感觉Min Steps最少步进次数816太小近处台阶感明显Max Steps最多步进次数3264太大指令数暴涨Step Scale步长缩放系数1.0 起调偏离 1 太多会漏采Height Map Channel高度所在通道R 或 A选错整张图反向Reference Plane参考平面偏移01影响凹凸中心位置函数通常吐出两个输出Parallax UVs 和 Pixel Depth Offset。前者接给所有需要采样表面细节的贴图BaseColor、Normal、Roughness 都要用同一组偏移 UV否则颜色和凹凸会错位后者接材质的 Pixel Depth Offset 引脚用来把假深度写回深度缓冲。步进次数是最直接的性能旋钮。Min Steps 决定近处质量Max Steps 决定极端角度下的上限。常见做法是把 Min Steps 设成 8、Max Steps 设成 32然后根据项目预算上下浮动。要注意这玩意儿是指令数大户一个 64 步的 POM 材质在 1080p 全屏铺开GPU 时间能吃掉几个毫秒别在地面这种占屏比极高的物体上无脑拉满。提示POM 的循环在材质编译器里会被展开Steps 越大编译时间越长材质变体多的时候编辑器会明显卡顿。改参数前先想清楚是不是真的需要 64 步。2.3 三条路线的选型判断表把三条路线摆在一起看选择其实不难难的是团队里没人愿意承认这面墙该上真几何。维度BumpOffsetPOM 函数真几何 Nanite额外采样数1 次Steps 次0遮挡正确性无有完整深度写入无需手动接 PDO原生移动端可用是受限是掠射角稳定性好一般好近距特写差好最好美术成本低中高我一般按观看距离和视角给规则主视角距离大于两米、以正视为主的墙面BumpOffset 够了玩家能走到一米以内、并且会侧着看的地面、墙裙、石材台面上 POM需要凑到二十厘米、必须要有硬边投影和真实轮廓的家具边缘、雕花、破损砖角老老实实建模视差只用来补表面细节。还有一条容易被忽略的判据这东西会不会出现在反射里。POM 在屏幕空间反射、平面反射里不会自动跟着凹你会在湿地面反射里看到一面平墙。这种场景要么上真几何要么干脆别用视差。3. 搭一套可复用的视差材质从高度图规范到参数标定节点连起来只是开始真正决定效果好坏的是高度图规范和参数标定。这一章给一套可以直接抄的流程主材质做一次后面所有墙面都从材质实例派生。3.1 高度图的生产规范与打包策略高度图不是随便拿张灰度图就完事它的数值分布直接决定视差层高。约定比技术更重要统一规定 0 为最低、1 为最高中间调对应实际层高。全项目用同一套约定否则从不同来源拿的贴图混在一起必然有一半是反的。在 UE 里导入高度图有两个设置必须手动改默认值会毁掉一切import unreal # 批量修正高度图的导入设置关 sRGB、改灰度压缩 def fix_heightmap(asset_path: str): tex unreal.EditorAssetLibrary.load_asset(asset_path) if not isinstance(tex, unreal.Texture2D): return # 高度是线性数据必须关掉 sRGB否则中间调被 gamma 掰弯 tex.set_editor_property(srgb, False) # BC4 单通道压缩比 BC1/BC3 的块状伪影小得多 tex.set_editor_property(compression_settings, unreal.TextureCompressionSettings.TC_GRAYSCALE) tex.set_editor_property(lod_group, unreal.TextureGroup.TEXTUREGROUP_WORLD) unreal.EditorAssetLibrary.save_loaded_asset(tex) for path in unreal.EditorUtilityLibrary.get_selected_assets(): fix_heightmap(path.get_path_name())这段脚本选中一批高度图后跑一遍核心是三个属性。srgb必须为 False因为高度是线性数据打开 sRGB 会让 0.5 的中间调实际变成 0.21 左右层高分布整个塌向一侧表现是凹凸只往一个方向偏。compression_settings设成 TC_GRAYSCALE 走 BC4单通道 8 位比塞进 RGB 用 BC1 压缩的块状台阶好太多。lod_group设成 World 是为了让贴图流送策略和场景贴图一致避免远景突然糊掉。如果你的管线要求把所有贴图打包进一张图的 RGB A高度就放 A 通道压缩用 BC3 或 BC7此时脚本里的压缩设置要改成对应的 TC_BC7材质里 Height Map Channel 选 A。打包的代价是 A 通道和 RGB 共享压缩块高度精度会略降近距离大层高时能看出台阶。分辨率上高度图和法线图保持一致都跟 BaseColor 同 UV 平铺。别用 2K 高度配 4K 法线采样位置对不上凹凸和颜色会错半格。平铺次数超过 4 次的墙面建议单独做一张只含高度的低分辨率平铺图避免高度图在远处产生摩尔纹。3.2 主材质节点连线与参数暴露主材质的连线顺序很重要错一步后面全乱。我的固定顺序是先算视差 UV再用同一组 UV 采样所有表面贴图最后把 PDO 单独接到材质输出。第一段Heightmap Texture 采样节点UV 用 TexCoord 乘平铺系数输出接 ParallaxOcclusionMapping 的 Heightmap Texture 和 UVs。注意这里的高度采样不要加任何偏移POM 内部会自己算。第二段ViewDir 不需要手动接函数内部用像素的切线空间视线。但如果你要做自定义版本需要把 Camera Vector 转到切线空间用 TransformVectorTangent Space节点。第三段POM 输出的 Parallax UVs 接给 BaseColor、Normal、ORM 三张贴图的 UVs 输入。这一步最容易出错有人只把 BaseColor 接了偏移 UV法线还用原始 UV结果颜色在动、高光不动看着像贴图在墙上滑。第四段PDO 输出接材质的 Pixel Depth Offset 引脚。这个引脚在材质细节面板的材质分类下需要把着色模型设成能写深度的类型才生效。参数暴露上把 Height Ratio、Min Steps、Max Steps、Step Scale 都提成材质参数勾上 Use Custom Primitive Data 更省但先保证能用。命名统一加前缀比如MP_HeightRatio后面用脚本批量改实例参数时不容易撞名。如果要做自定义版本很多人做是因为官方函数不够灵活想加自适应步长Custom 节点里可以这么写一个最简步进// Custom 节点输入UV(float2)、HeightTex(Texture2D)、ViewDirTS(float3)、 // HeightRatio(float)、Steps(float) // 注意Steps 必须是编译期常量别接动态参数 float2 curUV UV; float layerStep 1.0 / Steps; // 视线越斜单步在 UV 上的投影越长 float2 delta (ViewDirTS.xy / max(abs(ViewDirTS.z), 0.15)) * HeightRatio * layerStep; float curLayer 0.0; float curHeight HeightTex.SampleLevel(HeightTexSampler, curUV, 0).r; [loop] for (int i 0; i 64; i) { if (curLayer 1.0 || curHeight curLayer) break; curLayer layerStep; curUV - delta; curHeight HeightTex.SampleLevel(HeightTexSampler, curUV, 0).r; } return curUV;ViewDirTS.z加了 0.15 的下限保护防止视线完全平行表面时 delta 爆炸这是掠射角撕裂的主要来源之一。SampleLevel显式指定 mip 0避免循环里自动求导导致 mip 选择抖动。循环上限写死 64UE 的材质编译器不接受完全动态的循环次数接一个动态 Steps 进去会编译报错或者退化成常量。注意Custom 节点里的 Texture2D 输入UE 会自动配一个参数名Sampler的采样器写代码时直接引用这个名字别自己声明。3.3 参数标定Height Ratio 到底给多少Height Ratio 是这个系统里唯一没有标准答案的参数因为它跟你的贴图内容强相关——同一面墙如果高度图最高点代表 2 厘米凸起和代表 8 厘米凸起比率差四倍。我用的标定法子是这样在场景里放一个 1m×1m×1m 的立方体贴同一套材质UV 平铺设成 1。然后从正前方平视慢慢调 Height Ratio直到立方体的侧面真实厚度 1m和墙面视差产生的假厚度在视觉上抵消。这个值记为基准值。比如基准值调到 0.08 正好对上 1m 的立方体那说明这张高度图的满量程对应 8 厘米的真实起伏接下来所有墙都从这个值出发按实际砖缝深度微调。Steps 的标定看的是另一个东西把相机贴到墙面前 30 厘米视线压到 15 度左右慢慢降 Max Steps直到砖缝侧壁开始出现明显台阶一层一层的横向条纹再往上加 8 步。这个值就是你这张图的步进下限。同一张图在 4K 屏上需要更多步因为一个像素覆盖的世界尺寸更小。Step Scale 一般不碰除非你发现视差在深度方向上分层了——表现为凹凸之间有一圈圈的环状纹路那是步长和高度变化率不匹配把 Step Scale 调到 1.2 左右能缓解。Reference Plane 影响的是凹凸的零点位置。设成 0 时高度 0 的部分是基准面整个表面往里凹设成 1 时反过来往外凸。做墙面一般设 0.5让砖面和砖缝各占一半这样材质在物料边缘不会突然变厚一层。3.4 用 PDO 把深度补回来不接 PDO 的视差材质在深度缓冲里依然是平的。后果是三件事贴花会浮在凸起的砖面上但实际画在平面软阴影和距离场 AO 不认这些凹凸砖缝里不会变暗景深和半透明排序按平面算玩家靠近时会有明显的割裂感。PDO 的近似公式是Height * HeightRatio也就是把当前像素的高度值按比率折算成世界单位的深度偏移。POM 函数已经把这个值算好了直接接过去就行。代价要说清楚一旦写了 PDO材质就进入深度预通道并关闭 Early-Zoverdraw 变成实打实的像素着色开销。墙面这种大面积、深度复杂度高的物体开了 PDO 之后 GPU 时间会涨。我一般只在玩家能走到一米内的墙面上开远景墙面不开。还有一条坑PDO 和 Nanite 网格搭配时行为不一致下一章细说。4. 让视差材质在 Nanite、Lumen、移动端上不出戏本地看着挺好一进正式场景就出问题九成是这三样东西在打架。它们各自的渲染路径和视差的屏幕空间假象天生不对付。4.1 Nanite 网格上的视差偏移与深度排序Nanite 走的是虚拟化几何的软件光栅化路径和传统网格的深度写入方式不一样。把带 PDO 的视差材质贴到 Nanite 网格上常见现象是深度排序错乱——墙体自己和自己打架或者远处物体被墙咬掉一块。原因在于 PDO 修改的是光栅化后的像素深度而 Nanite 的可见性缓冲和软件光栅有自己的深度处理逻辑两者叠加时深度预通道算出来的值和最终着色的值对不上遮挡剔除阶段就会误判。解决办法很直接我的习惯是视差墙面根本不勾 Nanite。墙面本来就不是高面数模型一个立方体加平铺材质Nanite 带来的好处趋近于零反而引入一堆兼容问题。如果项目规范强制全场景 Nanite那就把视差强度压到 0.03 以下并关掉 PDO用纯 UV 偏移的 BumpOffset 替代牺牲立体感换稳定。还有一个隐蔽的坑Nanite 的 fallback mesh 在远处会切回传统光栅。如果你在材质里用 Feature Level 或距离判断做了分支切回的一瞬间参数会跳变表现为远处墙面啪地一下变平。要么保持两套分支参数一致要么干脆不做距离分支。4.2 与 Lumen、SSAO、贴花的相互影响POM 造出来的凹凸是屏幕空间的幻觉场景的全局光照看到的是那张平面。所以砖缝深处不会因为遮挡而变暗砖面也不会有来自侧壁的反弹光。结果就是视差看起来贴在墙上而不是墙体的一部分。常见的补法有三层。第一层在高度图上烘一张 AO按高度值乘进 BaseColor 和 Roughness砖缝压暗。这个最便宜效果也最直接。第二层法线贴图别省视差负责大轮廓、法线负责细节高光两者叠加才有质感。第三层如果预算允许用材质里的 Distance Field AO 或者屏幕空间 AO 反向影响但别指望它能救回砖缝里的暗部。贴花的处理稍微麻烦一点。视差墙面上的贴花比如污渍、涂鸦如果材质没接 PDO贴花会按平面投影落在凸起上的部分会悬浮。接了 PDO 之后贴花能正确贴合但要注意贴花的材质也得支持深度测试。反过来如果贴花本身带位移两套 PDO 叠加会互相干扰这种场景我一般把贴花改成贴图混合进主材质不做独立贴花。Lumen 还有一个具体的表现墙面视差很强的时候附近的间接光会显得平因为 Lumen 的表面缓存采样的是几何法线不认视差偏移。缓解方式是把法线贴图的强度在视差区域适当加大让表面缓存捕捉到更多法线变化。4.3 移动端降级Feature Level Switch 与材质变体移动端ES3.1对视差的支持是受限的。POM 的循环在移动端着色器里要么不支持动态次数要么被展开成固定次数后指令数爆炸直接表现是材质编译报错、模型粉屏或者帧率腰斩。正确做法是在材质里用 FeatureLevelSwitch 节点做分支SM5 及以上走 POM 完整版ES3.1 走 BumpOffset 简化版。两支的 BaseColor、Normal、Roughness 采样逻辑保持一致只有 UV 计算不同这样视觉差异只在立体感上不会出现颜色对不上。// Custom 节点内做特性等级判断伪代码示意 #if ES3_1_PROFILE // 移动端单次偏移Height Ratio 压到 0.02 return UV - ViewDirTS.xy * HeightRatio * Height; #else // 桌面端走完整步进 return ParallaxStep(UV, HeightTex, ViewDirTS, HeightRatio, 32); #endif实际项目里我更推荐用两个独立材质而不是一个材质两分支。原因很实在FeatureLevelSwitch 会在编译期展开但材质变体数量是按分支组合算的一个材质里塞两套逻辑变体数会翻倍打包时间和包体都受影响。分开做两个材质移动端那个只保留必要节点指令数能压到桌面版的三分之一。移动端还有个必须注意的点高度图在移动平台上如果走了 ASTC 压缩单通道精度的损失比桌面 BC4 更明显近距离会出现明显的层状台阶。这种情况下把 Height Ratio 再压小用视觉上的浅浮雕换稳定。提示移动端做降级时一定要在真机上验证不要只看编辑器里的 Mobile Preview。预览器的着色器编译路径和真机不完全一致PDO 相关的报错经常只在真机上出现。5. 视差材质避坑清单五个血泪现场下面这五条都是从实际项目里攒出来的按现象 → 原因 → 解决写碰到对应症状直接对号入座。5.1 掠射角下撕裂、抖动、拉丝现象相机贴近墙面、视线几乎平行表面时砖缝边缘出现锯齿状拉丝移动相机时纹理像在水里游。原因视线方向的 z 分量趋近 0ViewDir.xy / ViewDir.z这个比值爆炸单步在 UV 上的投影长度远超一个纹素步进直接跨过了整个凹凸结构。步数不够时更明显。解决在计算 delta 时给 z 加下限钳制前面代码里的max(abs(z), 0.15)同时提高 Max Steps 到 48 以上。如果还是抖说明这张高度图的层高太大降低 Height Ratio把立体感交给法线贴图。另外检查贴图的 mip 设置掠射角下如果 mip 没降下来高频细节会持续闪烁。5.2 高度图反相凹的变凸的现象砖缝应该是凹进去的结果看起来是凸出来的网格线光照方向也反了。原因有三种。一是高度图本身的 0/1 约定和材质里的期望相反导入时没统一二是材质里 Height Map Channel 选错了通道比如高度在 A 通道却选了 R采样到的是别的数据三是 UV 用了负值缩放导致偏移方向整体翻转。解决先单独预览高度图用一张已知方向的测试图左黑右白确认数值方向再检查材质里的通道选择最后排查 UV 缩放。三处都对还反就在高度采样后面加一个 OneMinus 节点直接翻转这是最快的后悔药。5.3 阴影和物体浮在墙上现象墙角的箱子在地面上投的阴影是对的但贴着墙时影子画在砖面上没有落在砖缝的凹陷里贴花也像贴在玻璃上。原因材质没接 Pixel Depth Offset深度缓冲里墙还是平的。解决把 POM 输出的 PDO 接到材质引脚。接完之后如果出现新的问题比如半透明物体排序错乱说明 PDO 的数值符号反了取负再试。还有一种情况是物体本身也在做视差两套 PDO 叠加这就要把其中一方的 PDO 关掉。5.4 远距离噪点和摩尔纹现象站在十米外看墙面视差区域出现一片闪烁的噪点相机一动就变。原因高度图没有正确生成 mip或者采样时用了 SampleLevel(0) 强制采样最高精度远处像素覆盖多个纹素采样结果在帧间跳变。解决循环内采样用SampleLevel显式指定 mip 层级这个层级根据像素到相机的距离或者 UV 导数算出来或者在材质里加一层距离衰减超过一定距离直接把视差偏移量 lerp 到 0退回普通贴图。后者更粗暴但很有效我常用。5.5 材质变体爆炸、编译时间失控现象加完视差材质之后项目编译 shader 的时间从几分钟涨到半小时打包出来的包体也大了不少。原因POM 的步进循环在编译期展开Steps 越大展开越多如果再叠加 FeatureLevelSwitch、Quality Switch、多个材质参数组合变体数呈乘法增长。解决把 Steps 固定成两档低配 16、高配 32用 Quality Switch 而不是暴露成可变参数把静态参数Static Switch能砍的都砍掉把视差材质单独做成一个材质函数只在需要的材质里引用别全局挂在母材质上。还有一个容易忽略的Height Map Channel 如果是参数化选择的也会产生变体直接写死成常量。6. 进阶分层材质里的视差与一套可复用的验收流程单层视差调通了只是入门真正吃性能的地方在分层材质——一套墙面材质里混砖、石、木三种表面每种都要各自的视差。做法是给每一层单独算视差 UV用高度图的通道区分层高然后按混合权重做加权。这里有个原则只对权重最高的那一层跑完整 POM其他层用 BumpOffset最后按权重 lerp。全层全步进是性能杀手而且视觉上看不出差别。层间混合的边界处两套视差 UV 差值过大时会出现接缝撕裂处理办法是在混合区把偏移量整体 lerp 到 0让过渡区域退回平面。场景类型路线Height RatioMax StepsPDONanite远景墙面BumpOffset0.02—关可开可走近的砖墙POM0.0632开关地面石材POM0.0424开关移动端墙面BumpOffset0.02—关关家具边缘雕花真几何———视情况验收我固定走三个角度正前方 1 米、侧 30 度 1 米、侧 60 度 3 米。第一个看层高是否自然第二个看步进台阶有没有露出来第三个看有没有噪点和 mip 问题。三个角度都过再放到实际光照环境里看 Lumen 的表现。参数改一次就重跑一遍别偷懒。从那以后我每次接手视差材质都强制先跑一遍这套验收尤其是侧 60 度那一档——十次翻车有七次是在这个角度暴露的而玩家在场景里走动时恰恰最常出现在这个视角。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。