Подтвердить что ты не робот

Методы отбраковки для рендеринга большого количества кубов

Я работаю над личным учебным проектом, чтобы сделать Minecraft клон. Он отлично работает в стороне от одной вещи. Подобно Minecraft, у моей местности много кубов, уложенных на Y, чтобы вы могли выкапывать. Несмотря на то, что я делаю отсев, это все равно означает, что я бесполезно рисую все слои кубов ниже меня. Кубы X, Y и Z упорядочены (хотя и только в 1 направлении, поэтому его технически Z не заказывал камеру). Я в основном из позиции игрока добавляю только указатели на кубы вокруг игрока. Затем я делаю отговорку против них. Я не занимаюсь подразделением oct tree. Я думал просто не показывать слои ниже игрока, за исключением того, что это не работает, если игрок смотрит вниз в отверстие. Учитывая это, как я мог избежать отображения кубов ниже меня, которые я не вижу, или кубов, которые скрыты другими кубами.

Спасибо

void CCubeGame::SetPlayerPosition()
{
PlayerPosition.x = Camera.x / 3;
PlayerPosition.y = ((Camera.y - 2.9) / 3) - 1;
PlayerPosition.z = Camera.z / 3;
}

void CCubeGame::SetCollids()
{

SetPlayerPosition();

int xamount = 70;
int zamount = 70;
int yamount = 17;

int xamountd = xamount * 2;
int zamountd = zamount * 2;
int yamountd = yamount * 2;
PlayerPosition.x -= xamount;

PlayerPosition.y -= yamount;

PlayerPosition.z -= zamount;


collids.clear();
CBox* tmp;

    for(int i = 0; i < xamountd; ++i)
    {
        for(int j = yamountd; j > 0; --j)
        {
            for(int k = zamountd; k > 0; --k)
            {

                tmp = GetCube(PlayerPosition.x + i, PlayerPosition.y + j, PlayerPosition.z + k);



                if(tmp != 0)
                {
                    if(frustum.sphereInFrustum(tmp->center,25) != NULL)
                    {
                        collids.push_back(tmp);
                    }
                }

            }
        }

}
4b9b3361

Ответ 1

Рендер спереди назад. Для этого вам не нужна сортировка, используйте октреи. Листья будут не отдельными кубиками, а большими группами.

Сетка для каждого такого листа должна кэшироваться в списке отображения (как предложил Бобмитч) или, что еще лучше, в буфере вершин (дешевле обновлять). Когда вы генерируете эту сетку, не генерируйте все кубы методом грубой силы. Вместо этого для каждого лица куба проверьте, есть ли у него непрозрачный сосед в том же листе, если это так, вам вообще не нужно создавать это лицо. Вы также можете объединить соседние грани с одним и тем же материалом в один длинный прямоугольник. Вы также можете разделить сетку на шесть наборов, по одному на каждое основное направление: + / -XYZ грани. Нарисуйте только те наборы граней, которые могут стоять перед камерой.

Рендеринг спереди назад не помогает сам по себе. Однако вы можете использовать выборку окклюзии, предлагаемую современным оборудованием, чтобы воспользоваться этим заказом. Перед рендерингом листа октерева проверьте, проходит ли его bbox запрос окклюзии. Если он не проходит, вам не нужно его вообще рисовать.

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

Ответ 2

Вот что я узнал, когда писал свой собственный клон:

  • Не просто выгружайте каждый куб в OpenGL, но также не беспокойтесь о том, чтобы обойти всю видимость. Как сказал еще один ответ, проверьте все 6 граней, чтобы увидеть, полностью ли они перекрыты соседним блоком. Только визуализировать лица, которые могут быть видны. Это примерно сводит счет вашего лица с кубического члена (объем кубов n * n * n) до квадрата (поверхность только около n * n).
  • OpenGL может делать просмотр frustrum более быстрым, чем вы можете. После того, как вы превратили все ваши поверхностные лица в список отображения или VBO, просто отправьте весь blob в OpenGL. Если вы разбиваете свою геометрию на кусочки (или то, что Minecraft вызывает куски), вы можете избежать рисования кусков, которые вы легко можете определить, находятся за камерой.
  • Отметьте всю геометрию в списке отображения (или списках) и перерисуйте это каждый раз. Это простой шаг, если вы используете немедленный режим, потому что вы просто завершаете свой существующий код в glNewList/glEndList и перерисовываете с помощью glCallList. Сокращение числа вызовов OpenGL (за кадр) будет иметь значительно больший эффект, чем уменьшение общего объема полигонов для рендеринга.
  • Как только вы увидите, сколько времени требуется для создания списков отображения, чем для их рисования, вы начнете думать о том, как добавлять обновления в поток. Здесь конверсия в VBOs окупается: поток превращается в простые старые массивы (добавление 3-х планет в массив вместо вызова glVertex3f, например), а затем поток GL должен загружать их только на карту с помощью glBufferSubData. Вы выигрываете дважды: код может работать в потоке, и он может "нарисовать" точку с 3-х строчными записями вместо 3 вызовов функций.

Другие вещи, которые я заметил:

VBOs и списки отображения имеют очень схожую производительность. Вполне возможно, что данная реализация OpenGL использует VBO для хранения списка отображения. Я пропустил вершинные массивы (своего рода клиентский VBO), поэтому я не уверен в этом. Используйте расширенную версию ARB для VBOs вместо стандарта GL 1.5, потому что драйверы Intel используют только расширение (несмотря на то, что утверждают, что поддерживают 1.5), и драйверы nvidia и ATI не заботятся.

Правило атласов текстур. Если вы используете одну текстуру на лице, посмотрите, как работают атласы.

Если вы хотите увидеть мой код, найдите меня в github.

Ответ 3

Как и другие, я играл с блочным мирным движком, используя Ogre, и писал некоторые статьи, когда я иду (см. Block World Articles). Основной подход, который я принимал, заключается в следующем:

  • Создавайте только видимые грани блоков (не грани между блоками).
  • Разделить мир на более мелкие куски (необходимо только для более быстрого обновления отдельных блоков).
  • Объединить текстуры блоков в один файл текстуры (атлас текстуры).

Просто используя их, вы получите очень хорошую производительность на больших простых блочных мирах (например, 1024x1024x1024 на достойном оборудовании).

Ответ 4

В настоящее время я работаю над клоном minecraft в python/pyglet, просто для любопытства.

Я разбиваю данные на куски, как в minecraft, а затем для каждого фрагмента создается список отображения opengl, основанный на видимости куба. Затем я выполняю простые 2d-усечения, отбраковывающие эти куски и вызывающие каждый отображаемый список на определенном расстоянии от игрока.

При добавлении/удалении куба я воссоздаю список отображения для фрагмента.

Нет отрезков окклюзии, кроме кубов, которые полностью окружены другими кубами.

Для простых сцен это может достигать более 600 кадров в секунду на скромной карте gfx с расстоянием около 200 кубов.

Ответ 5

Octtree и т.д. будут работать наверняка, но другой возможный способ решить вашу конкретную проблему может заключаться в том, чтобы хранить, чтобы добавить usigned char visible к каждому объекту куба. Значение поля visible рассчитывается следующим образом:

  • если правая сторона (глядя вдоль оси x) не имеет соседа, тогда будет установлен первый бит (1).
  • если левая сторона (глядя вдоль отрицательной оси x) не имеет соседа, тогда будет установлен второй бит (2).
  • если передняя сторона (смотрящая вдоль оси z) не имеет соседа, тогда будет установлен третий бит (4)
  • ... и т.д., так что у вас есть 1 бит для каждой из 6 сторон куба

Всякий раз, когда игрок выкапывает куб, вы должны обновить поле visible всех его соседних кубов.

Итак, но как это поможет? Если куб имеет значение visible 0, то это легко - куб никогда не будет показан. Но пусть куб имеет значение visible 1. Тогда куб (возможно) будет только видимым, если Xplayer < Xcube. Другие стороны работают одинаково, и я думаю, что такая функция, которая решает, может ли куб быть видимым, будет довольно быстрым и может пропускать много скрытых кубов.

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

Ответ 6

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

Ответ 7

В случае, если проблемой является только рисование (а не вращение неиспользуемых вершин), не может быть что-то вроде c-buffer полезным? Я использовал его с большим успехом, он требует отсортированных полигонов (например, по алгоритму живописца) и почти нулевой памяти (в отличие от z-буфера).

Ответ 8

Следите только за кубами, описывающими вашу поверхность. Вы можете сделать это с помощью простой структуры данных, где каждый куб содержит ссылки на него соседи. На любой современной графической карте не должно быть проблем, чтобы выталкивать все эти треугольники. Отправляйся назад. Кроме того, вы можете только кубики ближе к определенному расстоянию до зрителя. "Мир" мог начаться с огромного "кубика кубиков", внешних оболочек куба, сделанного кубиками. Если кто-то выкапывает, у вас есть чек, если соседние местоположения уже содержат кубы или нет, если вы не создаете эти кубы и не связываете их.

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

Итак: Для мира, содержащего кубики 1000x1000x1000, вы получите: 1000 * 1000 * 2 + 998 * 999 * 4 = 5988008 вместо 1000 * 1000 * 1000 = 1000000000, или на 167 единиц меньше кубов.

Конечно, вы не должны рисовать все эти кубы, начинайте с простого расстояния до зрителя, чтобы сделать суб-выбор.

Вы также можете объединить 8 кубов (групп) вместе как одну группу, а затем продолжить так, пока на верхнем уровне у вас есть только одна кубическая группа (упомянутое выше oct-tree). Это дерево можно использовать для определения того, какую часть мира нужно рисовать, а не рисовать. Если группа содержит ссылки на 8 других кубических групп, то позади не отображается. Сзади в этом были бы те кубические группы, которые не пересекаются или лежат за пределами луча, прослеженного конусом, начиная с пользователя и проходящего только по краю группы, используемой для тестирования. (Плохое описание, но я надеюсь, что вы получите какие-то намеки в любом случае на то, что можно сделать для оптимизации). В любом случае это вряд ли понадобится на сегодняшних видеокартах.

Удачи вам в вашем проекте.