LibGDX 纹理混合与 OpenGL 混合功能 [英] LibGDX texture blending with OpenGL blending function

查看:22
本文介绍了LibGDX 纹理混合与 OpenGL 混合功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 libGdx 中,我正在尝试创建形状纹理:获取完全可见的矩形纹理并对其进行遮罩以获得形状纹理,如下所示:

在这里我在矩形上测试它,但我想在任何形状上使用它.我查看了 - 警告:非常混乱)

解决方案

您正在尝试做的事情看起来像是非常巧妙地使用了混合.但我相信你应用它的确切方式是被设计破坏的".让我们来看看这些步骤:

  1. 您使用红色和绿色方块渲染背景.
  2. 你在你的背景上渲染一个不透明的纹理.
  3. 您可以通过应用蒙版擦除在第 2 步中渲染的部分纹理.

问题在于,对于您在第 3 步中擦除的部分,之前的背景不会恢复.实在不行,因为你在第 2 步把它擦掉了.整个纹理区域的背景在第 2 步被替换了,一旦它消失了就无法恢复.

现在的问题当然是如何解决这个问题.我可以想到两种常规方法:

  • 您可以通过将纹理和蒙版渲染到一个离屏帧缓冲区对象 (FBO) 中来组合它们.您像现在一样执行第 1 步和第 2 步,但渲染到带有纹理附件的 FBO.然后,您渲染到的纹理是一个带有 alpha 值的纹理,可以反映您的蒙版,您可以使用此纹理通过标准混合渲染到您的默认帧缓冲区中.
  • 您可以使用模板缓冲区.屏蔽部分渲染是模板缓冲区的主要应用,对于您的用例来说,使用模板绝对是一个非常好的解决方案.在此答案中,我不会详细说明如何将模板缓冲区准确应用于您的案例.如果您搜索OpenGL 模板",您应该能够在网上和书籍中找到大量示例,包括在本网站的其他答案中.例如,最近的这个问题涉及使用模板缓冲区做类似的事情:OpenGL 模板(剪辑实体).

所以这些将是标准解决方案.但是受到您尝试中的想法的启发,我认为实际上可以通过混合来实现它.我想出的方法使用了稍微不同的序列和不同的混合函数.我还没有尝试过,但我认为它应该可以工作:

  1. 您像以前一样渲染背景.
  2. 渲染蒙版.为了防止它抹去背景,禁用写入帧缓冲区的颜色组件,只写入 alpha 组件.这会将掩码留在帧缓冲区的 alpha 组件中.
  3. 渲染纹理,使用帧缓冲区 (DST_ALPHA) 中的 alpha 分量进行混合.

您需要一个带有 alpha 组件的帧缓冲区才能使其工作.确保在设置上下文/表面时为帧缓冲区请求 alpha 位.

代码序列如下所示:

//绘制背景.glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_TRUE);glDisable(GL_BLEND);//绘制蒙版.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);glEnable(GL_BLEND);glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);//绘制纹理.

In libGdx, i'm trying to create a shaped texture: Take a fully-visible rectangle texture and mask it to obtain a shaped textured, as shown here:

Here I test it on rectangle, but i will want to use it on any shape. I have looked into this tutorial and came with an idea to first draw the texture, and then the mask with blanding function:

batch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_ALPHA);

  • GL20.GL_ZERO - because i really don't want to paint any pixels from the mask
  • GL20.GL_SRC_ALPHA - from original texture i want to paint only those pixels, where mask was visible (= white).

Crucial part of the test code:

batch0.enableBlending();
batch0.begin();

batch0.draw(original, 0, 0); //to see the original
batch0.draw(mask, width1, 0); //and the mask

batch0.draw(original, 0, height1); //base for the result

batch0.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_ALPHA);
batch0.draw(mask, 0, height1); //draw mask on result
batch0.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

batch0.end();

The center ot the texture get's selected well, but instead of transparent color around, i see black:

Why is the result blank and not transparent?

(Full code - Warning: very messy)

解决方案

What you're trying to do looks like a pretty clever use of blending. But I believe the exact way you apply it is "broken by design". Let's walk through the steps:

  1. You render your background with red and green squares.
  2. You render an opaque texture on top of you background.
  3. You erase parts of the texture you rendered in step 2 by applying a mask.

The problem is that for the parts you erase in step 3, the previous background is not coming back. It really can't, because you wiped it out in step 2. The background of the whole texture area was replaced in step 2, and once it's gone there's no way to bring it back.

Now the question is of course how you can fix this. There are two conventional approaches I can think of:

  • You can combine the texture and mask by rendering them into an off-sreen framebuffer object (FBO). You perform steps 1 and 2 as you do now, but render into an FBO with a texture attachment. The texture you rendered into is then a texture with alpha values that reflect your mask, and you can use this texture to render into your default framebuffer with standard blending.
  • You can use a stencil buffer. Masking out parts of rendering is a primary application of stencil buffers, and using stencil would definitely be a very good solution for your use case. I won't elaborate on the details of how exactly to apply stencil buffers to your case in this answer. You should be able to find plenty of examples both online and in books, including in other answers on this site, if you search for "OpenGL stencil". For example this recent question deals with doing something similar using a stencil buffer: OpenGL stencil (Clip Entity).

So those would be the standard solutions. But inspired by the idea in your attempt, I think it's actually possible to get this to work with just blending. The approach that I came up with uses a slightly different sequence and different blend functions. I haven't tried this out, but I think it should work:

  1. You render the background as before.
  2. Render the mask. To prevent it from wiping out the background, disable writing to the color components of the framebuffer, and only write to the alpha component. This leaves the mask in the alpha component of the framebuffer.
  3. Render the texture, using the alpha component from the framebuffer (DST_ALPHA) for blending.

You will need a framebuffer with an alpha component for this to work. Make sure that you request alpha bits for your framebuffer when setting up your context/surface.

The code sequence would look like this:

// Draw background.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glDisable(GL_BLEND);
// Draw mask.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_BLEND);
glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
// Draw texture.

这篇关于LibGDX 纹理混合与 OpenGL 混合功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆