Decals, blending and gbuffer

I would just like to brainstorm a bit, it is more generic opengl question that anything jme3 specific really.

I’m slowly getting near the end of my deferred renderer. After playing with deferred lightning, I have decided it is a waste of processing and went for rich gbuffer instead. Still, I’m trying to optimize it a bit, so I have packed things into smaller amount of buffers. Among them, I have

albedo [R,G,B] reflectiveness [A]

It works fine. Now I’m trying to add decal system. Basics are clear - draw small bounding box, do a projection of texture on underlying gbuffer (with possible material id showing if it should be applied or not). Problem is that for color decal, I would like to blot both RGB and reflection parts. At same time, decals need to have soft edges, so they need some kind of alpha blending.

Ideal solution would be to compose RGB using some kind of decal alpha and then compose separate reflectiveness value onto same buffer, using same alpha value. But everything seems to be hardcoded to assume that alpha I’m trying to blend into A channel is also a number I want to use to scale all the components.
For pure RGB composition, I could use https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBlendFuncSeparate.xml and to specify A to be left as destination number. This would allow me to specify decal blending factor as Alpha. But this will not allow me to compose reflectiveness.
I was considering two-pass solution for decals which would need to modify reflectiveness. But I cannot find any combination of opengl calls which would allow me to do A = x1*(x2) + Ad*(1-x2), where x1 and x2 differ per pixel in second pass.
Other possibility is to copy all the buffers, so I can do blending manually - but it feels like a lot of waste and doesn’t help in case of overlapping decals.

How people are doing blending in case of gbuffers using alpha channel for non-transparency purposes?

Hm, just a quick idea i had, you could use additive blending for the alpha channel. It would give you a small amout of control only however. Basically you can increase/decrease reflectiveness only… And it would require to have special alphamaps for the decals.

Some research:

Reading about texture barriers right now.

It turned out that simpliest solution just works. I don’t know why I thought that I cannot bind same buffer as output framebuffer and as input texture. It works without issues as long as you don’t do multiple operations on same place - and only then you need texture barrier to avoid inconsistent behaviour.

If you go the imageload/store way, a texure barrier is not required as long you modify the same pixel you read.

At least following the specs.

@abies said: It turned out that simpliest solution just works. I don't know why I thought that I cannot bind same buffer as output framebuffer and as input texture. It works without issues as long as you don't do multiple operations on same place - and only then you need texture barrier to avoid inconsistent behaviour.

Hi, i am currently not at home, but as far i remember the spezification say that the output of such operations is “unspecified and might produce garbage”.

I would kind of guess that that the reading from the buffer is happening immediately without waiting on possible past writes. At least you should test it on a few different cards before using it, since it sounds like the vendor can do whatever he wants in that case.

What bothers me most in that usage is that all others use “ping pong” for postprocessing which would not be required if your method works reliable. (At least not for some filters)
You could even have gpu particles without any buffer ping pong if you are allowed to writen/read to the same texture on gl2 hardware…

All very suspecious, but if it works it would be possible to save some fbo texture switches in the jme3 postprocessor

Main issue is that you cannot have overlapping objects with this approach. Everything you write has to touch distinct pixels. If you want to touch same pixels, you need to call texture barrier in between. Sample they are giving is calling for
[java]
dirtybbox.empty();
foreach (object in scene) {
if (dirtybbox.intersects(object.bbox())) {
TextureBarrierNV();
dirtybbox.empty();
}
object.draw();
dirtybbox = bound(dirtybbox, object.bbox());
}
[/java]
Dirtybox computation might be a killer here.
That said, it is still better than ping-pong approach. With ping-pong, you either switch buffers between each draw (which is considerably heavier than texture barrier) or keep reading from stale buffer (which is similar situation as not having texture barrier - but probably you are consistently wrong, while with using same buffer you might randomly see some recent updates or not).

But yes, it requires testing on few cards at least. On the other hand, NV_texture_barrier seems to be supported on every sane card in last 6+ years or so.

Yet another extensions which can help
http://www.opengl.org/registry/specs/ARB/blend_func_extended.txt
This looks exactly like what I was looking for originally.