预乘Alpha的作用

转载自:https://www.cnblogs.com/xiaonanxia/p/9448444.html

Premultiplied Alpha 这个概念做游戏开发的人都不会不知道。Xcode 的工程选项里有一项 Compress PNG Files,会对 PNG 进行 Premultiplied Alpha,Texture Packer 中也有Premultiplied Alpha 的选项。那么问题来了,Premultiplied Alpha 是什么呢?我被这个问题困惑了很久,之前搜到过 Nvidia的这篇文章,其实说的很清楚,只是当时有很多相关概念没搞清楚,所以没看懂。直到前几天读《Real Time Rendering》时终于搞懂了。

Alpha Blending

要搞清楚这个问题,先得理解Alpha通道的工作原理,如果你已经了解可以直接跳过。

最常见的像素表示格式是RGBA8888即 (r, g, b, a),每个通道8位,0-255。例如红色60%透明度就是 (255, 0, 0, 153),为了表示方便alpha通道一般记成正规化后的0-1的浮点数,也就是 (255, 0, 0, 0.6)。而 Premultiplied Alpha 则是把RGB通道乘以透明度也就是 (r * a, g * a, b * a, a),50%透明红色就变成了(153, 0, 0, 0.6)。

透明通道在渲染的时候通过 Alpha Blending 产生作用,如果一个透明度为 as 的颜色 Cs 渲染到颜色 Cd上,混合后的颜色通过以下公式计算,

 

Co=αsCs+(1−αs)CdCo=αsCs+(1−αs)Cd

 

以60%透明的红色渲染到白色背景为例:

Co=(255,0,0)⋅0.6+(255,255,255)⋅(1−0.6)=(255,102,102)Co=(255,0,0)⋅0.6+(255,255,255)⋅(1−0.6)=(255,102,102)


也就是说,从视觉上,(255, 0, 0, 0.6)渲染到白色背景上 和 (255, 102, 102) 是同一个颜色。如果颜色以 Premultiplied Alpha 形式存储,也就是Cs已经乘以透明度了,所以混合公式变成了:

Co=Cs′+(1−αs)CdCo=Cs′+(1−αs)Cd

 

为什么要 Premultiplied Alpha 呢?

Premultiplied Alpha 后的像素格式变得不直观,因为在画图的时候都是先从调色板中选出一个RGB颜色,再单独设置透明度,如果RGB乘以透明度就搞不清楚原色是什么了。从前面的 Alpha Blending 公式可以看出,Premultiplied Alpha 之后,混合的时候可以少一次乘法,这可以提高一些效率,但这并不是最主要的原因。最主要的原因是:

没有 Premultiplied Alpha 的纹理无法进行 Texture Filtering(除非使用最近邻插值)。

以最常见的 filtering 方式线性插值为例,一个宽2px高1px的图片,左边的像素是红色,右边是绿色10%透明度,如果把这个图片缩放到1x1的大小,那么缩放后1像素的颜色就是左右两个像素线性插值的结果,也就是把两个像素各个通道加起来除以2。如果使用没有 Premultiplied Alpha 的颜色进行插值,那么结果就是:

((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)

 

如果绿色 Premultiplied Alpha,也就是 (0, 255 * 0.1, 0, 0.1),和红色混合后:

((255,0,0,1)+(0,25,0,0.1))⋅0.5=(127,25,0,0.55)

 

从上面的图里第三个颜色是没有 Premultiplied Alpha 的混合结果,对比第四个 Premultiplied Alpha 后颜色的结果,显然第四个颜色更符合直觉,第三个颜色太绿了,因为绿色通道没有乘以透明度,所以在线性插值的时候占了过大的权重。

所以 Premultiplied Alpha 最重要的意义是使得带透明度图片纹理可以正常的进行线性插值。这样旋转、缩放或者非整数的纹理坐标才能正常显示,否则就会像上面的例子一样,在透明像素边缘附近产生奇怪的颜色。

纹理处理

我们使用的PNG图片纹理,一般是不会 Premultiplied Alpha 的。游戏引擎在载入PNG纹理后回手动处理,然后再glTexImage2D传给GPU,比如 Cocos2D-x 中的 CCImage::premultipliedAlpha:

复制代码

void Image::premultipliedAlpha() {
    unsigned int* fourBytes = (unsigned int*)_data;
    for (int i = 0; i < _width * _height; i++) {
        unsigned char* p = _data + i * 4;
        fourBytes[i] = CC_RGB_PREMULTIPLY_ALPHA(p[0], p[1], p[2], p[3]);
    }  
    _hasPremultipliedAlpha = true;
}

复制代码

 

 

而GPU专用的纹理格式,比如 PVR、ETC 一般在生成纹理都是默认 Premultiplied Alpha 的,这些格式一般是GPU硬解码,引擎用CPU处理会很慢。

总之 glTexImage2D 传给 GPU 的纹理数据最好都是 Multiplied Alpha 的,要么在生成纹理时由纹理工具 Pre-multiplied,要么载入纹理后由游戏引擎或UI框架 Post-multiplied。

<think>嗯,用户的问题是关于Spine动画软件中和不模式下出现黑线的问题。这看起来是一个比较专业的技术问题,用户应该是在制作2D动画时遇到了图像边缘的渲染问题。 用户可能正在使用Spine制作游戏角色动画,在导出时发现角色边缘出现了不自然的黑线,这会影响游戏视觉效果。ta的提问很具体,说明已经对问题有一定了解,可能尝试过一些基础解决方案但没完全解决。 从技术角度看,这个问题涉及图像处理的alpha通道混合原理。(premultiplied)和直接(straight)alpha是两种不同的透明通道处理方式: - 模式下,RGB值已经与alpha通道相 - 非模式则保持原始RGB值 当渲染器处理方式与纹理格式不匹配时,就会出现边缘黑线或白边 用户可能遇到的具体场景包括: 1 在Spine中设置混合模式与导出设置不一致 2 纹理本身带有背景色时边缘处理不当 3 不同软件间工作流衔接问题 需要给用户一个系统解决方案,包括: - 如何检查当前纹理模式 - Spine内的正确设置步骤 - 配套的导出参数 - 常见制作陷阱的规避方法 这个问题在游戏开发中很典型,可以补充说明Unity/UE引擎端的对应设置,因为用户最终肯定要在游戏引擎中使用这些动画资源。还要提醒纹理制作阶段的注意事项,因为很多问题源头其实在PS/AI等设计软件环节。 考虑到用户是技术人员,回复可以包含一些底层原理,但要以解决方案为主。最后用注意事项总结关键点,方便用户快速实施。</think>在 Spine 中处理(premultiplied alpha)和不(straight alpha)模式下的黑线问题,关键在于**理解混合模式、确保纹理边缘干净、正确设置导出参数以及在运行时匹配渲染设置**。以下是系统解决方案: --- ### **1. 理解黑线成因** * **非纹理+错误混合**: 当纹理使用**不**的透明通道(Straight Alpha)时,若渲染器错误使用混合公式 `glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)`,会导致边缘半透明像素与黑色背景混合,产生黑边。 * **纹理+残留背景色**: 若纹理在导出前**未完全清除背景色**(尤其是边缘抗锯齿像素),在模式下这些残留色会因 Alpha 通道<1 而显现为暗边。 * **纹理过滤(Filtering)**: 缩放或旋转时,纹理采样可能混合到边缘外的透明黑色像素。 --- ### **2. 解决方案步骤** #### **(1) 确保纹理源文件干净** * **彻底清除背景**: 在 Photoshop 等工具中,使用 **`魔棒+删除`** 或 **`钢笔工具精确抠图`**,确保边缘无残留背景色。导出 PNG 时选择 **“无”** 或 **“无仿色”** 的透明度选项。 * **检查抗锯齿像素**: 放大检查边缘像素,确认半透明区域的颜色值是期的(如白边应为 `(255,255,255,128)` 而非 `(255,255,255,255)` 叠加在灰色背景上)。 #### **(2) Spine 内部设置** * **纹理打包设置**: 在 **`Texture Settings`** 中,明确选择纹理格式: * **模式** → 勾选 **`Premultiplied Alpha`** ✅ * **非模式** → 取消勾选 **`Premultiplied Alpha`** ❌ * **混合模式选择**: 在 Spine 的 **`Draw Order`** 或 **`Slot`** 属性中: * **纹理** → 使用 **`Normal`** 混合模式(对应 `SRC_ALPHA, ONE_MINUS_SRC_ALPHA`)。 * **非纹理** → 使用 **`Additive`** 或 **`Screen`** 可能引发问题,优先用 `Normal`。 #### **(3) 导出与运行时设置** * **导出 JSON/Binary**: 确保 Spine 导出时 **`Premultiplied Alpha`** 选项与纹理设置一致。 * **游戏引擎设置**: * **Unity**: * 纹理:在 Texture Importer 中勾选 **`Alpha Is Transparency`**,**不勾选** `PreMultiply Alpha`。 * Shader 使用 **`Spine/Skeleton`** 或 **`Standard`** 并确认混合模式为 `SrcAlpha, OneMinusSrcAlpha`。 * **Unreal Engine**: 在纹理资产中设置 **`Compression Settings`** 为 **`UserInterface2D (RGBA)`**,勾选 **`SRGB`**。材质混合模式设为 **`Translucent`**。 * **自定义引擎**: 严格匹配混合函数: ```cpp // 纹理的正确混合 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // 非纹理的正确混合 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ``` #### **(4) 边缘填充(Bleeding)** * Spine 的 **`Texture Packer`** 设置中,增加 **`Padding`** 值(如 2-5 像素),并在 **`Bleed`** 选项中启用 **`Bleed`** 或 **`Extrude`**。这复制边缘像素,防止过滤时采样到透明黑色。 #### **(5) 着色器修复** 若问题持续,在片段着色器中手动修正边缘: ```glsl // 用于纹理的修正(如 Unity Shader) fixed4 color = tex2D(_MainTex, uv); color.rgb /= max(color.a, 0.0001); // 逆转 return color * _Color * vertexColor; ``` > ⚠️ 此方法有性能开销,优先通过前4步解决。 --- ### **3. 选择 or 非?** * **优点**:抗锯齿效果更好,兼容光照/特效叠加。 * **非优点**:美术工作流更直观,无需额外处理。 * **推荐**:**模式**是行业标准,能彻底避免黑边问题,尤其在动态光源环境下[^1]。 --- ### **关键总结** 1. **源头清理**:确保纹理边缘无残留背景色。 2. **Spine设置**:纹理打包与导出时明确勾选/取消 `Premultiplied Alpha`。 3. **引擎匹配**:运行时混合模式必须与纹理类型一致。 4. **边缘填充**:启用 Padding 和 Bleed 防止采样黑边。 5. **统一流程**:团队统一使用 **模式** 可减少兼容问题[^2]。 > 示例:Unity 中纹理的正确设置流程: > **PS导出无背景PNG → Spine勾选Premultiplied Alpha → Unity Texture Importer勾选Alpha Is Transparency → 使用Spine官方Shader**。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值