1

I am having some troubles with libgdx 3d shadows. In my game I implemented the experimental DirectionalShadowLight. And everything works great on desktop however when I run it on android there are a lot of artifacts on the ground.

Picture (left-android, right-desktop):

I took the rendering code almost directly from tests in libgdx's github repositories.

    Gdx.gl.glClearColor(ExtendedEnvironment.FarBackgroundColor.r,ExtendedEnvironment.FarBackgroundColor.g,ExtendedEnvironment.FarBackgroundColor.b,1);
    Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

    terrain.prepareForShadows();

    environment.shadowLight.begin(new Vector3(cam.position.x+10,0,0), cam.direction);
    shadowBatch.begin(environment.shadowLight.getCamera());

    ball.draw(shadowBatch, null);
    terrain.draw(shadowBatch, null);

    shadowBatch.end();
    environment.shadowLight.end();

    terrain.recoverFromShadows(ball.getPosition().x);

Theres not much to it. Also considering that it works on desktop I would think that theres something wrong with shadow implementation itself. Is there anything I can do to fix this? Considering that I had never touched a shader in my life. Some simple hack maybe? If not maybe someone could recommend other working shadow implementation for libgdx?

Thank you.

EDIT: additional code:

BlendingAttribute  blendAttribute = new BlendingAttribute(1f)
IntAttribute intAttribute = IntAttribute.createCullFace(GL20.GL_FRONT);


 public void prepareForShadows(){

    batchedCubesInstance.materials.first().remove(blendAttribute.type);
    batchedCubesInstance.materials.first().remove(intAttribute.type);


}

public void recoverFromShadows(float posX){

    batchedCubesInstance.materials.first().set(blendAttribute);
    batchedCubesInstance.materials.first().set(intAttribute);

}

    //creating the batchedMesh:

    ModelBuilder builder = new ModelBuilder();
    builder.begin();
    MeshPartBuilder mpb = builder.part("cubes", GL20.GL_TRIANGLES, (Usage.Position | Usage.Normal | Usage.Color), new Material(
            IntAttribute.createCullFace(GL20.GL_FRONT),//For some reason, libgdx ModelBuilder makes boxes with faces wound in reverse, so cull FRONT
            blendAttribute = new BlendingAttribute(1f), //opaque since multiplied by vertex color
            new DepthTestAttribute(true), //don't want depth mask or rear cubes might not show through
            ColorAttribute.createDiffuse(Color.WHITE) //white since multiplied by vertex color
            ));

    for (int i=0; i < NUMCUBES; i++){

        mpb.box(1, 1, 1); 

    }
    batchedCubes = builder.end();
    batchedCubesInstance = new ModelInstance(batchedCubes);

enter image description here

Madmenyo
  • 8,389
  • 7
  • 52
  • 99
  • what does `terrain.prepareForShadows();` and `terrain.recoverFromShadows` do? it looks like your terrain shouldn't cast shadows at all, so don't render them to the shadowLight. – Xoppa Aug 08 '14 at 22:37
  • These methods remove blendingAttribute from my batched model in order to hack around the fact that shadows don't support semi transparent stuff. I tried moving away all the ground before rendering shadows, but that didn't change anything. Also these artifact seem to appear on everything, not only the ground. (I added picture and some code) – Justas Sakalauskas Aug 09 '14 at 08:47
  • I looks like you modified the default cull face. Make sure to show all relevant code. – Xoppa Aug 09 '14 at 16:25
  • I added the code that creates the batched mesh. I'm sorry I don't really know what is relevant here as i feel so out of my comfort zone when it comes to 3d. Heres my previous question about batching all the cubes that may have some relevant information : http://stackoverflow.com/questions/24724359/trouble-batching-cubes-in-libgdx?noredirect=1#comment38436228_24724359 – Justas Sakalauskas Aug 09 '14 at 21:44
  • Remove `IntAttribute.createCullFace(GL20.GL_FRONT)` if you experience any problems after that for the normal rendering then wind your vertices counter clock wise (the default glFrontFace​ is GL_CCW​). Here's more info: http://www.opengl.org/wiki/Face_Culling – Xoppa Aug 09 '14 at 21:49
  • As you suggested I tried my dirty hack to remove cull face attribute before rendering in prepareForShadows() method and then bringing it back to avoid problems with normal rendering (updated code above), but that didn't change anything. Should my dirty workaround work in theory or the model should be created without the attribute in the first place? – Justas Sakalauskas Aug 10 '14 at 15:32
  • I didn't suggest any dirty hack, nor will that solve your problem. Remove the cullface attribute, it shouldn't be there in the first place. – Xoppa Aug 10 '14 at 15:59
  • I have the cullface attribute because without it the back faces seem to render on top of front faces. So if the temporary removal of the attribute has no effect then removing it complete should have no effect too?Or am I wrong? If so why? Theres more on why the cull face is needed in this question: http://stackoverflow.com/questions/24724359/trouble-batching-cubes-in-libgdx?noredirect=1#comment38436228_24724359 .I'm sorry if I'm being a pain in the ass. I just don't want to spend a lot of time (because I don't know how to do it)trying to rewind cube vertices if that won't make any difference. – Justas Sakalauskas Aug 10 '14 at 16:59
  • That's indeed wrong. The fact that you need GL_FRONT for normal rendering implies that the winding is incorrect (you should want to see the front face, not cull them). The DepthShader actually uses GL_FRONT by default (see https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g3d/shaders/DepthShader.java#L38) to avoid shadow acne (which is what you experiencing). If, for some reason, you don't want to correct the winding, then remove the cullface attribute and instead call: `Gdx.gl.glFrontFace​(GL20.GL_CW);` like i referred to in the previous comment. – Xoppa Aug 10 '14 at 17:13
  • Hey bro, how did u get shadows on the floor? where can i find a guide or a tutorial? – chelo_c Aug 23 '14 at 21:04
  • https://github.com/libgdx/libgdx/blob/master/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/ShadowMappingTest.java – Justas Sakalauskas Aug 24 '14 at 15:33

1 Answers1

7

The problem you're experiencing is called shadow acne (in contrast to peter panning), which is caused by (floating point) precision errors. These are more noticeable on mobile than on desktop because the precision on desktop is most commonly better. There are several ways to avoid shadow acne. One of which is to make sure that the near and far plane of the camera are as close to each other as possible. So don't use a very small cam.near and very high cam.far value. The DepthShader (which is used to create the depth buffer) tries to avoid shadow acne, by only having back faces cast shadows. For this it uses front face culling.

However, you're also front face culling for the normal rendering (you're only rendering the back side of the models). This causes the visible faces and faces used for generating the depth buffer to be the same. Hence the shadow acne.

You could solve this by using back face culling while generating the shadow map. However, this will over-complicate your code and might cause other (future) problems as well. Instead you should try to keep the front faces the visible faces, thus removing IntAttribute.createCullFace(GL20.GL_FRONT). Note that by default back faces are culled, you don't have to specify that.

Removing the cull face attribute might cause other problems in your normal rendering (otherwise you wouldn't have it in there in the first place). This is most likely because the vertex winding of your models is incorrect. You say that you create these using:

for (int i=0; i < NUMCUBES; i++){
    mpb.box(1, 1, 1); 
}

It is highly unlikely that this is actually the code you're using to create the model in the image (this just creates an undefined number of boxes with the same size at the same location). Depending on the method that you used to create the scene, you can easily correct the vertex winding by swapping either the horizontal or vertical coordinates, but only one of those.

If, for some reason, you don't want to correct the vertex winding, then you can call Gdx.gl.glFrontFace​(GL20.GL_CW); to swap the vertex winding used from counterclockwise to clockwise. Note, however, that this might cause other (future) problems, because most commonly the default (counterclockwise) is assumed.

Xoppa
  • 7,983
  • 1
  • 23
  • 34