# Shadow artifacts/acne on block intersections

This is using the default shader and depth shader from LibGdx. It creates lines at the blocks intersections.

This is what I use to create the blocks models for now:

private Model createModel(Color color) {
Model model = modelBuilder.createBox(
cellSize, cellSize, cellSize,
new Material(ColorAttribute.createDiffuse(color)),
Usage.Position | Usage.Normal);
return model;
}
...
for(int x = 0; x < data.models.length; x++) {
for(int y = 0; y < data.models[x].length; y++) {
int z = data.getElevation(x, y);
ModelInstance instance = new ModelInstance(model);
instance.transform.setTranslation(new Vector3(cellSize * x + cellSize/2, cellSize * y + cellSize/2, z * cellSize - cellSize/2));
}
}


This sets the environment lights:

getEnvironment().set(new ColorAttribute(ColorAttribute.AmbientLight, getAmbiantBrightness(), getAmbiantBrightness(), getAmbiantBrightness() , 1f));
getEnvironment().set(new ColorAttribute(ColorAttribute.Specular, getAmbiantBrightness(), getAmbiantBrightness(), getAmbiantBrightness() , 1f));


This is how I render:

@Override
public void render(float delta) {
getWorld().buildCache();
clearScreen();
controls.update(delta);
renderWorld(delta);
renderHud(delta);
}
public void clearScreen() {
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClearColor(getBGColor().r, getBGColor().g, getBGColor().b, getBGColor().a);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
}

}

protected void renderWorld(float delta) {
getBatch().begin(getCam());
// render the cached models
getBatch().render(getWorld().cache, getEnvironment());
// render the temp models
getWorld().tempModels.forEach(m -> getBatch().render(m, getEnvironment()));
getBatch().end();
}


I tried to copy the default fragment and vertex shader and edit the PCFoffset with random values, but the result wasn't better.

float getShadow()
{

float pcfOffset = 0.0f;
}


Should I code the lights/shadows myself using Lwjgl instead of using LibGdx defaults? Seems like a lot of work for something I expected to be included.

• I went to look up the docs on DirectionalShadowLight and noticed they're all marked "deprecated - do not use" - so it might be that this implementation has known issues that are unsuited to production use. – DMGregory Dec 29 '18 at 18:24

Ok, there is a simple fix to shadow acne that I found here https://lwjglgamedev.gitbooks.io/3d-game-development-with-lwjgl/content/chapter18/chapter18.html

float bias = 0.05;
if ( projCoords.z - bias < texture(shadowMap, projCoords.xy).r )
{
// Current fragment is not in shade
}


And I found how to apply it to LibGdx's default fragment shader :

float getShadowness(vec2 offset)
{
const vec4 bitShifts = vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 16581375.0);

// EDIT
float bias = 0.0005f;

}


This gets rid of the shadow acne very cleanly.

Unfortunately, I still have some problems with white/black lines showing on intersections because, as I just realized, the wrong vertex gets rendered on top of the other. I.e. if I look in the same direction as the directional light, I get white lines and if I look in the opposite direction (towards the light source), I get black lines. Perhaps I can fix this with a noise filter.

Edit :

Final solution : I found out that the seams indeed came from the unculled side faces of the blocks which can ocasionnaly render on top of the "top" faces of the blocks. The problem is hard to explain by text, but the solution is simple. I found out about greedy meshing, which merges all possible quads. Here are the resources I used :

I ended up not using Robert O'Leary's implementation as I had problems with winding/indexing with it. So I implemented Mikola Lysenko's code in java myself which also gave me a better understanding of the algorithm and let me arrive to this result. Extremely clean mesh which produces no seams :

The only problem with Mikola Lysenko's original code is that it doesn't include the actual quad mesh generation and vertex winding. So one has to add backface management by himself to know if a face's normal must be positive or negative, and adjust the winding accordingly.