Sprite blendmodes not working correctly

Thank you so much, @Deltakosh for fixing/incorporating the blendMode property for the SpriteManager! I just this morning finally went to make the changes you suggested, and saw that you had already done it!

However, the blend modes do not behave as expected, unfortunately. I guess that may mean removing the feature before 4.1, but if we can make it work, it would be incredible.

Here is the look we had achieved before using particles and a little circular fire sprite:
The sprite
fire
The look we had with particles

We were using the ONEONE mode to add the particles over one another and the terrain below, so that the fire gets brighter where the particles were denser. When they are animated, the result is very pleasing. We moved to sprites because they solved performance problems, but as you can see in the playground below, the sprites block one another despite the blend mode.

Let me know if I can be of any help.
https://www.babylonjs-playground.com/#9RI8CG#110

@Deltakosh That’s because of those lines in SpriteManager.render:

effect.setBool("alphaTest", true);
engine.setColorWrite(false);
engine.drawElementsType(Material.TriangleFillMode, 0, (offset / 4) * 6);

It renders the sprite in the zbuffer, so breaks the blending that comes afterward:

engine.setColorWrite(true);
effect.setBool("alphaTest", false);

engine.setAlphaMode(this._blendMode);
engine.drawElementsType(Material.TriangleFillMode, 0, (offset / 4) * 6);
engine.setAlphaMode(Constants.ALPHA_DISABLE);

Maybe those 3 lines of code should only be executed if this._blendMode === Constants.ALPHA_DISABLE ?

However, it would be a breaking change as those lines were executed previously when _blendMode = Constants.ALPHA_COMBINE, so I don’t really know…

1 Like

Maybe:

if (this._blendMode === Constants.ALPHA_DISABLE || this._blendMode === Constants.ALPHA_COMBINE) {
    effect.setBool("alphaTest", true);
    engine.setColorWrite(false);
    engine.drawElementsType(Material.TriangleFillMode, 0, (offset / 4) * 6);
}

to stay backward compatible?

Or:

effect.setBool("alphaTest", true);
if (this._blendMode === Constants.ALPHA_DISABLE || this._blendMode === Constants.ALPHA_COMBINE) {
    engine.setColorWrite(false);
    engine.drawElementsType(Material.TriangleFillMode, 0, (offset / 4) * 6);
    effect.setBool("alphaTest", false);
}

engine.setColorWrite(true);
engine.setAlphaMode(this._blendMode);
engine.drawElementsType(Material.TriangleFillMode, 0, (offset / 4) * 6);
engine.setAlphaMode(Constants.ALPHA_DISABLE);

as I think we need alpha test enabled for the new cases (meaning, when the if branch is false)?

Use a real transparent mesh, not one that is discarding the black?

https://www.babylonjs-playground.com/#9RI8CG#110

You linked the same PG than @IIerpos.

It will indeed work with a picture that has a real alpha channel, but only up to a point (the alpha test is a hard test, everything above the cutoff value will still write in the zbuffer):

https://www.babylonjs-playground.com/#9RI8CG#111

In hindsight, it still won’t work as @IIerpos would like, because the colors won’t add up as the zbuffer will prevent the farer sprites to contribute. The zbuffer should really be disabled when rendering geometries with alpha enabled.

Whoops, sorry I was running out the door to get to physical therapy and must have not hit save.

Im on my phone now so:

Just use like sun.png and set mode to combine. It uses the alpha channel and has the behavior he wants.

Flair.png has just black so it’s using that as a discard.

Here we go:
https://www.babylonjs-playground.com/#9RI8CG#113

Then just drop the alpha of the png down globally in your image editing program, to make them more transparent, make sure each pixel has an alpha value under 1, then you can take advantage of the color.a on the sprite to drop its global opacity.

https://www.babylonjs-playground.com/#9RI8CG#114
The glitches you are seeing here are where the sprite has a 1 alpha on the texture I believe. Needs to be like 0.998 or something if its “opaque” and still be able to use the color.alpha channel. That is because it is a scalar and if its 1 it wont do any scaling…

Here’s a comparison with the current code and the one that does not write into the zbuffer:

Current:

Don’t write to Zbuffer:

Both are using ALPHA_COMBINE.

The rendering with the current code depends on the order with which the sprites are drawn.

When a sprite has been drawn, all other sprites that are farer than this sprite won’t contribute to the final color (because of the zbuffer rejection), and all that are nearer will. So, the final color depends on the current view and the corresponding sprite rendering order (sprites are not sorted before rendering).

https://www.babylonjs-playground.com/#9RI8CG#115

seems to be sorting to me.

https://www.babylonjs-playground.com/#9RI8CG#116

Ok, there’s no sorting but the alpha cutoff value is a hard value set to 0.95 in the sprite fragment shader, that’s why setting an alpha value lower than 0.95 does work (no zbuffer update), and values above that one makes the sprite to render into the zbuffer.

So, I suppose it all boils down to know if supporting pictures without an alpha channel should be supported by the blendMode parameter in the sprite manager or not.

Note @Pryme8: there’s a console.log in _makePacked, not sure it is intended?

1 Like

No i’m surprised that slipped through, I’ll fix it in the morning.

@IIerpos You can make your test case work by adding an alpha channel to the picture and putting values < 0.95.

Im wondering who chose the 0.95 as the arbitrary number. Shouldn’t it be something a little higher?

You guys are amazing! I did a little testing, and it does indeed work. I’ll set the alpha for now. Thanks SOOOOO much!

I think too, but changing it would be a breaking change, so not sure @Deltakosh would approve.

What we can do is adding a boolean to disable the depth write. False by default so everyone is happy

If you want to do it, do it now :wink:

PR created: