[Solved] GBuffer alpha blending

Main question is in bold at the bottom.

I’m not sure if this is possible, but I figured I’d ask before giving up and reformatting my GBuffer. The setup is that I’m trying to write to 2 render targets of my GBuffer:

  • Target1 (RGB: albedo color, A: emissive)
  • Target2 (RGB: irrelevant, A: specular)

I’m trying to support 3 types of decals:

  1. Transparent (e.g. bullet hole)
  2. Transparent, emissive (e.g. glowing rune)
  3. Transparent, specular (e.g. puddle of water)

I’m not sure this is standard, but the way I have things working is that if a decal is emissive, the specular value doesn’t matter. However if a decal is specular, the emissive value must be 0.

So I’m trying to find a way to use alpha blending so that I can do this, and I have the first two so far:

  1. DestinationBlend is InverseSourceAlpha to perform alpha blending in Target1, then AlphaSourceBlend is BlendFactor to set emissive to 0. BlendFactor also sets specular to 0.
  2. Same as before, but BlendFactor sets emissive to 1. BlendFactor also sets specular to 1, but that’s fine.
  3. I need to perform alpha blending using alpha value A. I need to write a different alpha value 0 to emissive. And I can’t use the same BlendFactor trick because I need to write alpha value 1 to specular. Is there any good way of doing this?

I was looking at glColorMaski, which would be perfect, but it’s not supported in GLES 3.0 which is my primary target. My only other idea was that I could premultiply alpha, but then all the colors will get saturated since there’s no 1 - alpha to blend with.

I got it working! I had some extra space in my GBuffer, so I moved emissive to a different render target and left the albedo alpha channel open as a sort of “alpha workspace” so I can do alpha blending without worrying about overwriting something there. Now I can just use the NonPremultiplied blend state for all 3 types of decals, and I only see minor issues around the edges of effects where transparency causes emissive and specular values to blend kind of oddly, but it’s barely noticeable.

1 Like

This looks like it’s coming together nicely! :slight_smile:

Thanks! Just updated the blood in the image above to make it a little darker. Now all my friends are arguing over how reflective blood is.

It’ll be a long way before it’s anything playable.

1 Like

Looks cool!


I’ve been doing dungeons as well lately (albiet in C++ and not monogame because VR and my usage of C++/CLI won’t fly on android for GearVR).

If you want some easy pizzaz you could add some chamfer quads/trapezoids along the walls aligned the floor (or walls if they’re always going to be flat) with depth-test as equals to add a trim texture. This old video on OPAL should clarify what I mean.

I have some old java code for doing that as well as an old DoomBuilder plugin that dumps UDMF to XML to load maps with a LibGDX renderer. I never got around to pairing the two together but it’d be interesting and it might be useful stuff for you if you’re doing a dungeon-crawler, since the stuff fits the play-space and is pretty easy to embellish (especially with GZDoomBuilder that has more advanced positioning).

You could snaz up your walls (unless made in a 3d modeler) by creating 2d profiles of the wall and then extruding those along the wall edge. Caves are just a uniform subdivision with noise. Fairly handy since it just means creating filler models for angles that exceed some tolerance to hide that the wall mesh is restarting.

Also, you could use the stencil buffer to mask out your play area and then render a big quad that tracks the camera and is textured in worldspace aligned to the ceiling to fill in the black area. You’d have to draw your wall backfaces as stencil-only to clear the stencil though and then draw spheres or something for things that should be seen through the ceiling-plane into stencil only as well so the plane doesn’t overlap them. That filled-void-space in Diablo 1/2 provides a strong claustraphobic underground feeling ‘diablo 1 dungeons’ google image search. Add the same borders along the outsides of the tops of the walls and bam - claustrophobic feel.

Cool bit there is you only need to touch the stencil for your dungeon structure aside from the mask shapes for NPCs/PCs. Everything else can just go.

Lastly Metazelda is awesome for random gen (can merge connected blocks to build larger ones). I have a C++ port, I might have a C# port laying around … can’t remember.

1 Like

Thanks for the tips! I have quite a bit as far as room generation goes, dynamically generating floor and wall polys just from a few lines of code specifying door positions, path radius, and curvature. I do still need to add some noise for the walls to look more cave-like though.

I’ll definitely check out what you’re talking about with the stencil buffer. I’ve been using an image of one of my all time favorite RPGs, Neverwinter Nights 2, as a guide, which does a similar masking. My first instinct was to just generate polys that connect the tops of all the walls, so there’s actually a black cover to all the emptiness and you can’t see through the backfaces of walls. Maybe the stencil buffer could work better though. Do you know of a good way to draw wall backfaces to the stencil buffer?

It’s the ColorWrite stuff in BlendState, set it to zero to disable all color writing (the versions with number suffixes are for MRT).

In this case DepthStencilState can be set as depth readonly and stencil write just needs to be whatever value you pick. The catch is that stencil-test has to be off or set to match whatever your masked area is as stencil is checked before depth. Setting it to match will save you a lot of fragment work.

You can clear voids that the plane shouldn’t cover by drawing the equivalent of triangle fans in screenspace around an NPC/PC (obviously, don’t use fans because they suck for batching), you can add noise in the vertex shader to give it a wavey effect to hide what it is.

Oh of course, I knew that. I can’t use the MRT ColorWriteChannels because they’re not supported by GLES 3.0, but I can still use the normal ones.

So I have to do CPU testing to check if each wall is being seen from the front or the back, and draw the back ones like you say, with clockwise culling?

And unless I’m misunderstanding, I think it might look weird to show the PC through the void, because the whole point is that you’re blacking out the floor below him and everything else around, so he’d just be kinda floating.

I can’t use the MRT ColorWriteChannels because they’re not supported by GLES 3.0, but I can still use the normal ones.

Oh crap, I’m in trouble then. I thought GLES 3 was up to GL2 in par.

So I have to do CPU testing to check if each wall is being seen from the front or the back, and draw the back ones like you say, with clockwise culling?

Just change the culling mode and draw it again, no need for the CPU unless your stuff is already sorting things on the CPU.

And unless I’m misunderstanding, I think it might look weird to show the PC through the void, because the whole point is that you’re blacking out the floor below him and everything else around, so he’d just be kinda floating.

It might, that’s really down to whatever your game is and needs to feel like. I’m targeting VR so I must do that as spinning even a flat surface to see things is kind of nauseating.

I’m hoping to have all of this stuff up and rendering in a week, screenshots explaining what I mean would make a lot more sense.

glColorMaski is supported in GL 3.0 and GLES 3.2.

I suppose you’re right. I guess the only efficiency loss is that I have to iterate through all the walls and write them to GPU buffers, but they’ll be culled before any drawing occurs.

Looking forward to it!

@AcidFaucent
Actually, quick question. Can you explain what this means?

Suppose I’m writing ReferenceStencil = 1 for my void, and nothing else. Then I guess I’d want my StencilFunction to be NotEquals so that I never need to draw void on top of more void. Is that what you mean?

Well I got it working pretty simply. I’m just now remembering that I never decided if I wanted wall backfaces to occlude vision or just fade away naturally. There seem to be AAA games that do both.

Suppose I’m writing ReferenceStencil = 1 for my void, and nothing else. Then I guess I’d want my StencilFunction to be NotEquals so that I never need to draw void on top of more void. Is that what you mean?

If you mean to draw the backfaces to cut out those walls to be void again the test would be Equals with stencil lookup as 1 and the write as 0. So only places that really need to be cleared even get checked. With depthtest still enabled the backfaces won’t cut out good camera facing walls.

But that’s only if the void-filling plane should encroach like that. You might want it to only come up to the towards the floor but never overlap it.

Well I got it working pretty simply. I’m just now remembering that I never decided if I wanted wall backfaces to occlude vision or just fade away naturally. There seem to be AAA games that do both.

Everything about walls pretty much sucks. It feels like there’s no win to be had.