Давайте напишем ... MMO! Часть 19: Опять прозрачность

Опубликовано NowhereMan - пт, 05/15/2020 - 18:27

10 мая 2011

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

Рис. 1: Прототип части 16

Я упомянул, что этот код "был не совсем интерактивен". На этой неделе у меня для вас новая демо-версия, которая действительно работает на разумной скорости. А еще лучше, что прозрачность обрабатывается на всем диапазоне дистанций.

"Screen Door" Transparency

Рис. 2: Сортированная прозрачность

В отсортированной прозрачности мы применяем текстуры в правильном порядке, спереди, и смешиваем их, используя альфа-компонент. При этом выбирается, сколько фона, а сколько - новой текстуры. Это дает внешний вид полупрозрачных текстур, как показано на Рисунке 2.

Когда я впервые задумался о прозрачности, кто-то (вероятно, Флориан) упомянул мне стиль "экранной двери". В прозрачности экранной двери мы совсем не выполняем смешивание. Альфа-компонент либо 0%, либо 100%. Другими словами, текстура твердая, но с отверстиями в ней (как в экранной двери.) Прозрачность достигается путем просмотра через отверстия.

Рис. 3: Стеклянные блоки Minecraft

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

Это то, что делает Minecraft, и вы можете увидеть это на рисунке 3. Стеклянные блоки в основном полностью прозрачны, с непрозрачными полосами, чтобы показать отражения от блоков.

Я думал, что это выглядит как "Minecraft", и я не хотел подражать этому. Я понял, что вместо этих полос можно использовать сетку, но когда я попробовал это в своём собственном коде, результат мне не понравился. См. рис. 4.

Виден не только шаблон, но и эффекты сглаживания, когда две "прозрачные" поверхности накладываются друг на друга. Фактически, в реальной жизни можно увидеть один и тот же эффект (Узоры Муара), когда два слоя экранной двери накладываются друг на друга. Поэтому я продолжил сортировать прозрачность.

Рис. 4: Прозрачность "Экранной двери".

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

Я не уверен, что происходит в этом частичном совпадении - если это патологический случай сглаживания, или какая-то ошибка. Я все еще изучаю это. Вы увидите это в демо-версии, если будете искать. Похоже, это случается довольно часто.

A "D'oh!" Moment

У меня было несколько "D'oh!" моментов на этой неделе, и один из них был о прозрачности двери. Я подумал, что это не стоит того, потому что вблизи выглядит ужасно. Тем не менее, сортировка прозрачных кубов и пересылка их на дисплей каждый цикл обновления убивает производительность. Мне не нравился вид непрозрачных данных вдали. Поэтому я снова пробовал прозрачность в стиле экранных дверей и хмурился на изображение.

Потом я понял, что это выглядит прекрасно на среднем расстоянии, где не видно рисунка отверстий. И поэтому для рендеринга на среднем расстоянии я мог использовать прозрачность экранной двери вместо непрозрачной, не теряя при этом производительности. Я также могу использовать его для точек, так как они текстурированы так же, как треугольники. Это полностью решило проблемы с внешним видом в версии из 16 части. Теперь она выглядит так, как показано на рисунке 5. D'oh!

Рис. 5: Новый смешанный режим рендеринга

Бессмысленность

Рис. 6: Один чанк

В 17-ой части я закодировал всю поддержку кубов для рендеринга в виде точек, но не добавил код, чтобы включить его на расстоянии. На этой неделе я это сделал и проверил в GitHub. Читатель Клеменс Фридль попробовал его и сказал, что он даже медленнее, чем кубы на его машине.

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

Я сразу же пришел к выводу, что точки просто не поддерживаются, как и треугольники на дешевых видеокартах. Был соблазн просто проигнорировать это, так как я устал от борьбы с аппаратным обеспечением. Но потом я подумал: "Как трудно было бы нарисовать маленькие треугольники?". Это было бы три вершины вместо одной, и мне пришлось бы больше работать в шейдерах, чтобы сориентировать треугольник на экран, но, казалось, стоит попробовать.

Я запрограммировал это, и это было быстрее, чем точки, но все равно медленнее, чем кубы.Это на самом деле не имело смысла - кубы сделаны из треугольников, и их меньше. Я был в тупике. В конце концов, я просто вставил код для подсчета всех треугольников, которые я выставлял на экран, и у меня был еще один момент "D'oh!"... На рисунке 6 показано, как выглядит один фрагмент в демо.

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

Чего я не потрудился понять, так это сколько кубиков это добавляет. Каждый кусочек равен 32 на 32 на 32, так что каждая сторона - 32 на 32 или 1024 кубика. Дополнительные пять граней (или шесть, для закопанных кусков) - это пять или шесть тысяч кубиков. В тестовом примере я отображаю 300 чанков... 1,5 миллиона дополнительных кубов.

Потом я понял, в чем разница между точками. При рендеринге кубов не рисуются все стороны, которые направлены в сторону от глаза. Но я писал точку на куб, и дисплей рисует их независимо от ориентации. Так что в демо-версии я на самом деле нарисовал на миллион точек больше, чем кубы! "D'oh!" опять.

Вдобавок ко всему, кубики масштабируются вниз до тех пор, пока сторона становится в пиксель или около того по горизонтали. Точки имеют фиксированный размер, и я использовал размер=3. Так что на самом деле я рисовал в 9 раз больше пикселей для удаленных кубов, и на миллион точек больше, чем в кубах. Это убивало производительность.

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

Демонстрация рисует точки с треугольниками, а не с примитивом точки GL. Я не мог решить, что мне больше нравится для внешнего вида. Точки 2 на 2 прямоугольника и выглядят больше, чем треугольники, что делает далекие пейзажи явно более плотными. Они также медленнее, так как пишут больше пикселей на точку, чем треугольник.

Глупые баги

Мой последний "D'oh!" момент на этой неделе начался с очередной ситуации "этого не может быть". Я восстановил мир из данных Minecraft и отключил все лишние кубы. Пока я занимался этим, я удалил нижний слой кубиков, так как они в основном не используются, и я хотел лучшей производительности. К сожалению, с новыми данными мира, демо работало, наверное, в пять раз медленнее, чем в старом мире. Это было явно невозможно..

Я пошёл искать какую-то ошибку в коде извлечения, которая включала слишком много сторон. Я снова посчитал выходные треугольники в демо, и с новым миром их было меньше, как я и ожидал. Я отключил прозрачность, и это не имело никакого значения. Я отключил весь рендеринг и обнаружил, что он всё ещё медленный! Это означало, что лишнее время находилось на CPU, а не на GPU.

Я наконец-то отследил это до дурацкого бага. В настоящее время я получаю список чанков, которые нужно показать очень тяжелым способом. Я прохожу все позиции кусочков из глаза, и на каждом из них я спрашиваю хэш-таблицу, есть ли там кусочек. Затем я спрашиваю, виден ли кусок (пересекает view frustum). Если да, то я рисую его.

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

Ненавижу, когда я нахожу такие баги. Я смотрю на них и удивляюсь, как вообще работает код, и как я никогда его раньше не замечал. Это как идешь по старому зданию и нога проходит сквозь пол. Не обнадёживающе.

С этим исправлением, а также новой прозрачностью и новыми точками и смешанным рендерингом ландшафта, я получаю очень достойную частоту кадров на моем оборудовании. Попробуйте новое демо ниже и дайте мне знать, работает ли оно у вас.

Для тех из вас, кто не может запустить демо, есть видео тут. Переключитесь на 480p, чтобы увидеть мелкие детали.

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

The Demo

Я перестроил мир на основе данных, которые я извлек из Минекрафта. В новой версии больше нет набора видимости на поверхности всех кусков. Это позволяет значительно ускорить рендеринг. (Это также выглядит круто под землей.) Демо будет работать на старой версии, но вы должны скачать новую.

Скачайте Часть 19 Демонстрационный мир. Распакуйте его в тот же каталог, что и демо. Каталог "world" должен быть рядом с "docs" и "options.xml". Или вы можете отредактировать атрибут "worldDir" в "options.xml", чтобы указать на него.

Для Windows, качайте The Part 19 Demo - Windows.

Для Linux, качайте The Part 19 Demo - Linux.

Как упоминалось в последнем обновлении, я прекратил работу над всеми версиями фреймворка, кроме OpenGL 3.3. Если вы работаете под Windows, убедитесь, что ваши драйверы дисплея в актуальном состоянии. Возможно, вам понадобится зайти на сайт производителя драйверов (Nvidia или ATI) для получения последней информации. И Windows Update, и Dell настаивали на том, чтобы мой ноутбук был в актуальном состоянии с драйверами OpenGL 1.2, но на сайте ATI была полная реализация 3.3.

Эта демонстрация не содержит поля астероидов из части 18 (следующий раз, будем надеяться), но она содержит поддержку сжатых вершин, описанную в части 17. Файл options.xml настроен для маленьких (256 мега) видеокарт. Увеличьте параметры displayMemory и systemMemory для лучшей производительности.

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

Поэтому, если вы хотите, чтобы все отображалось медленно и качественно, установите оба значения summaryDistance и detailDistance на высокое значение, например 99999. Если вы хотите, чтобы все использовало прозрачность screen door, установите detailDistance в ноль, и summaryDistance в 9999. Если вы хотите, чтобы все рисовалось точками, установите оба значения detailDistance и summaryDistance равными нулю.

Исходники

Качайте The Part 19 Source, или идите в https://github.com/mgoodfel/SeaOfMemes гитхаб.