5

我有非常简单的 CCScene,只有 1 个 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];
[自我 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];

当用户使用第一个彩色画笔刷时,它会按预期与背景混合。但是当在前一个画笔上继续刷另一种颜色时,它会出错(当 2 个画笔相互重叠时,软 alpha 边缘会失去不透明度):

在此处输入图像描述

我尝试了许多混合选项,但不知何故我找不到正确的选项。

CCRenderTexture 有什么特别之处,它不会像预期的那样与自身(与先前绘制的内容)融合吗?

我用于刷涂的片段着色器只是标准纹理着色器,稍作更改以保留纹理中的输入颜色 alpha:

无效的主要()
{
    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.and

[_renderTexture.sprite setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}];

这很好用,给了我想要的东西......

在此处输入图像描述

...但仅当 _rederTexture 完全不透明时。

当不透明度_rendertexture.sprite降低时,画笔会变亮,而不是像预期的那样淡出:

在此处输入图像描述

为什么当父纹理完全不透明时,画笔的 alpha 与背景正确混合,但当不透明度降低时会变成香蕉?如何使画笔与背景正确融合?

4

1 回答 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% 的画笔层并使图像过饱和。

imo 最简单的解决方案是自己想办法通过全局图层不透明度来缩小颜色并继续使用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”,您可以在其中保留透射率值而不是 alpha/opacity(来自此处):

glBlendEquation(GL_FUNC_ADD); 
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); 
于 2013-09-11T09:16:02.693 回答