Opengl 半透明精灵
Open GL Translucid Sprites
所以,我的目标是绘制具有半透明像素的精灵。
首先,我使用仅渲染不透明像素的着色器渲染精灵。然后,我禁用深度缓冲区写入并使用仅渲染半透明(或透明精灵)的着色器渲染相同的精灵,如此处解释:
这是动画精灵 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
画半透明部分
您提供的代码中没有任何内容显示可能的原因。然而,这可能是以下结果:
- 您实际上并没有在使用
_programID
时调用 render()
。
- 以后
_programID
你glClear(GL_COLOR_BUFFER_BIT)
- 您将
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)
。
所以,我的目标是绘制具有半透明像素的精灵。
首先,我使用仅渲染不透明像素的着色器渲染精灵。然后,我禁用深度缓冲区写入并使用仅渲染半透明(或透明精灵)的着色器渲染相同的精灵,如此处解释:
这是动画精灵 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
画半透明部分
您提供的代码中没有任何内容显示可能的原因。然而,这可能是以下结果:
- 您实际上并没有在使用
_programID
时调用render()
。 - 以后
_programID
你glClear(GL_COLOR_BUFFER_BIT)
- 您将
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)
。