OpenGL全透明或不透明的Alpha混合

3

翻译:

我之前问过这个问题,但我的初始示例有点不完整,现在我认为我可以更具体地说明我的问题。

为了背景,我正在一台旧的苹果电脑上使用openGL 3.3,并尝试渲染重叠的四边形层。每个四边形都被uv映射到一个png 4096 x 4096纹理的一部分,该纹理具有完全透明的区域。

我想使用alpha混合,以便第N层的透明部分显示第N-1层(在其后面)的不透明部分。因为我不需要半透明度,所以我想知道 - 是否有一种方法可以配置glBlendFuncSeparate,使片段颜色只是最前面的不透明像素 - 不需要额外的乘法?

我尝试了类似于这样的东西:

glBlendFuncSeparate(GL_SRC_COLOR, GL_DST_COLOR, GL_ONE, GL_ZERO);

但我认为我完全误解了参数的含义。我认为它们的意思是:保留源颜色并将其设置为目标颜色...应用原始alpha。(我知道这是绝对错误的)。

起初我以为这是一个xyz问题,并搜索其他原因,导致我注意到性能问题,但即使上述函数调用导致完全错误的颜色,它也会使我的动画在hello-triangle + sin(time)翻译案例中平稳运行。

我想发布一些我的代码,但它主要包括设置顶点缓冲区和大型静态数组声明进行测试。我的着色器基本上是通过的:(例如,我的片段着色器中的主函数:)

void main(void)
{
    vec4 texel = texture(tex0, v_uv);

    color = vec4(texel.rgb, texel.a);
}

纹理正确加载。我已经设置了从后往前的渲染。

我该如何设置这种类型的渲染?

提前感谢您。

更新:

我尝试过:

glEnable(GL_BLEND); 
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE);
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);

但是只有不透明白色的前层出现了。我检查了RGBA值,看起来透明部分是[1.0, 1.0, 1.0, 0.0],所以即使颜色不是[0.0, 0.0, 0.0, a],当alpha为0.0时需要忽略颜色。为了模拟这种情况,我添加了一个if (alpha is 0.0), then discard;语句,但延迟问题仍然存在。

因此,这些函数并不完全正确,或者其他地方出了问题。

关于我的设置的更多信息:

(我在底部发布了我的大型纹理)

每个图层的顶点和索引数据太长了,无法发布,但每个五个图层中有三个四边形(我将相机位置传递给着色器,并使用z位置值进行偏移)。这可能是效率低下的另一个原因。

完整的片段和顶点着色器:

// 顶点

#version 330 core
precision highp float;

layout (location = 0) in vec3 a_position;
layout (location = 1) in vec2 a_uv;

out vec2 v_uv;

uniform mat4 u_matrix;
uniform float u_aspect;
uniform vec3 u_position_cam;

uniform int u_count_layers;

void main(void) 
{
    // scroll speed depends on the inverse of the z position
    float speed_factor = (1.0 / pow(2.0, u_count_layers - a_position.z));

    // x offset
    float X = -u_position_cam.x * speed_factor;

    // make sure the bg quad jumps back to the correct position
    X = mod(X, (u_aspect) * 2.0);
    X += a_position.x;

    // y offset 
    float Y = -clamp(u_position_cam.y, 0.0, 2.0) * speed_factor;
    Y += a_position.y;

    gl_Position = u_matrix * vec4(X, Y, a_position.z, 1.0);

    v_uv = a_uv;
}

// 碎片

#version 330 core
precision highp float;

in vec2 v_uv;

uniform sampler2D tex0;

out vec4 color;

void main(void)
{
    vec4 texel = texture(tex0, v_uv);

    color = vec4(texel);
}

这篇未解决的stackoverflow问题讨论了我正在经历的问题——简单来说,许多重叠的透明形状会导致延迟,但如果相同的形状没有重叠,那么一切都没问题。当然,还没有找到替代混合算法。

考虑数字——每个纹理部分覆盖1280 x 720像素。有5层,每层3个四边形。对于每个像素,我要做同样的工作N层次,所以是4,608,000?我想这可能非常昂贵。我还在做奇怪的取模运算和偏移量计算。如果我尝试实现视差滚动的方式太笨拙,那么该如何高效地实现呢?如果确实有更好的方法,我很愿意改变我的算法或设置。我很好奇如何正确设置纹理和透明层以避免我正在经历的问题。

一个想法:如果我从前面渲染会怎样?混合函数会如何改变?(我知道通常是从后向前渲染透明对象,但在这里,我只需要一旦碰到alpha == 1.0 就停止检测层)

4096x4096 bg texture "atlas"

1个回答

0

你的问题非常难以理解,但似乎关键在于

片段颜色只是最前面不透明的像素——不需要额外的乘法

那么你是说你只想绘制那些 alpha 值为 1.0 的像素,而其余的则被丢弃了吗?就像以下示例是正确的:

你正在绘制 3 个大小为 3x1 的纹理。第一个纹理有所有 3 个像素都不透明;第二个纹理有前两个像素不透明,最后一个只有第一个像素不透明。现在的结果应该是第一个像素来自纹理 3,第二个像素来自纹理 2,第三个像素来自纹理 1。

如果这是真的,那么首先假设你的纹理已经输出了 alpha 值为 1.0.0。在清除缓冲区后,你的混合函数应该是 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA。这意味着即将到来的颜色将与其源 alpha 值相乘,并混合到当前颜色中:

  • 对于 alpha 值为 1.0,这意味着源颜色将被完全使用(乘以 1.0),目标颜色将被完全忽略(乘以 0.0destination = source*1.0 + destination*.0。通俗地说,这个像素将被新颜色完全重绘。
  • 对于 alpha 值为 0.0,这意味着源颜色将被完全忽略(乘以 0.0),目标颜色将被完全使用(乘以 1.0destination = source*0.0 + destination*1.0。通俗地说,这个像素将完全保留原样,新颜色将被忽略。

你可能甚至不需要使用混合的 alpha 分量,所以我猜可以应用 GL_ZERO, GL_ONE,表示你将从 glClear 中保留 alpha 通道。否则,相同的规则适用于 alpha 通道,因此请根据需要进行操作。

如果您需要进一步定义“不透明”,您可能希望在着色器中进行控制。可以应用一些阈值,例如:

void main(void)
{
    vec4 texel = texture(tex0, v_uv);
    color = vec4(texel.rgb, step(texel.a, 0.5));
}

请注意,即使您的输入纹理只有0.01.0值,在您的着色器中可能并非如此,除非您在纹理上使用nearest过滤器。插值值可以是任意大小。

如果我表达不够清晰,我很抱歉。我已经在原问题中添加了细节。 - synchronizer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接