Occlusion/depth test issues with Point sprites, a skybox, and a Quad

So I’m making a galaxy with point sprites representing stars (thousands, not billions, obviously- for both rendering and gameplay reasons). I’ll be rendering this galaxy both somewhat close up and very far out (it should be possible to see the entire galaxy on the screen). To make it look not-crappy, in addition to the stars, I want to render a quad with a texture on the galaxy’s plane of the ecliptic (is that a term for galaxies? I know it’s used for solar systems) so it looks more solid than it would with just stars. I also want a skybox - realistically, very few other galaxies would be visible and they would be so distant as to appear as points, but people associate space with seeing a dense starfield, so a skybox is necessary.

I have all these rendering, but I’m running into some very odd depth problems. This is the code for creating the point sprite for a star, after its characteristics have been determined:

[java] private Geometry createStar(Vector3f color, Vector3f position,
float size, String texture, AssetManager assetManager) {
Mesh m = new Mesh();
m.setMode(Mesh.Mode.Points);

    m.setBuffer(VertexBuffer.Type.Normal, 3,
            BufferUtils.createFloatBuffer(Vector3f.ZERO));
    m.setBuffer(VertexBuffer.Type.Color, 3, 
            BufferUtils.createFloatBuffer(color));
    m.setBuffer(VertexBuffer.Type.Position, 3,
            BufferUtils.createFloatBuffer(new Vector3f(0f, 0f, 0f)));
    m.setBuffer(VertexBuffer.Type.TexCoord, 4,
            BufferUtils.createFloatBuffer(new Vector4f(0.0f, 0.0f, 1.0f, 1.0f)));
    m.setBuffer(VertexBuffer.Type.Size, 1,
            BufferUtils.createFloatBuffer(size));

    Geometry g = new Geometry("Star", m);
    g.setLocalTranslation(position);

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
    mat.setTexture("Texture", assetManager.loadTexture(texture));
    mat.setFloat("Quadratic", 20f);
    mat.setBoolean("PointSprite", true);
    mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
    mat.getAdditionalRenderState().setDepthTest(true);

    g.setMaterial(mat);
    
    return g;
}[/java]

Once a list of stars has been created, I create these and batch them together:

[java] SimpleBatchNode galaxy = new SimpleBatchNode();

    for (Star s : stars) {
        Geometry g = createStar(
                s.color, s.location, s.size, "Textures/Star2d.DDS", assetManager);
        galaxy.attachChild(g);
        s.sprite = g;
    }
    
    galaxy.batch();
    rootNode.attachChild(galaxy);[/java]

It uses alpha blend mode and (supposedly) its material is doing depth testing, but some stars are occluded when they should not be:

Upon further testing, it seems like if star A is created (and added to the scenegraph) before star B, B will always appear in front of A, regardless of their 3D locations.

Additionally, I use a quad to render a galaxy image I cobbled together from my star density map and Hubble pics. This is rendered on the galaxy’s plane of the ecliptic, with some stars below it and some above. This is the code I use to create it:

[java] private void createGalaxyImage(Node rootNode, AssetManager assetManager, int diameter) {
Material galaxyMat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
galaxyMat.setTexture(“ColorMap”, assetManager.loadTexture(“Textures/GalaxyLarge2.png”));
galaxyMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
galaxyMat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);

    Quad galaxyImage = new Quad(diameter, diameter, true);
    Geometry galaxyImageGeo = new Geometry("Galaxy", galaxyImage);
    galaxyImageGeo.rotate((float)(-Math.PI/2), 0, 0);
    galaxyImageGeo.move(-diameter/2, 0, diameter/2);
    galaxyImageGeo.setMaterial(galaxyMat);
    rootNode.attachChild(galaxyImageGeo);
}[/java]

This comes with its own rendering issues. The image has an alpha channel and its blendmode is set to Alpha, but it occludes the star PointSprites:

The weirdest problem arises when I render he skybox. This is the code to make it, which runs after both the galaxy and its quad are created/placed in the scenegraph (not sure if that’s relevant):

[java] Texture top = assetManager.loadTexture(“Textures/Galaxies_top.png”);
Texture bottom = assetManager.loadTexture(“Textures/Galaxies_bottom.png”);
Texture front = assetManager.loadTexture(“Textures/Galaxies_front.png”);
Texture back = assetManager.loadTexture(“Textures/Galaxies_back.png”);
Texture left = assetManager.loadTexture(“Textures/Galaxies_left.png”);
Texture right = assetManager.loadTexture(“Textures/Galaxies_right.png”);

    rootNode.attachChild(SkyFactory.createSky(assetManager, left, right, front, back, top, bottom));[/java]

And now, the galaxy image quad occludes the skybox as well as the stars, and it seems like stars are occluded by having the quad, rather than the skybox, behind them:

So, depth on point sprites isn’t being calculated correctly, a BlendMode.Alpha quad is occluding stuff, and pointsprites won’t render if the only thing behind them is the skybox. I’m not entirely new to the theory behind rendering stuff but I’m less familiar with implementation, especially in JME.

Depth is calculated correctly. It’s the sorting that is giving you problems. Sorting is done at the Geometry level. It can’t really be done otherwise.

It’s interesting that you specifically set depth test to true because it’s already true by default. It’s setting depth test to false that can fix this issue. Stars behind will be drawn in front of stars but they will all be drawn.

Another option, and probably useful for galaxies is to set depth test or depth writing off and then set the blend mode to alpha add. Then multiple stars overlaying each other will be additive… which might look nice for a galaxy.

For some use cases, setting alpha test and alpha fall-off can help but I think it will do more harm than good here.

I changed the depth test call from true to false, and nothing changes w.r.t. star-star sorting. I also do not want to use additive mode; not all stars are white in color, which means that brighter stars behind them would be visible.

@pspeed said:Stars behind will be drawn in front of stars but they will all be drawn.

That’s not a fix; that’s exactly what is happening, and what I’m trying to prevent.

[EDIT] Oops, I derped. This does solve the issue of the galactic plane quad obscuring stars (the quad still obscures the skybox). Star-star rendering is still incorrect, however.

@ShivanHunter said: I changed the depth test call from true to false, and nothing changes w.r.t. star-star sorting. I also do not want to use additive mode; not all stars are white in color, which means that brighter stars behind them would be visible.

That’s not a fix; that’s exactly what is happening, and what I’m trying to prevent.

[EDIT] Oops, I derped. This does solve the issue of the galactic plane quad obscuring stars (the quad still obscures the skybox). Star-star rendering is still incorrect, however.

If the quad is obscuring the sky box then you haven’t put it in the transparent bucket.

Try turning depth test off and using blend mode AlphaAdd. See what it looks like.

There is no perfect solution for this so you have to find ways to get the look you want with the tools OpenGL provides. Your only other choice is to treat them completely as separate objects and let the frame rate drop to 0 FPS.

1 Like

Note: if a bright star was behind a yellow star then in real life you would see it overpowering the one in front.

You could also manually sort them every few frames and rebuild your mesh. But really, I’d try the alpha add thing first. It’s the approach I’d use for stars as it’s easy and it will better model the accumulation of pin-points of light.

And while I’m accumulating posts, setting alpha test and alpha falloff will somewhat mitigate the issue also but you will occasionally get odd black halos around your stars… or halos where the stuff behind the the stuff behind them shows through oddly right around the edge.

Putting the quad in the transparent bucket causes it to render correctly against the skybox (thanks btw… didn’t know about queue buckets), but no stars render at all. I suspect they’re being occluded by the skybox like some of them were before.

Then the quad must still be writing depth. If it is not a picture with a defined border (ie: most pixels either transparent or fully not transparent) then you don’t have very many options.

My main remaining problem is was not with the quad; it’s between the point sprites and the skybox. The quad was actually solving it in some weird way, before its transparency worked correctly.

Solved it. The SkyFactory.createSky function I was using always returns a Geometry cast to a Spatial; I cast it back to a Geometry so I could get its material, and set its render mode to additive. This seems to work. I still have no idea why it wasn’t working before.

Also, I changed the aesthetics of the stars a bit so they can be additive and the farther stars shining through close-up ones isn’t so noticeable/annoying. It’s not an ideal solution, but it’ll work.

Render order goes like this:
-opaque bucket drawn front to back
-sky bucket
-transparent bucket drawn back to front

So if any of your transparent stuff was in the opaque bucket then it gets drawn before the sky. Any of its transparent parts would block the sky from rendering because they’d have written to the z-buffer and the sky would not get drawn there.

My guess is that maybe your stars are still in the opaque bucket or something. Otherwise I can’t see how there would be a problem because the sky bucket would always be drawn before transparent objects.

1 Like