Методы отбраковки для рендеринга большого количества кубов - PullRequest
21 голосов
/ 12 сентября 2010

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

Спасибо

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

            }
        }

}

Ответы [ 8 ]

16 голосов
/ 19 октября 2010

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

  1. Не просто сваливайте каждый куб в OpenGL, но и не беспокойтесь о том, чтобы все обрезать видимость самостоятельно. Как указано в другом ответе, проверьте все 6 граней, чтобы увидеть, полностью ли они закрыты соседним блоком. Отрисовывать только лица, которые могут быть видны. Это примерно уменьшает количество ваших лиц с кубического члена (объем кубов n * n * n) до квадрата (поверхность всего около n * n).
  2. OpenGL может выполнять выборочный просмотр намного быстрее, чем вы. Как только вы отобразили все свои поверхности в список отображения или VBO, просто отправьте весь большой объект в OpenGL. Если вы разбиваете свою геометрию на кусочки (или то, что Minecraft называет кусками), вы можете избежать рисования кусков, которые вы можете легко определить, находясь за камерой.
  3. Визуализируйте всю свою геометрию в список отображения (или списки) и перерисовывайте его каждый раз. Это простой шаг, если вы используете непосредственный режим, потому что вы просто оборачиваете свой существующий код в glNewList / glEndList и перерисовываете с помощью glCallList. Уменьшение количества вызовов OpenGL (на кадр) окажет гораздо большее влияние, чем уменьшение общего объема полигонов для рендеринга.
  4. Как только вы увидите, сколько времени потребуется для создания списков отображения, чем для их составления, вы начнете думать о том, как поместить обновления в поток. Вот где преобразование в VBO окупается: поток рендерится в простые старые массивы (например, добавляя 3 массива в массив вместо вызова glVertex3f), а затем поток GL должен только загрузить их в карту с помощью glBufferSubData. Вы выигрываете дважды: код может выполняться в потоке, и он может «нарисовать» точку с 3 записями массива вместо 3 вызовов функций.

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

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

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

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

13 голосов
/ 13 октября 2010

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

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

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

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

5 голосов
/ 19 октября 2010

Как и другие, я играл с "движком" в мире блоков, используя Ogre, и на ходу пишу несколько статей (см. Статьи о мире блоков ). Основной подход, который я выбрал:

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

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

2 голосов
/ 11 октября 2010

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

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

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

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

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

1 голос
/ 15 октября 2010

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

1 голос
/ 14 октября 2010

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

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

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

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

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

0 голосов
/ 18 октября 2010

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

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

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

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

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

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

0 голосов
/ 17 октября 2010

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

...