سؤال

I'm experimenting with the Cocos2d HTML5 framework but believe this is a standard WebGL problem I cannot pinpoint as I don't have good knowledge on the topic. If I have a standard PNG image with semi-transparent pixels such as soft edges, and if the background (canvas or other element behind RenderTexture) is filled with a non-black color, the sprite when drawn to the RenderTexture ends up soaking in the background even when it shouldn't. For instance, if the graphic I'm drawing is red and I use a green background, the translucent pixels eventually morph to yellow. See these pictures for illustration:

Appearance when against a solid black background, this is what I desire Appearance when against a solid black background, this is what I desire

Appearance against a non-black background, with the bg bleeding through Appearance against a non-black background, with the bg bleeding through

Whatever is in the background will show through when I place any non-100% opaque image.

The full JavaScript-based RenderTexture class code can be seen on the cocos2d-html5 Github Repository under cocos2d/misc_nodes/CCRenderTexture.js. It is a conversion from Cocos2d-x C++ / Objective C (which do not exhibit this problem). There was a reported issue #937 on cocosd-iphone issue tracker describing the same problem for the non-HTML version several years ago. It was since resolved but I can't seem to determine what the resolution was or how it could be translated to the WebGL format. I'm sure it's a blend issue, but I have tried changing the blend modes (from GL_ONE, GL_ONE_MINUS_SRC_ALPHA to GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA and others) to no avail.

Any help from OpenGL / WebGL experts would be much appreciated. If I use the standard "sprites" in Cocos2d outside of this RenderTexture the problem does not exist. Also, the MotionStreak functionality seems to work without fault too, but I require RenderTexture to create dynamic textures for further use.

هل كانت مفيدة؟

المحلول

WebGL defaults to the same as images and canvas 2D, namely it expects the contents of the canvas to be RGBA, it expects RGB to be premultiplied by the alpha (in other words if the alpha is zero the RGB has to also be zero since anything times zero equals zero). And finally blends with whatever is behind it with GL_ONE, GL_ONE_MINUS_SOURCE_ALPHA.

If you want want it different you have 2 options

  1. Tell WebGL not to have alpha

    You do this by creating a context with no alpha

    gl = canvas.getContext("experimental-webgl", { alpha: false });
    

    This is probably the best option for your case. Since there will be no alpha there's no blending required which is faster than blending with the background which is what the browser normally has to do.

  2. Tell WebGL your RGB values are not premultiplied

    gl = canvas.getContext("experimental-webgl", { premultipledAlpha: false });

    This means the browser will still blend your WebGL canvas with the background, it will just do it with GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA instead. You'll still see the green behind.

There's a few other solutions that might be useful in special cases

  1. If you need alpha in the destination for some calculations you can clear the alpha to 1 when you're done

    ...render scene...
    
    gl.colorMask(false, false, false, true);
    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    

    The blending will still be happening you just won't see it because all your alpha is set to 1. Not the best solution but if you need destination alpha it might be your only solution.

  2. If you don't want to see through to stuff behind the canvas then give the canvas a background color

    <canvas style="background-color: green;"></cavnas>
    

    Setting the canvas' background color to say black would also do it but that's also a waste because it would still be blending your canvas with black. If that's what you want then just turn the alpha off in the first place.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top