Opengl 半透明精灵

Open GL Translucid Sprites

所以,我的目标是绘制具有半透明像素的精灵。

首先,我使用仅渲染不透明像素的着色器渲染精灵。然后,我禁用深度缓冲区写入并使用仅渲染半透明(或透明精灵)的着色器渲染相同的精灵,如此处解释:

https://gamedev.stackexchange.com/questions/51202/how-do-you-display-non-cutout-transparent-2d-textures-with-a-depth-buffer-open

这是动画精灵 sheet 我正在用作测试:

https://i.stack.imgur.com/MMs2W.png

这是我的渲染器更新函数:

updateRenderData();

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glDepthMask(GL_TRUE);
glUseProgram(_programID);
render();

glDepthMask(GL_FALSE);
glUseProgram(_translucencyPogramID);
render();


_window->SwapBuffers();

这是我的渲染函数(以防万一你想知道我是如何渲染精灵的):

GLint baseInstance = 0;
GLsizei spriteCount;

for (int i = 0; i < _spriteManager->GetSpriteBatchVector()->size(); i++)
{
    _SpriteBatch* spriteBatch = _spriteManager->GetSpriteBatchVector()->at(i);
    setTexture(spriteBatch->GetTexture());
    spriteCount = (GLsizei)spriteBatch->GetSpriteVector()->size();
    glDrawElementsInstancedBaseInstance(_quad.renderMode, _quad.indexCount, _quad.indexDataType, 0, spriteCount, baseInstance);
    baseInstance += spriteCount;
}

但是当我执行这段代码时,它只渲染半透明像素: https://i.stack.imgur.com/f4AY6.png

事实上,如果我删除第二个 render() 调用(仅调用,我仍然更改着色器并将深度遮罩设置为 false,则不会呈现任何内容(黑屏) ),像这样:

updateRenderData();

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glDepthMask(GL_TRUE);
glUseProgram(_programID);
render();

glDepthMask(GL_FALSE);
glUseProgram(_translucencyPogramID);


_window->SwapBuffers();

但是,如果我在第一次渲染后既不更改着色器也不将 glDepthMask 设置为 false,它会正确渲染精灵的不透明像素。

编辑: 这是我在第二遍中使用的片段着色器:

out layout(location = 0) vec4 outColor;

in vec2 texCoord;

uniform sampler2D tex;

void main()
{
    vec4 texel = texture(tex, texCoord);
    if(texel.a == 1)
    {
        discard;
    }
    outColor = texel;
}

第一遍是一样的,但如果 alpha < 1 则丢弃。 顶点着色器是标准的。

所以,我的问题是:这是什么原因造成的?

编辑 2:

澄清一下,这可以正确渲染(没有半透明像素):

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glDepthMask(GL_TRUE);
glUseProgram(_programID);
render();

这根本不呈现:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glDepthMask(GL_TRUE);
glUseProgram(_programID);
render();

glDepthMask(GL_FALSE);//Just added this line

这也根本不呈现:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glDepthMask(GL_TRUE);
glUseProgram(_programID);
render();

glUseProgram(_translucencyPogramID);//Just added this line

假设_programID画鸟的不透明部分,_translucencyPogramID画半透明部分

您提供的代码中没有任何内容显示可能的原因。然而,这可能是以下结果:

  1. 您实际上并没有在使用 _programID 时调用 render()
  2. 以后_programIDglClear(GL_COLOR_BUFFER_BIT)
  3. 您将 GL_ONE_MINUS_SRC_ALPHA 用作 glBlendFunc()sfactor

假设您的 _programID 的片段着色器包含:

if (texel.a < 1.0)
    discard;

您的 _translucencyPogramID 的片段着色器包括:

if (texel.a == 1.0)
    discard;

然后授予你的绘制循环是这样的(伪代码):

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glDepthMask(GL_TRUE)
glUseProgram(_programID)
for each sprite
    glDrawArrays(....)

glDepthMask(GL_FALSE)
glUseProgram(_translucencyPogramID)
for each sprite
    glDrawArrays(....)

glDepthMask(GL_TRUE)

然后在使用 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 时,应该会得到想要的结果。

编辑

那么当您添加 glDepthMask(GL_FALSE) 时它会中断的原因是因为您之后没有再次启用它。您可以通过以下方式检查:

GLboolean enabled;
glGetBooleanv(GL_DEPTH_WRITEMASK, &enabled);
assert(enabled == GL_TRUE);

在你之前 glClear(GL_DEPTH_BUFFER_BIT).

它中断的原因是如果写入深度缓冲区被禁用,那么 glClear(GL_DEPTH_BUFFER_BIT) 将不会做任何事情,因此深度缓冲区保持原样。

正如您在下面看到的,在第二张图片上,它模糊了所有鸟框的轮廓。这是因为深度缓冲区永远不会被清除,而这只鸟是最前面的鸟。如果在调用 glClear(GL_DEPTH_BUFFER_BIT) 时启用了写入深度缓冲区,那么它会正确生成第一张图片。

左:在glClear上启用了深度写入。右:在 glClear.

上禁用了深度写入

请注意,此关系适用于 glClear(...) 和任何 gl*Mask(GL_FALSE)