LibGDX 在 FrameBuffer 上绘制的纹理比原始纹理暗
LibGDX Texture drawn on FrameBuffer is darker then original Texture
更新 1
如您所见LibGDX 与直接在 SpriteBatcher 上绘制的纹理相比,在 FrameBuffer 上绘制然后在 SpriteBatcher 上绘制的纹理比原始纹理更暗。
左边部分是原图,比phone 1080x1920屏幕小50%。
中间部分是直接从SpriteBatch上的TextureRegion中绘制出来的,水平镜像,以便更好地与左右图像进行比较。它有点失真,但我启用了线性过滤,您可以在代码中看到:
texture = new Texture("texture.png");
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
image = new TextureRegion(texture, 0, 0, 1024, 1024);
batcher.begin();
batcher.draw(image, 0, 0, screenWidth, screenHeight);
batcher.end();
右边部分是先在FrameBuffer上绘制TextureRegion,再在SpriteBatch上绘制,如下代码:
texture = new Texture("texture.png");
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
image = new TextureRegion(texture, 0, 0, 1024, 1024);
pixmap = new FrameBuffer(Pixmap.Format.RGBA8888, screenWidth, screenHeight, false);
pixmapregion = new TextureRegion(pixmap.getColorBufferTexture(), screenWidth, screenHeight);
pixmap.begin();
batcher.begin();
batcher.draw(image, 0, 0, screenWidth, screenHeight);
batcher.end();
pixmap.end();
batcher.begin();
batcher.draw(pixmapregion , 0, 0, screenWidth, screenHeight);
batcher.end();
如您所见,它比直接从纹理绘制的图像暗一点。当我用着色器绘制它时,与用着色器绘制的纹理相比,结果真是一团糟。
所以真正的问题是 FrameBuffer 对纹理做了什么,弄乱了它,所以它不能用于着色器。我需要先在像素图上绘制它,这样我就可以混合两个透明的 FrameBuffers。一直以来我都认为问题出在着色器上,我花了 3 天时间才找到问题所在,所以如果有人能帮助我,我将不胜感激! :)
注意:我尝试在 FrameBuffer 上绘制时启用混合,首先使用 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT) 清除它;但没有什么不同。
更新 2
看起来我应该做些什么,在 FrameBuffer 上绘制时设置一种 GL 混合,在绘制 FrameBuffer 时设置另一种混合,但我还不能得到正确的组合...
老问题
左图是用Photoshop制作的,alpha透明度,但我把背景设为黑色,这样可以看得更清楚。右图是 android 屏幕的屏幕截图。这是我的代码,我首先在像素图上绘制纹理,然后在屏幕上绘制该像素图,纹理质量非常差,alpha 通道非常扭曲,如您所见。
texture = new Texture("drop-blue.png");
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
image = new TextureRegion(texture, 0, 0, 1024, 1024);
pixmap = new FrameBuffer(Pixmap.Format.RGBA8888, screenWidth, screenHeight, false);
pixmapregion = new TextureRegion(pixmap.getColorBufferTexture(), screenWidth, screenHeight);
pixmap.begin();
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batcher.begin();
batcher.draw(image, screenWidth/2 - 512, screenHeight/2 - 512, 1024, 1024);
batcher.end();
pixmap.end();
batcher.begin();
batcher.draw(pixmapregion, 0, 0, screenWidth, screenHeight);
batcher.end();
这是正常现象,也是混合的工作原理。您正在将具有透明度的纹理渲染到具有透明度的帧缓冲区上,然后再将其渲染到屏幕(后缓冲区)上。这会导致应用两次混合。
让我们通过查看单个像素如何从纹理转换到 fbo 再到屏幕来了解实际发生的情况。首先看看渲染到 FBO 时会发生什么:
- 假设纹理中的像素是
[r:0.5, g:1.0, b:0.0, a:0.75]
,这将是我们的源像素。
- 当您将其渲染到帧缓冲区时,目标像素最初是
[r:0.0, g:0.0, b:0.0, a:0.0]
,即您的 glClearColor
。
- 混合函数默认用于源
SRC_ALPHA
和目标 ONE_MINUS_SRC_ALPHA
。
- 渲染到 FBO 时,这将导致:
[r:0.75 * 0.5, g:0.75 * 1.0, b:0.75 * 0.0, a: 0.75 * 0.75] + [r:(1 - 0.75) * 0.0, g:(1 - 0.75) * 0.0, b:(1 - 0.75) * 0.0, a:(1 - 0.75) * 0.0]
,从而导致 [r:0.375, g:0.75, b:0.0, a:0.5625]
现在让我们看看将 FBO 渲染到屏幕时会发生什么:
- 源像素为:
[r:0.375, g:0.75, b:0.0, a:0.5625]
- 目标像素(屏幕的 glClearColor)是
[r:0, g:0, b:0]
(无 alpha 通道)
- 所以混合结果将是:
[r:0.211, g:0.422, b:0.0]
(无 alpha 通道)
注意结果如何变得越来越暗。这是因为它与原始颜色混合,在您的情况下是黑色。如果你使用例如白的话它会越来越亮
如果您不喜欢这种行为,则可以使用 Batch#disableBlending()
完全禁用混合。如果您只想混合一次,那么您可以使用不带 alpha 通道的 FBO(例如 RGB888
而不是 RGBA8888
)。或者,如果您想自定义行为,则可以使用 Batch#setBlendFunction
.
使用不同的混合函数
顺便说一句,当我打这个的时候,我记得我以前做过。这是几乎相同的问题和答案:
更新 1
如您所见LibGDX 与直接在 SpriteBatcher 上绘制的纹理相比,在 FrameBuffer 上绘制然后在 SpriteBatcher 上绘制的纹理比原始纹理更暗。
左边部分是原图,比phone 1080x1920屏幕小50%。
中间部分是直接从SpriteBatch上的TextureRegion中绘制出来的,水平镜像,以便更好地与左右图像进行比较。它有点失真,但我启用了线性过滤,您可以在代码中看到:
texture = new Texture("texture.png");
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
image = new TextureRegion(texture, 0, 0, 1024, 1024);
batcher.begin();
batcher.draw(image, 0, 0, screenWidth, screenHeight);
batcher.end();
右边部分是先在FrameBuffer上绘制TextureRegion,再在SpriteBatch上绘制,如下代码:
texture = new Texture("texture.png");
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
image = new TextureRegion(texture, 0, 0, 1024, 1024);
pixmap = new FrameBuffer(Pixmap.Format.RGBA8888, screenWidth, screenHeight, false);
pixmapregion = new TextureRegion(pixmap.getColorBufferTexture(), screenWidth, screenHeight);
pixmap.begin();
batcher.begin();
batcher.draw(image, 0, 0, screenWidth, screenHeight);
batcher.end();
pixmap.end();
batcher.begin();
batcher.draw(pixmapregion , 0, 0, screenWidth, screenHeight);
batcher.end();
如您所见,它比直接从纹理绘制的图像暗一点。当我用着色器绘制它时,与用着色器绘制的纹理相比,结果真是一团糟。
所以真正的问题是 FrameBuffer 对纹理做了什么,弄乱了它,所以它不能用于着色器。我需要先在像素图上绘制它,这样我就可以混合两个透明的 FrameBuffers。一直以来我都认为问题出在着色器上,我花了 3 天时间才找到问题所在,所以如果有人能帮助我,我将不胜感激! :)
注意:我尝试在 FrameBuffer 上绘制时启用混合,首先使用 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT) 清除它;但没有什么不同。
更新 2
看起来我应该做些什么,在 FrameBuffer 上绘制时设置一种 GL 混合,在绘制 FrameBuffer 时设置另一种混合,但我还不能得到正确的组合...
老问题
左图是用Photoshop制作的,alpha透明度,但我把背景设为黑色,这样可以看得更清楚。右图是 android 屏幕的屏幕截图。这是我的代码,我首先在像素图上绘制纹理,然后在屏幕上绘制该像素图,纹理质量非常差,alpha 通道非常扭曲,如您所见。
texture = new Texture("drop-blue.png");
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
image = new TextureRegion(texture, 0, 0, 1024, 1024);
pixmap = new FrameBuffer(Pixmap.Format.RGBA8888, screenWidth, screenHeight, false);
pixmapregion = new TextureRegion(pixmap.getColorBufferTexture(), screenWidth, screenHeight);
pixmap.begin();
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batcher.begin();
batcher.draw(image, screenWidth/2 - 512, screenHeight/2 - 512, 1024, 1024);
batcher.end();
pixmap.end();
batcher.begin();
batcher.draw(pixmapregion, 0, 0, screenWidth, screenHeight);
batcher.end();
这是正常现象,也是混合的工作原理。您正在将具有透明度的纹理渲染到具有透明度的帧缓冲区上,然后再将其渲染到屏幕(后缓冲区)上。这会导致应用两次混合。
让我们通过查看单个像素如何从纹理转换到 fbo 再到屏幕来了解实际发生的情况。首先看看渲染到 FBO 时会发生什么:
- 假设纹理中的像素是
[r:0.5, g:1.0, b:0.0, a:0.75]
,这将是我们的源像素。 - 当您将其渲染到帧缓冲区时,目标像素最初是
[r:0.0, g:0.0, b:0.0, a:0.0]
,即您的glClearColor
。 - 混合函数默认用于源
SRC_ALPHA
和目标ONE_MINUS_SRC_ALPHA
。 - 渲染到 FBO 时,这将导致:
[r:0.75 * 0.5, g:0.75 * 1.0, b:0.75 * 0.0, a: 0.75 * 0.75] + [r:(1 - 0.75) * 0.0, g:(1 - 0.75) * 0.0, b:(1 - 0.75) * 0.0, a:(1 - 0.75) * 0.0]
,从而导致[r:0.375, g:0.75, b:0.0, a:0.5625]
现在让我们看看将 FBO 渲染到屏幕时会发生什么:
- 源像素为:
[r:0.375, g:0.75, b:0.0, a:0.5625]
- 目标像素(屏幕的 glClearColor)是
[r:0, g:0, b:0]
(无 alpha 通道) - 所以混合结果将是:
[r:0.211, g:0.422, b:0.0]
(无 alpha 通道)
注意结果如何变得越来越暗。这是因为它与原始颜色混合,在您的情况下是黑色。如果你使用例如白的话它会越来越亮
如果您不喜欢这种行为,则可以使用 Batch#disableBlending()
完全禁用混合。如果您只想混合一次,那么您可以使用不带 alpha 通道的 FBO(例如 RGB888
而不是 RGBA8888
)。或者,如果您想自定义行为,则可以使用 Batch#setBlendFunction
.
顺便说一句,当我打这个的时候,我记得我以前做过。这是几乎相同的问题和答案: