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 精灵、特效
相关推荐
mxwin10 小时前
unity shader中 ddx ddy是什么
unity·游戏引擎·shader
郝学胜-神的一滴13 小时前
[简化版 GAMES 101] 计算机图形学 08:三角形光栅化上
c++·unity·游戏引擎·godot·图形渲染·opengl·unreal
nnsix13 小时前
Unity ILRuntime 笔记
unity·游戏引擎
nnsix15 小时前
Unity API 兼容的 .NET Standard 2.1 和 .NET Framework 区别
unity·游戏引擎·.net
mxwin15 小时前
Unity Shader 制作半透明物体 使用多Pass提前写入深度的方式 避免穿模
unity·游戏引擎
nnsix17 小时前
Unity HybridCLR 笔记
笔记·unity·游戏引擎
nnsix18 小时前
Unity Addressables 笔记
unity·游戏引擎
RReality18 小时前
【Unity Shader URP】视差贴图 实战教程
ui·平面·unity·游戏引擎·图形渲染·贴图
小清兔1 天前
Addressable的设置打包流程
笔记·游戏·unity·c#
3D霸霸1 天前
Sourcetree 拉取新工程
数据仓库·unity