Alpha 混合、透明排序与预乘颜色

Alpha 混合、透明排序与预乘颜色:保留 LearnOpenGL 3.3 Core 正文机制,以 context—资源—结果合同、GPU 轨迹和章专属单故障完成可重放验收。

学习目标

  • 能从 blend equation 推导 source-over 的 RGB 与输出 alpha,并配置独立颜色/alpha 因子
  • 能区分直通 alpha 与预乘 alpha 纹理,选择与资源约定匹配的 blend factors
  • 能实现 cutout 的 discard/alpha-to-coverage 与连续透明的 blending 两条渲染路径
  • 能实现“不透明 → 稳定远近排序 → 透明 pass → 状态恢复”,且不因同距离键值丢对象
  • 能判断对象中心排序何时失效,并为相交透明几何选择拆分或 OIT 等替代方案

为什么半透明不是简单地少写一点颜色

想象你在一张已经画好的画上,再盖一层半透明的颜料:颜料越浓,越能盖住底下的画;颜料越稀,底下的画就越透出来。一块磨砂玻璃、一片飘动的薄纱、水面下隐约的石头——它们都不是「完全挡住」或「完全没有」,而是让背后的东西按一定比例透出来

可前面几章那套画法做不到这件事:每个色块要么把那个位置整个盖掉、要么因为更远被深度测试丢掉,没有「半盖半透」这个中间档。结果就是你画不出玻璃、画不出薄纱,所有东西都硬邦邦地互相遮挡,假得很。

这一章要解决的就是:怎么让一块新画上去的颜色,按它自己的「浓度」,和画布上已有的颜色混在一起。这件事发生在管线很靠后的位置——前几章那些前后遮挡的「质检工序」都过完了,正要把颜色写进画布的那一刻,混合才登场。

混合:新颜色和旧颜色按比例相加

那「浓度」从哪来?从颜色的第四个分量——。一个颜色写成 (r, g, b, a),前三个是颜色本身,a 就是「这块颜色有多不透明」:a = 1 完全盖住背后、a = 0 完全透明、a = 0.6 盖六成、透四成。

把「按 alpha 把新颜色叠到旧颜色上」这件事,图形学里叫 (blending)。默认它是关着的——关着时新颜色直接覆盖旧颜色,没有「透」这回事;要用 glEnable(GL_BLEND) 把开关打开,才进入「按比例相加」的世界。

它在管线里的位置在最末端:一个片段先过深度测试、模板测试(前两章),通过了、轮到把它的颜色写进 (source,记 Csrc)写进画布时——若开了混合,就不直接覆盖,而是把它和画布上那个位置已有的颜色,也就是 (destination,记 Cdst),按比例相加。具体怎么个加法,就是下一节的混合方程。

混合方程:源色配源因子、目标色配目标因子

混合不是简单地「新的盖旧的」,而是给源色和目标色各配一个权重因子,相乘后相加。这条规则叫 (blend equation):

C=CsrcFsrc+CdstFdstC = C_\text{src} \cdot F_\text{src} + C_\text{dst} \cdot F_\text{dst}

它说:最终颜色 = 源色乘源因子 + 目标色乘目标因子。Fsrc(源因子)控制「新颜色占多少」,Fdst(目标因子)控制「旧颜色透出多少」。这两个因子由你用 glBlendFunc 指定,可以是常数 0、1,也可以是源色的 alpha 等。下面这张图把「两块色块各乘一个权重再相加」画得很直白:

最常用的一种配法,是把源因子设成源色的 alpha、目标因子设成 1 减源色 alpha——这正好对应「颜料按浓度盖、背景按剩下的比例透出」,业界叫 («source over»)。它的展开式 §4 会细推,直觉就一句:前景占 αsrc,背景占剩下的 1−αsrc。这也是本章主 Demo 让你亲手调的那条公式。

直通 alpha 与预乘 alpha:资源和因子必须配套

上面的公式默认纹理 RGB 仍是原始颜色,alpha 单独存放,这叫直通 alpha(straight alpha)。此时固定管线需要再乘一次源 alpha,使用 SRC_ALPHA, ONE_MINUS_SRC_ALPHA。另一种常见资源在存储时已经把 RGB 乘过 alpha,叫预乘 alpha(premultiplied alpha);它的源 RGB 已含覆盖率,再用 SRC_ALPHA 会二次相乘、边缘发暗,因此应使用 ONE, ONE_MINUS_SRC_ALPHA

如果渲染目标的 alpha 后续还要参与合成,只用两参数 glBlendFunc 还会让输出 alpha 按 RGB 因子计算成 αsrc² + αdst(1-αsrc),这不是标准 source-over。直通 alpha 更完整的设置是分别指定 RGB 与 alpha 因子:

glBlendFuncSeparate(
    GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, // RGB
    GL_ONE,       GL_ONE_MINUS_SRC_ALPHA  // alpha
);

这样 αout = αsrc + αdst(1-αsrc)。若最终默认 framebuffer 的 alpha 不再使用,两参数版本通常看不出差别;渲染到中间纹理、再做 UI/后处理合成时则必须把 alpha 契约讲清。颜色本身也应在线性空间混合,sRGB 目标需使用正确的 sRGB framebuffer/采样配置,不能直接把 gamma 编码值当线性光能相加。

discard:全透明的片段,干脆别画

混合擅长「半透明渐变」,但有一类东西不需要渐变——比如一片草、一张铁丝网贴图。它们的纹理里要么是完全不透明的草叶/铁丝、要么是完全透明的空隙,没有中间档。这种「非 0 即 1」的透明,与其走混合,不如直接把透明的部分扔掉

办法是片段着色器里的 :采样纹理拿到 alpha,若 alpha 小于一个阈值(接近全透明)就 discard,这个片段直接被丢掉、连颜色缓冲都不进,于是空隙处「抠空」露出背后。它和混合的区别很值得对照看——一个是硬边镂空、一个是柔和半透明:

什么时候用哪个?记住一句:二元透明(草、铁丝网这类非透即不透)用 discard,连续半透明(玻璃、水)用 blend。用错了会出毛病(§7 会讲),现在先记住这个分界。

半透明排序:为什么玻璃会互相穿帮

混合还藏着一个最容易翻车的坑。首先来自公式本身:source-over 不满足交换律,先叠 A 再叠 B 和先叠 B 再叠 A 的颜色不同。深度缓冲又只记最近值、不保存多层透明颜色,因此无法像不透明几何那样自动合成所有层。

如果先画近玻璃且写入深度,之后的远玻璃会直接深度失败;即便关闭深度写入,顺序仍然错误,因为颜色已经按“近后远”的顺序累积。下面这张图把乱序与正确透叠并排画清楚:

经典画法是:先画所有不透明物建立深度;再把透明表面从远到近稳定排序,保持深度测试开启、通常用 glDepthMask(GL_FALSE) 关闭。完美远到近且不相交时,透明层即便写深度通常也会依次通过,但关闭写入能让 pass 契约更稳健;它仍不能修复错误顺序或相交几何。

对象中心距离只是近似。两块透明几何若相交、包围或形成循环遮挡,不存在单一对象顺序;需要拆分三角形排序、depth peeling、weighted blended OIT 等方案。大量粒子常选择近似 OIT,接受一定颜色误差换稳定性能。

公式:从混合方程到 over

本节就一件事:把混合方程,和它最常用的 over 配法,一步步推清楚。先约定符号:

  • CC:混合后写进画布的最终颜色
  • CsrcC_\text{src}源色,正在画的这个片段的颜色(前景)
  • CdstC_\text{dst}目标色,画布上该像素已有的颜色(背景)
  • FsrcF_\text{src}源因子,乘在源色上的权重(由 glBlendFunc 指定)
  • FdstF_\text{dst}目标因子,乘在目标色上的权重
  • αsrc\alpha_\text{src}:源色的 alpha(不透明度,010\sim1

混合方程。最一般的形式是源色、目标色各配一个因子,相乘后相加:

C=FsrcCsrc+FdstCdstC = F_\text{src} \cdot C_\text{src} + F_\text{dst} \cdot C_\text{dst}

这个式子在说:最终颜色由两份拼成——一份是「源色乘源因子」(新颜色贡献多少),一份是「目标色乘目标因子」(旧颜色保留多少),把它们加起来。因子是 010\sim1 的权重,由你挑;挑不同的因子,就得到不同的混合效果。

展开成 over。最常用的配法把源因子取成源色的 alpha、目标因子取成 1 减这个 alpha:

Fsrc=αsrc,Fdst=1αsrcF_\text{src} = \alpha_\text{src}, \qquad F_\text{dst} = 1 - \alpha_\text{src}

这个式子在说:新颜色占的权重就是它自己的不透明度 αsrc\alpha_\text{src},旧颜色占剩下的 1αsrc1-\alpha_\text{src}——两份权重加起来正好是 1,不会越加越亮。把这两个因子代回混合方程:

C=αsrcCsrc+(1αsrc)CdstC = \alpha_\text{src} \cdot C_\text{src} + (1 - \alpha_\text{src}) \cdot C_\text{dst}

这个式子在说:前景占 αsrc\alpha_\text{src}、背景占剩下的 1αsrc1-\alpha_\text{src}。比如 αsrc=0.6\alpha_\text{src} = 0.6,结果就是 0.6 份前景色 + 0.4 份背景色——前景盖六成、背景透四成,正是「半透明颜料盖到画上」的样子。αsrc\alpha_\text{src} 越接近 1,前景越实、越盖住背景;越接近 0,前景越淡、背景越透。下面 Demo 里那个 uSrcAlpha 滑块,调的就是这条式子里的 αsrc\alpha_\text{src}

若目标 alpha 还要保留,source-over 的覆盖率组合是:

α=αsrc+αdst(1αsrc)\alpha = \alpha_\text{src} + \alpha_\text{dst}(1 - \alpha_\text{src})

人话就是:新层覆盖的部分算源 alpha,未被新层覆盖的剩余区域继续保留目标 alpha。对预乘颜色 Csrc=αsrcCsrcC'_\text{src}=\alpha_\text{src}C_\text{src},RGB 式则写成:

C=Csrc+Cdst(1αsrc)C' = C'_\text{src} + C'_\text{dst}(1 - \alpha_\text{src})

源 RGB 已经乘过覆盖率,所以固定管线源因子取 1;这就是预乘 alpha 使用 ONE, ONE_MINUS_SRC_ALPHA 的数学原因。

动手:把前景透明度调来调去,看重叠区怎么变

猜一猜:下面这块画布,左边是一块固定的暖黄背景色块,右边叠着一块紫色前景色块,两块在中间重叠。把前景的透明度 uSrcAlpha 往大调(接近 1)和往小调(接近 0),重叠区的颜色会更像前景的紫,还是更像背景的黄?先动手拖一拖,再看下面的解释。

这块画布在片段着色器里手画了两块色块——背景(目标色 Cdst)和叠在它上面的前景(源色 Csrc),重叠区严格按 over 公式 result = Csrc·αsrc + Cdst·(1−αsrc) 算颜色。拖 uSrcAlpha 改前景不透明度、拖 uSrcColor 换前景颜色,盯着重叠区怎么变:

可交互

实时演示加载中…

uSrcAlpha 调到接近 1,重叠区几乎全是前景的紫——前景几乎完全盖住了背景;调到接近 0,前景「透没了」,重叠区几乎全是背景的黄;停在 0.5 附近,重叠区是前景和背景各占一半的中间色。这正是 over 公式 Csrc·αsrc + Cdst·(1−αsrc):αsrc 越大,前景权重越大、越盖住背景,与上一节那条式子一字对上。

半透明排序:单步走一遍从穿帮到正确

猜一猜:场景里有三块半透明玻璃,一前一后。如果你不管顺序随便画,先画了最近那块、再画后面两块,后面两块会正常透出来,还是会「消失」?想一想深度缓冲那点事,再单步走一遍下面的修法。

下面把「为什么穿帮、两招怎么修」拆成三步。每一步都画出场景这一刻长什么样——盯着哪块玻璃透出来、哪块被丢弃:

分步1 / 3

① 乱序画 → 穿帮

不管顺序随便画:先画了最 那块玻璃,它把颜色混进画布、也把自己的 深度 写了进去。之后再画后面更 的两块,深度测试一看「更远」,把它们整片 丢弃 ——可它们是半透明的,本该透出来!结果后面两块消失,穿帮。

走完这遍要分清两个原因:source-over 的颜色累积本身要求远到近;深度写入又可能让后续透明层提前失败。先画不透明物建立遮挡,再排序透明层并通常关闭其深度写入,分别解决这两层问题。

代码逐段拆解

混合的代码分三块:开混合并设因子用 discard 做镂空半透明排序 + 关深度写入。三块对照看,C++/OpenGL 与 WebGL2/TS 互为镜像。

第一块:开混合、设 over 因子

混合默认是关的。初始化(或画半透明物之前)开 GL_BLEND,再用 glBlendFunc 把源因子、目标因子设成 over 那一对——源因子 GL_SRC_ALPHA(= 源 alpha)、目标因子 GL_ONE_MINUS_SRC_ALPHA(= 1 − 源 alpha)。

// 开混合开关(默认关;关着时新颜色直接覆盖,没有「透」)
glEnable(GL_BLEND);
// 设 over 混合:源因子 = 源 alpha,目标因子 = 1 - 源 alpha
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// 运算符默认是「相加」(GL_FUNC_ADD),即 C = Csrc*Fsrc + Cdst*Fdst;
// 想做相减等可改:glBlendEquation(GL_FUNC_ADD);  // 一般无需改

glBlendFuncSeparate 前两个参数是 RGB 的 source-over 因子,后两个让输出 alpha 使用 αsrc + αdst(1-αsrc)。若默认 framebuffer 的 alpha 不再被消费,可退回原书的两参数 glBlendFunc;渲染到中间纹理时优先保留正确 alpha。glBlendEquation 控制运算符,默认 FUNC_ADD

第二块:用 discard 做镂空(草 / 铁丝网)

镂空贴图(草、铁丝网)不走混合,走 discard。片段着色器采样纹理后,若 alpha 小于阈值(接近全透明)就 discard,这个片段直接被丢弃。两端 GLSL 几乎一样,只差版本声明与 precision。

#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D texture1;
void main() {
  vec4 texColor = texture(texture1, TexCoords);
  // alpha 太小(接近全透明)→ 丢弃这个片段,连颜色缓冲都不进
  if (texColor.a < 0.1)
    discard;
  FragColor = texColor;
}

discard 的片段不写颜色、不写深度,相当于「这里什么都没画」,于是草叶之间的空隙就露出背后——硬边镂空。discard 这条命令两端写法完全一致;唯一差别是版本声明:

第三块:半透明排序 + 关深度写入

半透明物体是混合最容易翻车处。正确流程:先画所有不透明物(深度测试照旧),再把半透明物从远到近排序、用 glDepthMask(GL_FALSE) 关掉深度写入后逐块画,画完恢复。

// 1) 先画所有不透明物体(正常深度测试、正常写深度)
drawOpaqueObjects();
// 2) 用容器稳定排序;不要用 map<float,...>,相同距离会覆盖丢对象
auto sorted = windows;
std::stable_sort(sorted.begin(), sorted.end(), [&](const auto& a, const auto& b) {
  const glm::vec3 da = camera.Position - a;
  const glm::vec3 db = camera.Position - b;
  return glm::dot(da, da) > glm::dot(db, db); // 远 -> 近
});
// 3) 关深度写入:半透明物仍比深度(被不透明物挡住),但不写回自己的深度
glDepthMask(GL_FALSE);
for (const auto& pos : sorted)
  drawWindowAt(pos);
glDepthMask(GL_TRUE);         // 画完恢复,别污染下一帧

两端都复制原列表再按距离平方降序排,保留每一个对象。不要使用 std::map<float, Object>:两个对象距离相同会占用同一个 key,后插入者覆盖前一个,透明物会凭空消失。稳定排序还能让距离相等时沿用提交顺序,减少相机轻微移动造成的跳变,但仍无法解决相交几何。

容易踩的坑

小结

  • source-over 的 RGB 与 alpha 有各自因子;中间纹理用 glBlendFuncSeparate 保留正确输出 alpha
  • 直通 alpha 配 SRC_ALPHA,预乘 alpha 配 ONE;资源约定和 blend factors 不匹配会产生边缘伪影
  • 硬边 cutout 用 discard/alpha-to-coverage,连续透明用 blending,并在线性颜色空间累积
  • 经典透明 pass 先画不透明物,再稳定地远到近排序;深度测试保持开启、深度写入通常关闭
  • 中心距离排序不能解决相交或循环遮挡,需拆分几何或采用 OIT,结束后恢复 blend/depth 状态

练习

问题 1(改 Demo 代码 / 观察题) 在上面的 over 混合 Demo 里,把 uSrcAlpha 分别拖到 00.51 三个值,观察重叠区颜色。回答:① 这三个值下重叠区分别最接近什么颜色?② 如果想让 αsrc = 0.6 时重叠区更偏背景色(背景透出更多),在不改 alpha 的前提下,该把混合公式里哪个因子改大?这还算 over 混合吗?

问题 2(独立实现题) 你要做一片的贴图(草叶不透明、叶间空隙完全透明)。不看上面的代码,写出片段着色器里做镂空的那一行(或几行),并说清:为什么草用这个而不用混合?这样做需不需要给草排序?

问题 3(排错题) 某人在场景里摆了一排半透明窗户,开了 glEnable(GL_BLEND) 和正确的 over glBlendFunc,但发现:站在某些角度看,后面的窗户透不出来、像消失了。他怀疑是 glBlendFunc 设错。请判断:真正的原因是什么?给出修法。

名词解释

本章出现的专业名词,用大白话再讲一遍。

alpha 通道

颜色除了红绿蓝(RGB)之外的第四个分量,记作 a,范围 0~1,表示这块颜色的不透明度:1 = 完全不透明(完全盖住背后)、0 = 完全透明(背后完全透出)、0.6 = 盖六成透四成。混合就是按 alpha 把前景叠到背景上。详见本章「混合」一节。

混合

把正在画的「新颜色」(源色)按它的不透明度 alpha,和画布上「已有的颜色」(目标色)按比例相加,得到一个介于两者之间的颜色写回画布。这就是半透明物体能透出背景的原理。默认关闭,用 glEnable(GL_BLEND) 打开。详见本章「混合」一节。

源色

正在画的这个片段算出来的颜色(前景),在混合方程里记作 Csrc。它带着自己的 alpha,决定要盖住背景多少。详见本章「混合」一节。

目标色

画布(颜色缓冲)上该像素当前已经存着的颜色(背景),在混合方程里记作 Cdst。混合时它按目标因子被保留一部分,从源色背后透出来。详见本章「混合」一节。

混合方程

混合时算最终颜色的规则:C = Csrc·Fsrc + Cdst·Fdst——源色乘源因子 + 目标色乘目标因子。因子是 0~1 的权重,由 glBlendFunc 指定;挑不同因子得到不同混合效果。详见本章「混合方程」一节与 §4。

over 混合

最常用的混合配法:源因子取源色 alpha、目标因子取 1 减源色 alpha,于是结果 = αsrc·Csrc + (1−αsrc)·Cdst——前景占 αsrc、背景占剩下的。直通 RGB 因子用 SRC_ALPHA, ONE_MINUS_SRC_ALPHA;若输出 alpha 还要保留,应以 separate factors 令 αout=αsrc+αdst(1-αsrc)。详见本章「混合方程」一节与 §4。

discard

片段着色器里的一条命令:一旦执行,当前片段就被立即丢弃,不往后走、不进颜色缓冲。常用来做镂空——纹理里 alpha 很小(接近全透明)的片段直接 discard,于是草、铁丝网的空隙被「抠掉」露出背后,得到清晰硬边。详见本章「discard」一节。

半透明排序

source-over 对多个透明层不满足交换律,因此经典管线要先画不透明物,再把透明表面从远到近累积。对象中心距离只是近似;相交或循环遮挡几何需拆分或使用 OIT。详见本章「半透明排序」一节与 §5。

深度写入

片段通过深度测试后,把自己的深度值写回深度缓冲的动作(默认开启)。画半透明物时用 glDepthMask(GL_FALSE) 通常把它关闭:透明物仍被不透明深度遮挡,却不把单层透明深度固化为最近面。它不能替代排序,画完必须恢复。详见本章「半透明排序」一节与 §6。

版本、来源与运行边界

本章以 Joey de Vries 的 LearnOpenGL 原章 为授权改编依据,教学运行基线是 OpenGL 3.3 Core Profile。Khronos 当前发布的规范参照是 OpenGL 4.6 Core Profile;这里用 4.6 规范核查术语和状态合同,但不把 4.6 API 偷偷倒填为原教程内容。GLFW、GLAD、Assimp 与驱动版本都属于运行环境,不能拿“编译通过”替代对 context、资源和 framebuffer 结果的验证。

正式概念与状态责任

  • blending:在“Alpha 混合、透明排序与预乘颜色”中由blend state、depth state 与透明 draw list负责解释其输入、受控状态和可观察结果;运行时以源/目标颜色、blend state、距离排序表、depth mask 与重叠像素定位它的第一处变化。
  • alpha:在“Alpha 混合、透明排序与预乘颜色”中由blend state、depth state 与透明 draw list负责解释其输入、受控状态和可观察结果;运行时以源/目标颜色、blend state、距离排序表、depth mask 与重叠像素定位它的第一处变化。
  • blend function:在“Alpha 混合、透明排序与预乘颜色”中由blend state、depth state 与透明 draw list负责解释其输入、受控状态和可观察结果;运行时以源/目标颜色、blend state、距离排序表、depth mask 与重叠像素定位它的第一处变化。
  • sorting:在“Alpha 混合、透明排序与预乘颜色”中由blend state、depth state 与透明 draw list负责解释其输入、受控状态和可观察结果;运行时以源/目标颜色、blend state、距离排序表、depth mask 与重叠像素定位它的第一处变化。

章专属 OpenGL 状态实验

先预测“先画不透明物,再按相机距离从远到近提交透明面并控制 depth write”发生后,blend state、depth state 与透明 draw list应怎样改变源/目标 RGBA、blend factors/equation、深度值、排序键和绘制顺序;再操作三个实验。实验不生成变化率或正确率等虚构总分,只显示真实 GL 状态、资源、命令和可观察结果。

实验一:Context—资源—结果合同

选择任一正式概念与基线/单故障场景,核对它是否进入本章状态合同。正式概念只有同时出现在解释、可视状态和交付证据中才算覆盖。

Context · resource · observable result

Alpha 混合、透明排序与预乘颜色:状态合同

把 alpha、blend equation/factors、深度写入和透明物排序接成可解释合成顺序

验证场景

官方教程正式概念

logl-20 · 基线帧

blending固定 context、资源内容与输入事件,执行“先画不透明物,再按相机距离从远到近提交透明面并控制 depth write”

状态所有者blend state、depth state 与透明 draw list
受控状态/资源源/目标 RGBA、blend factors/equation、深度值、排序键和绘制顺序
触发命令先画不透明物,再按相机距离从远到近提交透明面并控制 depth write

冻结输入:blending

blend state、depth state 与透明 draw list记录源/目标 RGBA、blend factors/equation、深度值、排序键和绘制顺序

结果:得到可重复的初始 GL 状态与资源身份

观测:源/目标颜色、blend state、距离排序表、depth mask 与重叠像素中的初始快照

预期:blend state、depth state 与透明 draw list得到可复查结果,并持续满足“混合因子匹配 straight/premultiplied alpha 约定,排序使用稳定对象身份”

实验二:CPU 命令到 GPU 结果的五段轨迹

逐段执行“先画不透明物,再按相机距离从远到近提交透明面并控制 depth write”,在每一步记录资源身份、状态变化与第一个可观察结果,并持续核对“混合因子匹配 straight/premultiplied alpha 约定,排序使用稳定对象身份”。

CPU command · GL state · GPU result

Alpha 混合、透明排序与预乘颜色:五段轨迹

选择一段命令—资源—结果1 / 5

当前观测:源/目标颜色、blend state、距离排序表、depth mask 与重叠像素中的初始快照

不变量:混合因子匹配 straight/premultiplied alpha 约定,排序使用稳定对象身份

实验三:单故障与同输入恢复

注入“透明窗按容器遍历顺序绘制,近处先写颜色导致远处被错误覆盖”,保存首个分岔;撤销后沿用完全相同的 context、资源内容、uniform 和 draw 输入重放。只有源/目标颜色、blend state、距离排序表、depth mask 与重叠像素一起恢复才算修复。

Single fault · first divergence · replay

Alpha 混合、透明排序与预乘颜色:反例与恢复

故障:透明窗按容器遍历顺序绘制,近处先写颜色导致远处被错误覆盖

1. 冻结输入一致

第 1 次沿用同一 context、资源、uniform 与 draw 输入

2. 注入单故障一致

保持其余输入不变,仅注入“透明窗按容器遍历顺序绘制,近处先写颜色导致远处被错误覆盖”

3. 定位首差一致

混合因子匹配 straight/premultiplied alpha 约定,排序使用稳定对象身份

4. 清理并重放一致

源/目标颜色、blend state、距离排序表、depth mask 与重叠像素

最小可重放检查

unit: logl-20
owner: blend state、depth state 与透明 draw list
state_or_resource: 源/目标 RGBA、blend factors/equation、深度值、排序键和绘制顺序
command: 先画不透明物,再按相机距离从远到近提交透明面并控制 depth write
pass_invariant: 混合因子匹配 straight/premultiplied alpha 约定,排序使用稳定对象身份
single_fault: 透明窗按容器遍历顺序绘制,近处先写颜色导致远处被错误覆盖
required_evidence: 源/目标颜色、blend state、距离排序表、depth mask 与重叠像素

复核者先仅依据以上合同写出预期,再运行基线、单故障和清理后重放。若两次基线的资源身份、首个状态变化或 framebuffer 结果不同,必须保留差异,不能用最终截图相似掩盖中间状态错误。

出处声明

本文为改编重写,改编自 Joey de Vries 的 LearnOpenGL。原文:learnopengl.com

原作及译作以 CC BY-NC 4.0 协议授权,本改编版同样遵循该协议(署名—非商业性使用)。

讨论

评论区加载中…