Occluding only a specific object

I’m looking for a way to have a piece of geometry occlude only one specific other piece of geometry.

For example, presume that we have objects A, B and C, which could be drawn in any order. Presume that we also have a fourth object, D. I want D to occlude A, for example, without occluding B or C.

To be a little more specific, I have a set of objects that I intend to draw additively to an off-screen buffer, but I also want each object to potentially have regions “removed”. At the moment the best idea that I have for removing these regions is to overlay black objects of the appropriate shape on the additive objects, meaning that when drawn additively the objects should only have an effect outside of those regions.

However, I don’t want the regions of one object to affect any other object.

I could perhaps use an offscreen buffer for each addtive object, but, given that this is already being rendered into an offscreen buffer and that I don’t know how many additive objects I’m going to have, doing so seems as though it might be a little expensive.

I’ve also considered generating only the parts of the additive objects that are not in the regions removed, but since this is intended to happen each frame, that might get a little slow.

Does anyone have any suggestions?

bump No ideas?

Could you perhaps include a visual aid to help us understand what you’re trying to do?

Fair enough. :slight_smile:

In brief, I’m experimenting with a 2D lighting mechanism: I have an off-screen buffer that is fill with dark blue, and onto which are additively rendered textured circles representing lights. The results thus far can be seen here:

Now, I want to implement shadows. Since my game involves sneaking, I want my character to be considered “invisible” while in shadowed regions. I already have code that allows me to specify an arc, including inner and outer radius, and my obstacles are likely to be more-or-less square, so creating logical “shadows” from obstacles shouldn’t be too difficult: I simply treat each obstacle as a circle slightly larger than it really is and, for each overlapping light, place an arc with centre at the light’s centre, inner radius at the obstacle’s distance from the light and outer radius either larger than the level or at just above the light’s radius. If the character is either not in a light’s circle, or in a light’s circle but also in one of the relevant light’s shadows for all such lights, then the character is “invisible”; otherwise the character is “visible”.

Now, I want to include these shadows in the lighting system that I showed above. Since I already have “shadow arcs”, I would like to use these, having each arc create (perhaps using MeshDrawer) a piece of geometry that blacks out the relevant light-circle as appropriate.

In this image, the blue “shadow” should only affect light from the blue light, while the magenta “shadow” should only affect light from the magenta light. There should only be an absence of light where both shadows overlap.

However, if I simply add black geometry in front of the lights I run the risk of shadows appearing on lights other than their owners. I could perhaps use an off-screen texture for each light and render the shadows into those, but that seems to me as though it could be somewhat expensive. I could also try to define the arcs produced by the lights, but that looks more complex than just using the shadows (one ends up with various truncated arcs, overlapping shadows (such as obstacles partially shadowed by other obstacles) may be problematic; additionally, there’s the problem of preventing overlap at the edges of light arcs producing bright lines due to the additive rendering).

(If this technique fails me I’ll likely fall back on a tile-based approach, but that’s likely to be rather less pretty.)

I think this is what the stencil buffer is invented for; you’d have to make sure that your shadow geometry is drawn before the light spots are and be configured to write 1 to the stencil buffer; you could set up your light spots to only render where the stencil bit is 0.

I haven’t made much use of the stencil buffer before, so please correct me if I’m wrong, but wouldn’t that prevent any light from affecting those regions so marked, instead of just the light that is “casting” the shadow? Or is this something that I can apply to the light circle objects themselves?

If I’m right, could I have the shadows write an id bit/integer to the stencil buffer, and have the associated lights only draw where the relevant bit/integer is not set (but ignoring any other bit/integer)?

Stencil buffer typically is 8-bits, and I believe it’s not limited to bitmask operations so you can use up to 255 lights at the same time. Your StencilAttrib for the shadow object would be configured to replace the value in the stencil buffer with the number of the associated light, and the StencilAttrib for the light object would be configured to only write to the framebuffer if the stencil buffer value is not equal to the number of that light.

Ah, that looks like just the thing – thank you very much! :slight_smile:

It works well! :slight_smile:

I did encounter two oddities, however. Neither is a major problem, I believe, as the system seems to work as it stands, but both seem strange. Here they are:

b[/b]
It only seems to work properly with I use “StencilAttrib.makeWithClear”, presumably zeroing the buffer between objects – if I just use “StencilAttrib.make” I seem to end up with shadows occluding lights other than their own (presumably those rendered after them).

Each light is assigned an id (and which I’m confident are not colliding) which is used for the StencilAttribs, like so:

        self.lightID = Light.getNewID() # This value should be an integer between 1 and 255, inclusive
        
        self.stencilReader = StencilAttrib.make(True, StencilAttrib.SCFNotEqual,
                                                StencilAttrib.SOKeep,
                                                StencilAttrib.SOKeep,
                                                StencilAttrib.SOKeep,
                                                self.lightID, 1, 0)
        
        self.shadowStencilWriter = StencilAttrib.makeWithClear(True,
                                                      StencilAttrib.SCFAlways,
                                                      StencilAttrib.SOZero,
                                                      StencilAttrib.SOReplace,
                                                      StencilAttrib.SOReplace,
                                                      self.lightID, 0, 1,
                                                      1, 0)

I would expect that this would work without the “clear”, since each light’s shadows should write a value other than that light’s id; as long as the lights are rendered after their respective shadows, it looks as though the above should work with just “StencilAttrib.make”, rather than the use of “makeWithClear” in the writer.

b[/b]
It only seems to work with odd-numbered id values; lights given even id values don’t seem to render at all, even without the shadows writing to the stencil buffer, as I recall. I imagine that the last bit on the id is used for something, but it is nevertheless unexpected.

(1) Is the stencil clear enabled on the window? Perhaps the buffer contains garbage because there are no clears on the window or display region?

(2) That’s because your “mask” arguments are set to 1. This means that your stencil values are and-ed with 1 before comparison, which means that everything but the first bit is chopped off. You should set it to 255 instead.

(1) I believe that I did check that, and found that “StencilClearActive” was True and that “StencilClear” was 0. :confused:

(2) Ah, thank you! I wasn’t at all clear on how those masks were to be used.