Z-fighting on multiple objects

Hello all,

up to now on change of ArcCamera.lockedTarget I altered minZ aswell. But this is not a full solution, because:

Lets say you have a space game with planets, suns, asteroids, space stations, space ships etc.
The only problem I encounter is that on planet I want to zoom in deeply until now this is done by minZ = 0.001, but it causes z-fighting on asteroids, ships and stations.

But I would like to have minZ lowered when it is actually needed, like if lockedTarget is planet and camera.radius becomes < certain threshold. Is this a recommended way to solve this or anyone got some better advice?

Planet demo: Babylon.js - Planet demo

My game measures:
Planet radius: 2
Asteroidbelt radius: 400

Edit: Just recognized material.zOffset is used to order what is on top, like lower values will be on top?

Edit2: I made a new playground that shows the z fighting on asteroids, just slightly rotate camera and watch the asteroids in the back:

Edit3: I edited material.zOffset to -1, seems way better, but not perfect?

Hello @Takemura , how are you doing?

You are right about the material.zOffset, it adds an offset to the zBuffer value for the object. This makes so that objects that are far can be set to render on top of objects that are near. However, I don’t think this can solve your problem since all the asteroids will be far from the camera in that case.

Can’t you consider using a value a little larger than 0.0001 for camera.minZ? Changing it to 0.01 already makes the z-fighting much better and keeps the planet visible.

the reason or moment i need pretty low minZ are cases like when you zoomed deep into the planet, so here something like a threshold on camera.radius could work. But if you have large and small space ships, you would need very tiny minZ-values like 0.001 to 0.0001 for the small space ships like drones or fighters, else I wouldnt be able to zoom near them, the camera would start cutting into them before I zoomed close to the ship.

But what I seem to have solved by zOffset = -1, is that there was a main flickering on asteroids, kinda is connected that asteroidsystem and the sun system with the planet are all rotating themself. That flickering was inacceptable.

Regarding the z-fighting on asteroids, they might also be lowered by making the asteroids smaller?

You can try using material.useLogarithmicDepth = true

See the example bellow:
Simple Asteroid System | Babylon.js Playground (babylonjs.com)


If I set this on my planet (like on demo above with ShaderMaterial), the asteroidsystem is shown in front of my planet.

code of planet material:

        const terrain = new BABYLON.DynamicTexture('random', 128, this.scene, false, BABYLON.Texture.NEAREST_SAMPLINGMODE);
        const clouds = new BABYLON.DynamicTexture('random', 128, this.scene, false, BABYLON.Texture.NEAREST_SAMPLINGMODE);

        let updateRandomSurface = function (random) {
            let context = random.getContext();
            let imgData = context.getImageData(0, 0, 512, 512);
            for (let i = 0; i < 512 * 512 * 4; i++) {
                imgData.data[i] = Math.random() * 256 | 0
            context.putImageData(imgData, 0, 0);

        let noiseTexture;
        let cloudTexture;

        this.body = BABYLON.MeshBuilder.CreateSphere('planet', { segments: 64, diameter: 2*this.radius}, this.scene);
		this.body.parent = this.root;
	this.body.material = new BABYLON.ShaderMaterial('shader', this.scene, {	// ShaderMaterial
            vertex: './assets/textures/planets/planet',
            fragment: './assets/textures/planets/planet'
        }, {
            attributes: ['position', 'normal', 'uv'],
            uniforms: ['world', 'worldView', 'worldViewProjection', 'view', 'projection'],
            needAlphaBlending: true
        this.body.material.setVector3('cameraPosition', this.scene.activeCamera.position);
        this.body.material.setVector3('lightPosition', this.mesher.light.position);

		const generateBiome = () => {
			this.options = this.worlder.planetOptions[this.data['planet_type_id']];
			for(const [optionKey,optionData] of Object.entries(this.worlder.planetOptions[1])) {// Default values
				if(!this.options[optionKey]) {
					this.options[optionKey] = optionData;
			this.options.seed *= this.data['planet_random'];
			if(this.options.cloudSeed)	this.options.cloudSeed *= this.data['planet_random'];
			this.options.groundAlbedo *= this.data['planet_random'];

            if (noiseTexture) {
            noiseTexture = new BABYLON.ProceduralTexture('noise', this.options.mapSize, './assets/textures/noises/noise', game.scene, null, true, true);
            noiseTexture.setColor3('upperColor', this.options.upperColor);
            noiseTexture.setColor3('lowerColor', this.options.lowerColor);
            noiseTexture.setFloat('mapSize', this.options.mapSize);
            noiseTexture.setFloat('maxResolution', this.options.maxResolution);
            noiseTexture.setFloat('seed', this.options.seed);
            noiseTexture.setVector2('lowerClamp', this.options.lowerClamp);
            noiseTexture.setTexture('randomSampler', terrain);
            noiseTexture.setVector2('range', this.options.range);
            noiseTexture.setVector3('options', new BABYLON.Vector3(this.options.directNoise ? 1 : 0, this.options.lowerClip.x, this.options.lowerClip.y));
            this.body.material.setTexture('textureSampler', noiseTexture);
            cloudTexture = new BABYLON.ProceduralTexture('cloud', this.options.mapSize, './assets/textures/noises/noise', game.scene, null, true, true);
            cloudTexture.setTexture('randomSampler', clouds);
            cloudTexture.setFloat('mapSize', this.options.mapSize);
            cloudTexture.setFloat('maxResolution', 256);
            cloudTexture.setFloat('seed', this.options.cloudSeed);
            cloudTexture.setVector3('options', new BABYLON.Vector3(1, 0, 1));
            this.body.material.setTexture('cloudSampler', cloudTexture);
            this.body.material.setColor3('haloColor', this.options.haloColor);


You have to set it on both.

Yes, I did:

At the end of planet class:

this.body.material.useLogarithmicDepth = true;

On asteroid, ships, suns etc. material aswell, which is why they are all in front of planet, even though they are further away. It feels like the property doesn’t work for ShaderMaterial or needs additional changes?

You have some changes to perform in your code to support logarithmic depth in ShaderMaterial materials, see ShaderMaterial and Logarithmic Depth Buffer.


Oh, I see. Thank you, both. I will check it out :slight_smile:

It does work!


Is there a way to apply this on ParticleSystem? It looks like my nebula got some z-fighting, too.

Similar to this:

To make sure z-fighting is the problem you can try your PG in WebGPU and set engine.useReverseDepthBuffer = true right at the start of your program (like this): if there is no z-fighthing then z-fighthing is the problem indeed.

Unfortunately, the particle system shaders don’t support logarithmic depth at the time. Maybe an issue can be created about this?

1 Like

I reproduced the flickering on my ParticleSystem:

When I add your statement, then flickering is gone, but logarithmic depth buffer is not running anymore?
If this implies that there is z-fighting on it, then I will need to create an issue. Maybe someone can confirm it is z-fighting before? Strangely it is a bit weaker than in my game project, but if you rotate camera it appears clearly.

Edit: Could it also be related to its emitter rotation speed and my camera target parents rotation speed?
Edit2: No, it is not just removed all rotations and flickering is still there.

You can’t mix materials using logarithmic depth and materials that don’t use it, because the depths are not compatible (logarithmic depth materials compute a different depth than the normal depth). So, adding a particle system in the scene (which is not using a logarithmic depth) can’t work…

The only way is to make sure particle systems can support logarithmic depth => you should create an issue in the repository and add a link to this thread.


Here we are:
Fix #13005 by deltakosh · Pull Request #13010 · BabylonJS/Babylon.js (github.com)


Am I doing something wrong, on my PG it works between thininstances and particlesystem but not between simple sphere mesh and particlesystem. The particlesystem (orange nebula) should be behind planet / sun.


This PR will fix the problem:

1 Like

Thanks, thats great! The nebula is behind planet now:

1 Like