How to make a mesh show through another but be occluded by everything else in front

I’m trying to create an effect where I can have a disc with a texture inside the sphere, but it is always visible through the sphere. I’ve been able to achieve this several ways including putting the disc on a higher render group id, using stencil buffer, etc… However, all these methods result in the disc showing through every other mesh in the scene that isn’t in the same rendering group. I’m looking for a solution where the disc is always visible as long as it’s only occluded by the sphere. If anything other than the sphere is in front it should not be visible. This would include other instances of the sphere and planes. I also need the disc to be centered within the sphere, as I am using billboard mode to ensure that it always faces the camera. I thought I could achieve this by just forcing the plane to render before or after the sphere within the same rendering group and just rely on depth sorting for everything else, but it isn’t working. I have verified through the debugger that the render order function is returning a 1 or -1 when the plane and sphere are being compared, but in both cases, the sphere blocks the plane.

1 Like

Using the stencil buffer should work, if you write 1 to the stencil where the sphere is drawn and only draw the disc where the stencil is 1.

Note that you should also disable depth testing for the disc, else it will be culled by the sphere itself.

To be sure that the disc is drawn after the sphere, you don’t need to change the rendering sort order but simply make sure that the material.uniqueId of the disc is greater than the one of the sphere, as the default mesh ordering is based on material.uniqueId.

Here’s a PG that does this:

2 Likes

Thanks. While this works in that the inner disc shows through the sphere and is occluded by the disc, I plan to have a number of instances of these spheres (created through AssetContainer.instantiateModelsToScene). I simulated this in this PG https://playground.babylonjs.com/#GRUWFA#19. It appears that the inner disc also will show through the cloned sphere. Ultimately each of these sphere instances would have a different texture on the inner disc and I want to make sure that the inner disc only shows through the parent sphere but not any other sphere instances that are in front of it.

I also don’t understand why in this PG, the inner disc shows through the cloned sphere when the cloned sphere does not write to the stencil buffer.

Indeed, I had a bug, it’s a little more involved. Also, your cloning code for the material is wrong because you must clone the materials of all the objects that are loaded, not only the root (which in fact you are not interested to clone the material because this node is not a real mesh).

So, to make it work:

  • render each sphere that must have a disc to the stencil buffer with a stencil value of 1 for the first sphere, 2 for the second, etc
  • render the discs after all other objects. For the disc of the first sphere, use a stencil test value of 1 to render the disc only at the sphere 1 location, use a stencil test value of 2 to render the disc 2 only at the sphere 2 location, etc

Note that all objects except the spheres and their discs should write 0 to the stencil buffer, so that in the end you only have stencil values of 1/2/… where the spheres 1/2/… are visible and 0 everywhere else.

Here’s the PG with two spheres/discs:

Note that it will only work with up to 255 spheres as the stencil has only 8 bits. Also, it’s working because we are cloning the meshes. If you want to use instances instead as you said in your post then I don’t see how to make it work, as all instances will use the same material than the master mesh and there’s no way around that…

2 Likes

This is great, thanks. Regarding instances, I’m doing container.instantiateModelsToScene((name) => ${instanceId}_${name}, false, { doNotInstantiate: true });, so I should be okay if I change the clone materials parameter to true?

Also, can I use render order in lieu of material unique ids to ensure that the disc renders after the sphere?

Yes you are good with cloneMaterials = true and doNotInstantiate: true.

You can use a custom render order function instead of the material unique id as long as you make sure all the discs are displayed after all other objects (the spheres they are linked too + any other objects in the scene).

2 Likes

Another follow up question. If I want the mesh to show through a mesh and all it’s children, I would need to set the stencil function on all of the mesh children materials, correct?

Yes indeed.

1 Like