“宁可用 Alpha Test镂空也比大面积半透明叠加便宜。”这句话在 PC / 主机上大体成立在手机上经常不成立在一些手机 GPU 上甚至会反过来。下面把原因讲清楚最后再给出该怎么选。一、两种做法到底有什么区别还是用那团烟20 张方形图片叠在一起图片四周是透明的中间是烟。Alpha Blend半透明混合每个像素有一个透明度 Alpha0 ~ 1 0.3 就是“30% 烟 70% 后面的颜色” → 颜色可以是半透半实边缘很柔和Alpha Test镂空也叫 Alpha Clip / Cutout定一个阈值比如 0.5 Alpha ≥ 0.5 → 这个像素当作“完全不透明”画上去 Alpha 0.5 → 这个像素直接扔掉discard什么都不画 → 每个像素只有两种结果画或者不画Shader 里就是一句clip(col.a - _Cutoff); // 结果小于 0 就扔掉这个像素关键区别Alpha Test 画上去的像素是“不透明”的。后面所有的差别都来自这一点。二、半透明的每一层具体贵在哪上一篇讲过半透明的每张图片、每个像素都要走① 跑 Shader取贴图 ② 读出屏幕上已有的旧颜色 ③ 和新颜色混合 ④ 写回屏幕除了①它还有三个特殊的代价代价原因读 写屏幕混合必须先读旧颜色比单纯写入多一次内存读取占带宽不写深度半透明不能挡住后面的东西所以不会记录“这里已经被挡住了”必须从远到近排序否则混合结果是错的排序还会打断合批其中最致命的是“不写深度”因为不写深度GPU 永远无法判断“这个像素已经被盖住了” → 20 层每层都必须跑完 Shader、做完混合 → 一层都省不掉三、Alpha Test 理论上省在哪省 1被扔掉的像素不做混合、不写屏幕透明的四周直接 discard第②③④步都省了只剩第①步。一张烟雾图四周 50% 是透明的 半透明这 50% 也要读旧颜色、混合、写回虽然混合后颜色没变白干 Alpha Test这 50% 跑完 Shader 就扔掉注意Shader 本身还是跑了必须取贴图才知道 Alpha 是多少。discard 省的是后面的读写和混合不是 Shader。省 2最大的一笔留下的像素会写深度挡住后面的留下的像素是不透明的所以会写入深度。这下 GPU 又能用深度测试了把 20 张图从近到远画不透明物体一般就这样排 第 1 张最近中间烟的部分写下深度 第 2 张落在第 1 张“实心部分”后面的像素 → 深度测试失败 → 连 Shader 都不跑直接跳过 第 3 张同理……只有那些从前面图片的镂空处漏出来的像素才需要继续画。盯住烟雾中心那个像素半透明Alpha Test从近到远跑 Shader20 次1 次后 19 次被深度挡掉读写屏幕20 次读 20 次写1 次写最终颜色20 层混合最前面那层Overdraw 从 20 层坍缩到接近 1 层这就是“便宜”的主要来源。省 3不用排序不透明物体顺序错了画面也不会错深度测试会兜底所以不需要从远到近排序 → 同材质的物体更容易合批 → Draw Call 更少四、隐藏的代价discard 会破坏 GPU 的“提前剔除”到这里看起来 Alpha Test 全面胜出。问题出在硬件上。1. GPU 原本有个“偷懒”机制Early-Z正常流程里深度测试在 Shader之后光栅化 → 跑 Shader → 深度测试 → 写屏幕但这样被挡住的像素也会白跑 Shader。所以现代 GPU 会偷偷把深度测试挪到前面光栅化 → 深度测试Early-Z→ 被挡住就不跑 Shader → ……2. discard 让 GPU 不敢偷懒问题是一个像素会不会被 discard要跑完 Shader 才知道。GPU“我想先写这个像素的深度挡住后面的。” GPU“但万一 Shader 跑完它被扔掉了呢那深度就写错了。” → 只好等 Shader 跑完再决定写不写深度所以有clip的 Shader✅ 仍然可以提前“测试”深度被前面东西挡住的可以跳过 ❌ 不能提前“写入”深度要等 Shader 跑完 ⚠️ 一些旧硬件 / 驱动会保守地把整个 Early-Z 都关掉在 PC 上这个损失通常不大第三节的“省 2”依然成立。3. 手机上问题严重得多破坏了 HSR手机 GPU 大多是分块延迟渲染TBDR它们有比 Early-Z 更厉害的机制GPU机制PowerVR / 苹果HSR隐藏面消除MaliFPKForward Pixel KillAdrenoLRZ低分辨率深度以 HSR 为例它对不透明物体的处理方式是先把这一小块屏幕上所有不透明三角形的深度都算出来 找出每个像素“最前面”的那个 → 只对最前面那个跑 Shader → 不管你按什么顺序画每个像素只着色 1 次这对不透明物体几乎是完美的零 Overdraw。但遇到 Alpha TestGPU“这个像素最前面是一张镂空的图。” GPU“它会不会被 discard不跑 Shader 不知道。” GPU“那它后面的东西也不能确定被挡住了。” → 必须先把这张镂空图的 Shader 跑完才能继续判断 → HSR 的流水线被打断、等待结果不透明 HSR 正常工作最便宜 Alpha Blend本来就不参与 HSR按正常的半透明成本算 Alpha Test 既要跑 Shader又打断了 HSR 对整块屏幕的优化 → 在 PowerVR / 苹果 GPU 上常常比 Alpha Blend 还贵这也是为什么Unity 官方移动端优化文档、苹果的 Metal 文档都建议在 iOS 和部分安卓设备上避免 Alpha Test优先用 Alpha Blend。五、还有画面上的代价即使性能划算Alpha Test 也不是哪里都能用问题表现缓解方法边缘硬、锯齿只有“画 / 不画”没有过渡开 MSAA Alpha to Coverage运动时闪烁边缘像素在阈值附近跳来跳去同上或调高贴图分辨率远处变稀Mipmap 缩小时 Alpha 被平均低于阈值的变多树叶 / 头发“消失”Unity 贴图设置里勾选Mip Maps Preserve Coverage做不了烟雾烟、雾、光晕本质是半透半实的镂空后变成硬边色块这类效果只能用 Blend所以 Alpha Test 适合的是本来就有清晰轮廓的东西✅ 树叶、草、铁丝网、栅栏、碎片、硬边火花 ❌ 烟、雾、光晕、柔和的火焰、魔法光效六、总账分平台对比用同一团 20 层叠在一起的东西估算Alpha BlendAlpha TestPC / 主机Alpha Test手机 TBDRShader 次数20 层全跑接近 1~几层深度挡掉难以剔除接近全跑读写屏幕每层都读 写只写留下的只写留下的写深度不写写但不能提前写写但打断 HSR排序必须不需要不需要结论层数多就贵通常明显更便宜常常不比 Blend 省甚至更贵 TBDR 手机上屏幕读写本来就发生在芯片内部的小块缓存里所以 Alpha Blend 的“读旧颜色”代价比 PC 小很多。这也削弱了 Alpha Test 的“省 1”。七、实际该怎么选PC / 主机有清晰轮廓的物体 → Alpha Test配合 MSAA Alpha to Coverage 柔和的烟雾光效 → Alpha Blend控制层数和面积手机1. 首选不透明 贴合形状的网格 → 用多边形把轮廓“切”出来不用 clip → HSR 完全生效最便宜 2. 烟雾、光效Alpha Blend → 重点是减少面积和层数上一篇的方法 3. 树叶、草这种必须镂空的 → 用网格把透明区域尽量裁掉减少 discard 的像素 → 或者用 Depth Prepass 技巧见下进阶技巧Depth Prepass深度预渲染把 Alpha Test 物体分两遍画第 1 遍只写深度带 clip不输出颜色Shader 很简单 第 2 遍画颜色不用 clip深度测试设为 ZTest Equal → 第 2 遍是“普通的不透明 Shader” → 每个像素只有最前面那个通过复杂的着色只跑一次代价是多了一次绘制适合着色很贵、遮挡很多的情况比如大片茂密的草丛需要实测是否划算。Unity 里的设置管线怎么开 Alpha TestURP Lit / UnlitSurface Type Opaque勾选Alpha Clipping设置 Threshold自定义 Shader加clip(col.a - _Cutoff);Render Queue 设为AlphaTest2450开启ZWrite OnRender Queue 设为 2450 的意思是在普通不透明物体2000之后画这样它们先写好深度可以挡掉更多镂空物体的像素。一句话总结Alpha Test 便宜的根源是留下的像素是不透明的、会写深度能让后面被挡住的层跳过 Shader把 20 层 Overdraw 坍缩成接近 1 层还免去了混合读写和排序但 discard 要等 Shader 跑完才知道结果会妨碍 GPU 的提前剔除在手机的 HSR / TBDR 架构上代价尤其大常常不比半透明省。所以 PC 上镂空物体放心用 Alpha Test手机上优先用不透明网格切出形状烟雾光效老老实实用半透明并控制面积和层数最终以真机 Profiler 为准。
阅读完成 · 觉得有帮助?