iPhone使用OpenGL ES 2.0与Cocos2D混合时出现意外结果

5
我有一个非常简单的CCScene,只包含一个CCLayer,其中包含以下内容:
  1. 使用标准混合模式的背景CCSprite
  2. 用于绘制画笔的CCRenderTexture,并将其精灵附加到位于背景精灵上方的根CCLayer:
_bgSprite = [CCSprite spriteWithFile:backgroundPath];
_renderTexture = [CCRenderTexture renderTextureWithWidth:self.contentSize.width height:self.contentSize.height];
[_renderTexture.sprite setBlendFunc:(ccBlendFunc){GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}];
[self addChild:_bgSprite z:-100];
[self addChild:_renderTexture];

画笔渲染代码:

[_renderTexture begin];
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE); // 1.
// calculate vertices code,etc...
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)count);
[_renderTexture end];

当用户使用第一个有颜色的画笔时,它会如预期地与背景混合。但是当继续使用另一种颜色的画笔覆盖在之前的画笔上时,会出现问题(两个画笔重叠时,边缘变得模糊失去不透明度)。
我尝试了许多混合选项,但是无论如何都找不到正确的方法。
CCRenderTexture是否有特殊的功能,不能像预期那样与自己(以前绘制的内容)混合?
我的用于涂抹的片段着色器只是带有保留输入颜色alpha的纹理标准着色器的轻微更改:
void main() { gl_FragColor = texture2D(u_texture, v_texCoord); gl_FragColor.a = v_fragmentColor.a; }
更新-几乎完美的解决方案:由jozxyqk
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                        GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

在渲染代码中(代替//1.和) [_renderTexture.sprite setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}]; 这样做非常好,可以得到我想要的效果...
但是只有当_rederTexture完全不透明时才有效。
_rendertexture.sprite的不透明度降低时,刷子会变亮,而不是像人们所期望的那样淡出:
为什么在父纹理完全不透明时,画笔的alpha值与背景正确混合,但在不透明度降低时却失效?如何使刷子与背景正确混合?
1个回答

9

混合画笔 -> 图层 -> 背景

好的,现在发生的是glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)用于将画笔笔划混合到画笔纹理中,但是生成的纹理中的 alpha 值不正确。每个添加的片段需要 1. 将其 alpha 添加到最终 alpha 值中 - 它必须精确地删除相应的光以进行交互;2. 通过余数对先前的 alpha 进行缩放 - 先前的表面减少了先前值的光,但由于添加了新的表面,它们要减少的光比以前更少。我不确定这是否有意义,但它会导致这种情况...

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

现在,画笔纹理的颜色通道包含要与背景混合的总颜色(经过 alpha 预乘),而 alpha 通道则给出权重(或者说是颜色遮盖背景的程度)。由于颜色已经经过 alpha 预乘,因此默认的 RenderTexture 混合 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA 再次按比例缩放 alpha,从而使整体颜色变暗。现在,您需要使用以下函数将画笔纹理与背景混合,该函数必须在 Cocos2D 中设置:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

希望这是可能的。我还没有仔细考虑如何管理设置画笔纹理与GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA混合的可能性,但这可能需要一个浮点纹理和/或一个额外的传递来分割/规范化alpha,听起来很痛苦。
或者,在绘制之前将背景涂抹到渲染纹理中,并保留所有层而不进行任何混合。
这对我有用:
glDisable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);

fbo.bind();
glClear(GL_COLOR_BUFFER_BIT);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
drawTexture(brush1);
drawTexture(brush2);
fbo.unbind();

drawTexture(grassTex); //tex alpha is 1.0, so blending doesn't affect background
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
drawTexture(fbo.getColour(0)); //blend in the brush layer

在GL中的小测试

画笔图层不透明度

使用 GL_ONE, GL_ONE_MINUS_SRC_ALPHA 会导致库在图层混合中实现不透明度时出现问题,因为它假设颜色已乘以 alpha。通过减小 opacity 值,可以在混合过程中缩小画笔图层的 alpha。然后 GL_ONE_MINUS_SRC_ALPHA 会导致背景颜色的量增加,而 GL_ONE 则会将 100% 的画笔图层相加并使图像过饱和。

我认为最简单的解决方案是找到一种方法来自己缩放全局图层不透明度的颜色,并继续使用 GL_ONE, GL_ONE_MINUS_SRC_ALPHA

  • 如果库支持,则实际上使用 GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA 可能是一个答案,但显然它不支持。
  • 您可以使用固定管线渲染来缩放颜色:glColor4f(opacity, opacity, opacity, opacity),但这将需要第二个渲染目标并手动进行混合,类似于上面的代码,其中您一次绘制全屏四边形用于背景,再一次用于画笔图层。
  • 如果您手动进行混合,则使用片段着色器而不是 glColor 方法更加健壮。如果您想播放更复杂的混合函数,特别是涉及到 0 到 1 范围之外的除法和临时变量的情况:gl_FragColour = texture(brushTexture, coord) * layerOpacity;

编辑结束


标准的 alpha 混合函数是 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,不完全是 GL 的“初始/默认”函数。

像您在 glBlendFuncSeparate 中所做的那样对 alpha 值求和将会使 alpha 过饱和,并且底部颜色完全被替换。 饱和混合可能会产生不错的结果:glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE)。 如果支持,则还可以尝试使用 glBlendEquationSeparate 和 MAX 混合。 使用 MAX 的优点是可以减少线条绘制代码中的重叠伪影(硬三角形部分) - 例如替换颜色,但仅在达到总 alpha 值 X 之前。 编辑:这两种情况都需要在每次笔画后进行混合和清除

我只能假设将渲染纹理混合到背景中实际上正在工作。 (对于当前图层值不起作用)

顺便提一下,与此大部分无关的是"Under Blending",其保持透射率值而非阿尔法/不透明度(来自这里):

glBlendEquation(GL_FUNC_ADD); 
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); 

我假设你指的是边缘变暗。当混合纹理时,通过归一化(将颜色除以 alpha 值)可以解决这个问题,但我猜你无法控制它。如果你能够混合并保存笔刷纹理和背景,允许笔画之间进行 alpha 混合,但在笔画内部保持饱和度,那就更好了。为什么标准的 alpha 混合不起作用呢? - jozxyqk
是的,我指的是边缘周围的变暗。为什么 alpha 混合不起作用呢?如果我知道答案,我就不会问这个问题了。我是 Cocos2D 和 OpenGL 的新手,尝试在这个非常简单的场景中使用标准混合模式。我不知道 Cocos2D 是否在幕后更改了一些东西,或者我是否做错了什么。 - Lukasz
@Lukasz,请查看编辑。更改笔刷渲染纹理的混合功能是否容易(例如http://www.cocos2d-iphone.org/forums/topic/ccrendertexture-blendingalpha-problems-on-iphone-4s/)? - jozxyqk
请看我的第三次更新。您最新的混合设置在渲染纹理不透明度为100%时完美运作。但是当我将其降低时,画笔会以疯狂的方式变亮(而不是像预期的那样逐渐消失)。您能解释一下这是为什么吗? - Lukasz
由于画笔纹理包含了完整的颜色,并且使用了GL_ONE,因此没有缩小比例。如果减少alpha值而不减少颜色,则背景不会被减去,而画笔会被完全添加,因此更亮。如果您找到一种方法将颜色按相同的不透明度缩小,则可以解决这个问题。不幸的是,看起来没有简单的方法可以做到这一点:http://www.cocos2d-iphone.org/forums/topic/gl_constant_color-support-for-ccblendfunc/。 - jozxyqk
显示剩余8条评论

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