Ускорение рендера моделей

2,494
77
375
Туториал предполагает, что вы хоть как-то знакомы с рендерингом.

Модели в майне (1.7.10) рендерятся очень не очень.  Уже при 30-50 моделях (~600 треугольников) на моем не очень мощном железе начинает просаживаться fps. Это нормально? Естественно нет. Лично меня это не устраивает. Что можно сделать?

Можно запилить рендер моделей через индексированные VBO + шейдеры. Это современных подход. Но в реалиях майна это не совсем хорошо. При рендере майн проходится по всем нужным сущностям и вызывает doRender для каждой. Соответственно, это постоянное включение-выключение шейдера, а это не дешевая операция. Любой нормальный движок сортирует объекты по шейдерам, тем самым переключение происходит только тогда, когда оно действительно нужно. Чтобы реализовать такое в майне потребуется переписать чуть ли не весь рендер и пачка костылей, посильных не каждому.

Есть другая устаревшая технология, но чрезвычайно простая в использовании - Display Lists. Что это такое?
Представляет собой группу команд OpenGL, которые сохранены (скомпилированы) для их последующего выполнения. После создания списка все данные вершин и пикселов оцениваются и копируются в память gpu.  После того, как список скомпилирован, его можно повторно использовать без повторной передачи данных в драйвер, чтобы рисовать каждый кадр. Это является достаточно быстрым способом рисования статических данных.

В коде это выглядит примерно так.
Код:
@SideOnly(Side.CLIENT)
public class RendererTileModel extends TileEntitySpecialRenderer
{
    private int list;
    
    public RendererTileModel(ResourceLocation res)
    {
        // Модель - локальный объект. Важно не оставлять в памяти информацию о вершинах.
        IModelCustom model = AdvancedModelLoader.loadModel(res);
        list = GL11.glGenLists(1);
        GL11.glNewList(list, GL11.GL_COMPILE);
        // Рендер. Да-да, прямо тут.
        model.renderAll();
        GL11.glEndList();
    }
    
    @Override
    public void renderTileEntityAt(TileEntity tile, double x, double y, double z, float pt)
    {
        GL11.glPushMatrix();
        GL11.glTranslated(x, y, z);
        GL11.glCallList(list);
        GL11.glPopMatrix();
    }
}

Выглядит очень легко. А производительность растет даже не в 10-ки раз, а в сотни. Причем, если требуется отрисовать много мелких моделей, то по производительности превосходит VBO (из-за причины описанной выше).

Лично я пользуюсь своим загрузчиком obj моделей, но для туториала запилил обертку над форжевской obj моделью, которая предоставляет все обычные возможности интерфейса IModelCustom. Надеюсь, что кому-нибудь пригодится.
Код:
public class ModelWrapperDisplayList implements IModelCustom
{
    // Для каждой группы свой лист
    private final Map<String, Integer> lists = new HashMap<>();
    // Буффер, который будет содержать все листы. Для более быстрого рендера всей модели.
    private final IntBuffer bufAll;
    private final String type;

    public ModelWrapperDisplayList(WavefrontObject model)
    {
        type = model.getType();
        int list = GL11.glGenLists(model.groupObjects.size());
        for (GroupObject obj : model.groupObjects) {
            GL11.glNewList(list, GL11.GL_COMPILE);
            model.renderPart(obj.name);
            GL11.glEndList();
            lists.put(obj.name, list++);
        }
        bufAll = initBuffer();
    }

    private IntBuffer initBuffer()
    {
        IntBuffer buf = BufferUtils.createIntBuffer(lists.size());
        for (int i : lists.values()) {
            buf.put(i);
        }
        buf.flip();
        return buf;
    }

    @Override
    public String getType()
    {
        return type;
    }

    @Override
    public void renderAll()
    {
        GL11.glCallLists(bufAll);
    }

    @Override
    public void renderOnly(String... groupNames)
    {
        if (groupNames == null || groupNames.length == 0) {
            return;
        }

        for (String group : groupNames) {
            renderPart(group);
        }
    }

    @Override
    public void renderPart(String partName)
    {
        Integer list = lists.get(partName);
        if (list != null) {
            GL11.glCallList(list);
        }
    }

    @Override
    public void renderAllExcept(String... groupNames)
    {
        if (groupNames == null || groupNames.length == 0) {
            renderAll();
            return;
        }

        for (Entry<String, Integer> it : lists.entrySet()) {
            if (Arrays.binarySearch(groupNames, it.getKey(), String::compareTo) < 0) {
                GL11.glCallList(it.getValue());
            }
        }
    }
}
Использование никак не отличится от использования форжевской obj модели. Нужно всего лишь обернуть моим враппером.
Код:
IModelCustom model;

model = AdvancedModelLoader.loadModel(resource);
model = new ModelWrapperDisplayList((WavefrontObject) model);

model.renderAll();

P.S. Насчет новых версий. @Oldestkon где-то говорил, что тесселятор теперь использует VBO. Специально скачал на днях последнюю версию форжа и глянул. От VBO там только название. Если кто-то ( @Oldestkon С: ) сможет меня переубедить, то хорошо. Может быть я плохо разобрался. В общем, не знаю насчет новых версий. Если там obj модели такие же лагучие, то вполне можно воспользоваться данным туториалом.

P.P.S. Отдельное спасибо @GloomyFolken за советы.
 
3,835
57
500
Так в последних версиях же всюду json. Зачем obj использовать?


Кстати, на счёт ускорения, только не рендера...
Во время работы над миром заметил, что основные лаги идут:
1 - из-за массового обновления блоков,
2 - из-за такого же массового пересчёта освещённости... (скорее всего первое связано со вторым)

Это можно наблюдать даже в ванильном МК, например, когда вода стекает с высокой горы.

При этом, если все изменения происходят на сервере, лагов не видно...
А возникают они, как не трудно догадаться, при синхронизации клиента.

В связи с этим вопрос: а нельзя ли как-то ускорить этот процесс? )))
 

Maxik

Золотой Петушок
4,945
46
715
json отстой, в топку. Неудобный кусок говна.OBJ модели есть в последних версиях, слава яйцам
 

Icosider

iMixin
Администратор
3,290
85
496
Liahim написал(а):
Так в последних версиях же всюду json. Зачем obj использовать?


Кстати, на счёт ускорения, только не рендера...
Во время работы над миром заметил, что основные лаги идут:
1 - из-за массового обновления блоков,
2 - из-за такого же массового пересчёта освещённости... (скорее всего первое связано со вторым)

Это можно наблюдать даже в ванильном МК, например, когда вода стекает с высокой горы.

При этом, если все изменения происходят на сервере, лагов не видно...
А возникают они, как не трудно догадаться, при синхронизации клиента.

В связи с этим вопрос: а нельзя ли как-то ускорить этот процесс? )))



Переписать майн?
 

Maxik

Золотой Петушок
4,945
46
715
Уже переписали. На другом языке.)
 

Maxik

Золотой Петушок
4,945
46
715
Тоже самое, только в альфе
1200+ фпс
В обычном майне у меня 340
На глянь
http://www.minetest.net/downloads/
 

Icosider

iMixin
Администратор
3,290
85
496
/\ Втирает простым людям дичь. Не будет у тебя никакого прироста, даже если другой язык.
 
1,990
18
104
По-моему Minecraft Windows 10 Edition куда круче, гляньте скорость загрузки чанков.  ;)

[video=youtube]https://www.youtube.com/watch?v=EGZLdLYpvNM[/video]

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

Dahaka написал(а):
От VBO там только название. Если кто-то ( @Oldestkon С: ) сможет меня переубедить, то хорошо.
Да я, честно, сам видел лишь мельком, а сижу на старом майне. Как именно реализовали VBO в новом - хрен его знает.
 

Maxik

Золотой Петушок
4,945
46
715
Это просто опечатка. Там написано Hi - то есть "привет" :)
Программисты майкрософта имели огромное преимущество
Уже только потому, что это программисты майкрософта. Туда салаг не берут вроде.
 
476
9
39
Это актуально только для 1.7.10? (Вдруг в новых версиях майна запилили сиё уже?)
 
2,494
77
375
talosdx написал(а):
Это актуально только для 1.7.10? (Вдруг в новых версиях майна запилили сиё уже?)
Я не в курсе.

Совсем мельком глянул.
Ванильные ModelBase рендерятся через display lists. Все остальное рендерится через тесселятор. И актуальность зависит как раз от того, на чем он основан. В 1.7.10 тесселятор каждый кадр формирует буфер вершин и отправляет его на gpu.
В новых версиях тесселятор полностью изменен. Но в то же время, только один класс во всем майне генерирует VBO (вызывает glGenBuffers) - VertexBuffer. И используются он только для рендера неба и чанков.

Вывод.
Тесселятор не использует VBO. Модели рендерятся не лучшим образом.
 
1,076
22
130
Немного не понятно, как рендерить модель по частям. Создавать для каждого парта новый лист? Прошу проконсультировать
 
477
7
50
Да правда, данныйметод очень удобен и лучше чем обычный для @fane4qa
Java:
public ModelNY() {
        GL11.glNewList(this.head, 4864);
        GL11.glPushMatrix();
        GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
        this.model.renderPart("Head");
        GL11.glPopMatrix();
        GL11.glEndList();
        this.body = this.head + 1;
        GL11.glNewList(this.body, 4864);
        GL11.glPushMatrix();
        GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
        this.model.renderPart("Body");
        GL11.glPopMatrix();
        GL11.glEndList();
        this.larm = this.body + 1;
        GL11.glNewList(this.larm, 4864);
        GL11.glPushMatrix();
        GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
        this.model.renderPart("Left_right");
        GL11.glPopMatrix();
        GL11.glEndList();
        this.rarm = this.larm + 1;
        GL11.glNewList(this.rarm, 4864);
        GL11.glPushMatrix();
        GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
 
477
7
50
я половину рендера кинул чтоб был как пример там лист еще завершить нужно
 
Сверху