libGDX 3D теневые артефакты

У меня возникли проблемы с 3D-тенями libgdx. В своей игре я реализовал экспериментальный DirectionalShadowLight. И все отлично работает на рабочем столе, однако, когда я запускаю его на Android, на земле появляется много артефактов.

Картинка (слева-андроид, справа-десктоп):

Я взял код рендеринга почти напрямую из тестов в репозиториях libgdx на github.

    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);

Там не так много. Также, учитывая, что он работает на рабочем столе, я думаю, что что-то не так с самой реализацией теней. Могу ли я что-нибудь сделать, чтобы исправить это? Учитывая, что я никогда в жизни не прикасался к шейдеру. Может быть, какой-нибудь простой хак? Если нет, может быть, кто-нибудь может порекомендовать другую рабочую теневую реализацию для libgdx?

Спасибо.

РЕДАКТИРОВАТЬ: дополнительный код:

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);

введите здесь описание изображения


person Justas Sakalauskas    schedule 08.08.2014    source источник
comment
что делают terrain.prepareForShadows(); и terrain.recoverFromShadows? похоже, что ваш ландшафт вообще не должен отбрасывать тени, поэтому не визуализируйте их в shadowLight.   -  person Xoppa    schedule 09.08.2014
comment
Эти методы удаляют blendingAttribute из моей пакетной модели, чтобы обойти тот факт, что тени не поддерживают полупрозрачные вещи. Я пытался убрать всю землю перед рендерингом теней, но это ничего не изменило. Также эти артефакты появляются на всем, а не только на земле. (я добавил картинку и немного кода)   -  person Justas Sakalauskas    schedule 09.08.2014
comment
Похоже, вы изменили лицо отбраковки по умолчанию. Не забудьте показать весь соответствующий код.   -  person Xoppa    schedule 09.08.2014
comment
Я добавил код, который создает пакетную сетку. Мне жаль, что я действительно не знаю, что здесь важно, потому что я чувствую себя не в своей зоне комфорта, когда дело доходит до 3D. Вот мой предыдущий вопрос о пакетной обработке всех кубов, которые могут иметь некоторую соответствующую информацию: ="проблема пакетной обработки кубов в libgdx">stackoverflow.com/questions/24724359/   -  person Justas Sakalauskas    schedule 10.08.2014
comment
Удалите IntAttribute.createCullFace(GL20.GL_FRONT), если у вас возникнут какие-либо проблемы после этого для нормального рендеринга, затем накрутите вершины против часовой стрелки (по умолчанию glFrontFace – GL_CCW​). Дополнительная информация: opengl.org/wiki/Face_Culling   -  person Xoppa    schedule 10.08.2014
comment
Как вы предложили, я попробовал свой грязный хак, чтобы удалить атрибут cull face перед рендерингом в методе prepareForShadows(), а затем вернуть его, чтобы избежать проблем с обычным рендерингом (обновленный код выше), но это ничего не изменило. Должен ли мой грязный обходной путь работать в теории, или модель должна быть создана без атрибута в первую очередь?   -  person Justas Sakalauskas    schedule 10.08.2014
comment
Я не предлагал никакого грязного взлома, и это не решит вашу проблему. Удалите атрибут cullface, его вообще не должно быть.   -  person Xoppa    schedule 10.08.2014
comment
У меня есть атрибут cullface, потому что без него обратные грани отображаются поверх передних граней. Итак, если временное удаление атрибута не имеет никакого эффекта, то полное его удаление также не должно иметь никакого эффекта? Или я ошибаюсь? Если да, то почему? Подробнее о том, почему в этом вопросе требуется отбраковка: пакетная обработка кубов в libgdx">stackoverflow.com/questions/24724359/. Извините, если я заноза в заднице. Я просто не хочу тратить много времени (потому что я не знаю, как это сделать), пытаясь перемотать вершины куба, если это не будет иметь никакого значения.   -  person Justas Sakalauskas    schedule 10.08.2014
comment
Это действительно неправильно. Тот факт, что вам нужен GL_FRONT для нормального рендеринга, означает, что намотка неверна (вы должны хотеть видеть переднюю грань, а не отбрасывать их). DepthShader по умолчанию использует GL_FRONT (см. github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/), чтобы избежать теневых прыщей (это то, что вы испытываете). Если по какой-то причине вы не хотите исправлять обмотку, то удалите атрибут cullface и вместо этого вызовите: Gdx.gl.glFrontFace​(GL20.GL_CW); как я упоминал в предыдущем комментарии.   -  person Xoppa    schedule 10.08.2014
comment
Эй, бро, как ты получил тени на полу? где я могу найти руководство или учебник?   -  person chelo_c    schedule 24.08.2014
comment
github.com/libgdx/libgdx/blob/master/tests/gdx-tests/src/com/   -  person Justas Sakalauskas    schedule 24.08.2014


Ответы (1)


Проблема, с которой вы столкнулись, называется теневой акне (в отличие от панорамирования Питера), которая вызвана ошибками точности (с плавающей запятой). Они более заметны на мобильных устройствах, чем на настольных компьютерах, потому что точность на настольных компьютерах чаще всего выше. Есть несколько способов избежать теневых прыщей. Один из них — сделать так, чтобы ближняя и дальняя плоскости камеры находились как можно ближе друг к другу. Так что не используйте очень маленькое cam.near и очень большое cam.far значение. DepthShader (который используется для создания буфера глубины) пытается избежать теневых прыщей, за счет того, что только задние грани отбрасывают тени. Для этого используется отсечение лицевой стороны.

Тем не менее, вы также отбираете лицевую сторону для нормального рендеринга (вы визуализируете только заднюю сторону моделей). Это приводит к тому, что видимые грани и грани, используемые для создания буфера глубины, будут одинаковыми. Отсюда и теневые прыщи.

Вы можете решить эту проблему, используя отсечение задней грани при создании карты теней. Однако это чрезмерно усложнит ваш код и может вызвать другие (будущие) проблемы. Вместо этого вы должны попытаться сохранить передние грани видимыми гранями, таким образом удаляя IntAttribute.createCullFace(GL20.GL_FRONT). Обратите внимание, что по умолчанию задние грани отбрасываются, вам не нужно указывать это.

Удаление атрибута cull face может вызвать другие проблемы в вашем обычном рендеринге (иначе у вас его бы не было изначально). Скорее всего это связано с тем, что вершинная намотка ваших моделей неверна. Вы говорите, что создаете их, используя:

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

Крайне маловероятно, что именно этот код вы используете для создания модели на изображении (это просто создает неопределенное количество блоков одинакового размера в одном и том же месте). В зависимости от метода, который вы использовали для создания сцены, вы можете легко исправить накрутку вершин, поменяв местами либо горизонтальные, либо вертикальные координаты, но только одну из них.

Если по какой-то причине вы не хотите исправлять намотку вершины, вы можете вызвать Gdx.gl.glFrontFace​(GL20.GL_CW);, чтобы поменять используемую обмотку вершины с против часовой стрелки на по часовой стрелке. Обратите внимание, однако, что это может вызвать другие (будущие) проблемы, потому что чаще всего предполагается значение по умолчанию (против часовой стрелки).

person Xoppa    schedule 10.08.2014
comment
Большое спасибо за подробное объяснение. Моя проблема теперь решена! - person Justas Sakalauskas; 11.08.2014