Cocos2D iPhone OpenGL ES 2.0 Compatibility Gives Unexpected Results

I have a very simple CCScene with ONLY 1 CCLayer containing:

  • CCSprite for background with standard blending mode
  • CCRenderTexture for painting brushes with its sprite attached to the root CCLayer over the sprite background:
  _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];

Brush Rendering Code:

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

When the user brushes with the first color brush, it blends with the background as expected . But when when continuing to brush a different color on top of the previous brush, this happens incorrectly (soft alpha edges lose their opacity when 2 brushes overlap each other):

enter image description here

I tried a lot of mixing options, but for some reason I can’t find the right one.

Is there anything special in CCRenderTexture that doesn't mix with itself (with pre-drawn content) as expected?

My fragment shader used for brushing is just a standard texture shader with a few changes to keep the alpha input color in the texture:

  void main ()
 {
     gl_FragColor = texture2D (u_texture, v_texCoord);
     gl_FragColor.a = v_fragmentColor.a;
 }

UPDATE - TOTALLY PERFECT SOLUTION: by jozxyqk

 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 

in the rendering code (instead of // 1. and

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

THIS WORKS BIG AND GIVES ME WHAT I WANT ...

enter image description here

... BUT ONLY WHEN _rederTexture has full opacity.

When the opacity of _rendertexture.sprite goes down, the brushes become brighter and not fade, as you might expect:

enter image description here

Why does the alpha brush mix with the background correctly when the parent texture is in full opacity, but bananas go when lowering the transparency? How to make brushes to blend correctly with the background?

+4
source share
1 answer

EDIT

Blend Brush -> Layer -> Background

Well, what happens glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) works to mix brush strokes into the brush texture, but the alpha values ​​obtained in the texture are incorrect. Each added fragment should 1. add alpha to the final alpha value - it should remove exactly so much light for interaction and 2. scale the previous alpha with the rest - the previous surfaces reduce the light by the previous value, but since a new surface is added to reduce them. I'm not sure if that made sense, but it leads to this ...

 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 

Now the color channel of the brush texture contains the overall color that should be mixed with the background (previously multiplied by alpha), and the alpha channel gives the weight (or the amount that the color obscures the background). Since the color is pre-multiplied by alpha, the default blend RenderTexture defaults to GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA again scales with alpha and therefore darkens the overall color. Now you need to blend the brush texture with the background using the following function that I am going to install in Cocos2D:

 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 

Hope this is possible. I did not think about how to control the ability to create a brush texture so that it combines with GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA , but decomposition / normalization of alpha may require a floating-point texture and / or an additional passage that sounds painful.

Alternatively, select the background in your rendering texture before drawing and save the place there without mixing the layers.

This worked for me:

 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 

small test in GL

Brush opacity

Using GL_ONE, GL_ONE_MINUS_SRC_ALPHA causes problems with the implementation of the opacity library in blending layers, since it assumes that the color is multiplied by alpha. By decreasing the opacity , the alpha of the brush layer decreases during blending. GL_ONE_MINUS_SRC_ALPHA then increases the amount of background color, however GL_ONE adds up 100% of the brush layer and oversaturated the image.

The simplest imo solution is to find a way to scale the color using the opacity of the global layer and continue to use GL_ONE, GL_ONE_MINUS_SRC_ALPHA .

  • In fact, using GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA might be the answer if the library supports it, but apparently it is not.
  • You can use fixed pipelined rendering to scale the color: glColor4f(opacity, opacity, opacity, opacity) , but for this you will need a second rendering goal and manual combination, similar to the code above, where you draw a full-screen square once for the background and again for brush layer.
  • If you do the combination manually, it would be more reliable to use the fragment shader instead of the glColor method. This will significantly increase control if you ever wanted to play with more complex blending functions, especially when it comes to units and time series outside the 0: 1 range: gl_FragColour = texture(brushTexture, coord) * layerOpacity;

End edit


Standard alpha blend glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); , not really GL "start" / default function.

Summing alpha values, as in glBlendFuncSeparate, will oversaturate alpha, and the bottom color will be completely replaced. A saturation overlay can produce decent results: glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE) . It might be worth experimenting with glBlendEquationSeparate and MAX blending, if supported. The advantage of playing with MAX will be to reduce overlapping artifacts (hard triangular bits) from your line drawing code — for example, replacing a color, but only until you reach the general alpha value of X. EDIT: Both cases will require mixing and cleaning after each hit.

I can only assume that overlaying the render texture on the background actually works. (not for current level values)

On a side note and largely unrelated to it is also “Under Blending”, where you save the transmittance value instead of alpha / opacity (from here ):

 glBlendEquation(GL_FUNC_ADD); glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); 
+9
source

Source: https://habr.com/ru/post/1501605/


All Articles