Unity Shader 预乘 Alpha 完全指南 解决半透明纹理边缘黑边问题,让你的 UI 渲染更干净

问题:传统 Alpha Blend 的黑边

你遇到过这种情况吗?

在使用 PNG 图标、UI 图集或半透明特效时,边缘总是出现难看的黑色轮廓(Dark Halo),尤其在非白色背景上格外明显。

核心概念

两种 Alpha 混合方式的本质区别

◉传统 Alpha Blend

RGB 和 Alpha 独立存储,混合时线性计算

混合公式

src.rgb × src.a + dst.rgb × (1 - src.a)

问题:当纹理滤波时,Alpha 变化但 RGB 不变,导致边缘半透明区域采样错误

●预乘 Alpha (Premultiplied)

RGB 提前乘以 Alpha 值,一起存储和传输

混合公式

src.rgb + dst.rgb × (1 - src.a)

优势:RGB 和 Alpha 始终保持一致,滤波后边缘自然过渡

技术原理详解

为什么传统方式会有黑边?

让我们假设一个 50% 透明度 的红色像素:

存储方式 RGB 值 Alpha 值 视觉含义
传统 RGBA (1.0, 0, 0) 0.5 50% 透明度,但 RGB 仍为纯红
预乘 RGBA (0.5, 0, 0) 0.5 50% 透明度,RGB 已提前乘以 Alpha

💡 关键洞察

预乘格式的 "预" 意味着 RGB 提前参与混合计算。 存储的 RGB 值已经包含了透明度信息,所以无论后续如何滤波/采样, RGB 和 Alpha 的比例关系始终保持正确。

双线性过滤的陷阱

💡

传统方式的问题:

双线性过滤在 A 和 B 之间插值时,RGB 取平均值 (1.0+0.5)/2=0.75, Alpha 也取平均 (1.0+0.5)/2=0.75

0.75 × 0.75 = 0.56 ≠ 边界处应有的 0.5! 这导致了错误的暗边效果。

Unity URP 中的实现

1. 创建预乘材质

cs 复制代码
Shader "Custom/PremultipliedAlpha"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Pass
{
// 关键:启用预乘 Alpha 混合
Blend One OneMinusSrcAlpha


CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 _Color;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 预乘格式:纹理的 RGB 已经是 premultiplied
fixed4 col = tex2D(_MainTex, i.uv);
return col * _Color; // 直接相乘,保持预乘
}
ENDCG
}
}
}
📌 Blend 指令对照
模式 Blend 命令 公式
传统 Alpha Blend SrcAlpha OneMinusSrcAlpha src × a + dst × (1-a)
预乘 Alpha Blend One OneMinusSrcAlpha src + dst × (1-a)

URP 材质设置

在 URP 的 Lit ShaderUnlit Shader 中, 只需勾选 Surface Type → Transparent 配合正确的 Blend Mode:

🎯 URP 2D 渲染器注意

如果使用 URP 2D 项目,确保 Sprite Shape 或 Sprite Renderer 的材质也配置了预乘设置。 在 Project Settings → Graphics → Tier Settings 中可以设置默认的透明混合模式。

UI 图集制作流程

STEP 1

导出设置

STEP 2

勾选预乘

STEP 3

Unity 导入

STEP 4

配置材质

  • **Photoshop / Affinity Photo 导出:**存储时选择 "Straight" 编码,避免Premultiplied,否则需要反转
  • **Texture Packer / Adobe Animate:**选择 premultiply alpha 选项,确保输出为预乘格式
  • **Unity Import Settings:**sRGB 保持勾选,Alpha Source 选择 "From Gray Alpha"
  • **运行时转换:**如需从普通纹理转换,可使用 Graphics.ConvertTexture() API

最佳实践建议

✓ 建议统一使用预乘格式的场景
  • 所有 UI 图集和图标资源
  • 2D 游戏中的角色和场景精灵
  • 粒子系统和特效纹理
  • 任何带有半透明边缘的 PNG/JPEG+Alpha 资源
  • 需要叠加混合(Additive)的特效

⚠️

保持一致性的重要性

在同一个渲染队列中混合使用预乘和非预乘资源会导致颜色计算错误。 强烈建议整个项目统一使用预乘 Alpha 格式。

总结

对比项 传统 Alpha Blend 预乘 Alpha
边缘黑边 ❌ 有 ✓ 无
滤波质量 ❌ 边缘失真 ✓ 平滑过渡
合成公式 src × a + dst × (1-a) src + dst × (1-a)
Blend 设置 SrcAlpha OneMinusSrcAlpha One OneMinusSrcAlpha
适用场景 特定后处理效果 UI、2D 精灵、特效
相关推荐
mxwin2 小时前
Unity URP 软粒子(Soft Particles)完全指南
unity·游戏引擎·shader
mxwin2 小时前
Unity Shader 深度偏移Depth Bias / Offset 完全指南
unity·游戏引擎·shader
星河耀银海3 小时前
Unity基础:UI组件详解:Button按钮的点击事件绑定
ui·unity·lucene
RReality4 小时前
【Unity Shader URP】平面反射(Planar Reflection)实战教程
ui·平面·unity·游戏引擎·图形渲染·材质
风酥糖5 小时前
Godot游戏练习01-第30节-教程结束我继续
游戏·游戏引擎·godot
Heikepengmu5 小时前
用Unity打造愤怒的小鸟游戏
游戏·unity·游戏引擎
雪儿waii14 小时前
Unity 中的 Resources 详解
unity·游戏引擎
RReality1 天前
【Unity UGUI】Toggle / ToggleGroup 与 Dropdown
ui·unity·游戏引擎·图形渲染·材质
雪儿waii1 天前
Unity 中的 InvokeRepeating 详解
unity·游戏引擎