Pull to refresh

Comments 57

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

Ну так и не строится для моделей с анимацией BSP-дерево. Для неё используется просто сортировка. В статье это упомянуто.

Про коррекцию перспективы я в статье упомянул. У меня используется честное попиксельное деление. Трюки с кусочно-линейной перспективной проекцией (с делением каждые N пикселей в линии) не используются.

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

Ещё есть функция растеризации вовсе без перспективной проекции, которая употребляется для очень далёких полигонов, на которых отсутствие перспективной коррекции не будет заметно.

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

2) Трюки с растеризацией это примерно так: у полигона есть направление вдоль которого коррекция перспективы не нужна (например для пола - горизонталь, для стены - вертикаль). Растеризовать можно по четырем направлениям: по строкам, по столбцам и по двум диагоналям. Если для произвольного полигона выбирать один из четырех наиболее подходящих случаев, то "изгиб" корреции перспективы будет небольшим. В таком случае есть смысл дробить полигон, только не на N пикселей, а на равные сегменты, их количество будет не так уж велико.

1) Анимации моделей из треугольников "дружить" с BSP не требуется. Сейчас при рисовании моделей решительно всё равно, как получены координаты их вершин. Поэтому сейчас возможны произвольные анимации, хоть ragdoll.
Сортировки треугольников разных мешей между собою нету, это было бы слишком накладно. В большинстве случаев это и не нужно - объекты ведь друг в друга не должны проникать чисто из физических соображений. Да, в такой схеме нельзя будет двух обнимающихся NPC корректно отобразить, но это и не сильно нужно. А если вот прям совсем надо - можно две модели сделать одной.

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

1) BSP это не про координаты вершин, это про сортировку статической (не динамической) геометрии

2) Во времена Wolfenstein и Doom это помогало. Что изменилось?

Во времена Wolfenstein и Doom это помогало. Что изменилось?

Во времена Wolf/Doom камера поворачивалась только вокруг вертикальной оси. Поэтому и было "направление вдоль которого коррекция перспективы не нужна". Впрочем в некоторых играх камеру можно было слегка наклонять в вертикальной плоскости ценой небольшого искажения картинки.

Уже в Quake таких босяцких "трюков" не было. Наверное потому что Кармак ничего не смыслил в оптимизации =)

В те времена задача была просто наложить текстуру. В наше время используются софтверные шейдеры, где лучше бы интерполировать барицентрические координаты и делать деление на пиксель (1/x сейчас делать дешёво).

В Quake был не Z-buffer, поэтому такой вид оптимизации не применялся.

В Quake был не Z-buffer,

В Quake Z buffer был и заполнялся по информации из спанбуфера (вот тут) и использовался для отрисовки моделей.

В любом случае упомянутые выше "трюки" сейчас бесполезны.

Как и FastInvSqrt() из Quake3.

Интересно было бы реализовать Asynchronous Reprojection как раз для случаев недостаточной производительности, или просто в качестве оптимизации. До этого видео не слышал о такой технологии, хотя она активно используется в VR.

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

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

Не думаю, что вне ВР в этой технологии имеется особый смысл.

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

К тому же артефакты её использования могут смотреться весьма некрасиво.

Не думаю, что при пикселизированной картинке это будет сильно заметно. Тем более при достаточно высоком фреймрейте, например, монитор 144 гц и рендер в 60.

динамически варьировать разрешение экрана

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

Вот источник на который ссылается LTT:
2022-10-26 Async Reprojection outside of VR by Comrade Stinger

В описании есть ссылка на билд + исходники (проект на Unity 2021.3.10f1):
* AsyncTimewarp_Movement_Improved.zip
* AsyncTimewarp_UnityProject.zip

Рекоммендую поклацать вживую, на видео не передать все ощущения.
ИМХО этот трюк имеет право на жизнь за пределами VR.

Почему код под GPL?

Почему используется сильно нестандартный стиль форматирования кода?

Почему выбор пал на hecs, а не, например, legion или bevy_ecs? (или ещё что-то)

Почему используются интринсики вместо какой либо готовой обёртки для SIMD?

Код под GNU GPL - потому, что я сторонник свободного ПО.

Стиль кода такой - по привычке.

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

Интринсики используются потому, что проще было их самому напрямую заиспользовать, чем искать библиотеки-обёртки.

Код под GNU GPL - потому, что я сторонник свободного ПО.

Если хотите делать код, который будут переиспользовать внутри комьюнити, то рекомендую использовать двойную лицензию MIT+Apache. https://rust-lang.github.io/api-guidelines/necessities.html#crate-and-its-dependencies-have-a-permissive-license-c-permissive

Ну и в целом желательно гайдлайнам следовать во имя добра.

Возможно, и что-то другое подошло бы, но до них я не добрался.

Как bevy_ecs на мой взгляд будет лучшим выбором. Как минимум по той причине, что помимо ECS там рядом лежат разные штуки от движка bevy. Загрузка ассетов, рефлексия и прочее. В вашем случае разумеется нет смысла тащить весь bevy ибо там достаточно тяжелый рендеринг и некоторые связанные вещи. Банально компилироваться будет долго. Но оно всё сделано таким образом, что вполне можно использовать по частям. Почему bevy? Это довольно большой движок с развитым комьюнити. Документация, поддержка, туториалы и вот это всё. Очевидный минус такого решения: достаточно долго разбираться с этим всем. Очевидный плюс - если заниматься интегрированием в bevy, то получите большую аудиторию для своего кода. Но в любом случае рекомендую взглянуть.

Кстати, раз уж я упомянул SIMD, то рекомендую ещё для всех типов сделать перегрузку операторов: https://doc.rust-lang.org/nightly/std/ops/index.html

Ещё заметил, что вы, кажется, не используете clippy для проверки кода.

Для чтения структур из файлов рекомендую присмотреться к bytemuck и zerocopy.

Если возникнет такая потребность - могу изменить лицензию. Как автор, я могу себе такое позволить.

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

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

Включаю занудство:

VESA было стандартизировано аппаратное ускорение 2D в 1996 году (для простоты назовём это так). А вот в 1987 году был представлен IBM PS/2 с битблиттером на борту.

Формально серия Atari XL/XE, Commodore 64, MSX, Amiga, Atari ST не были игровыми приставками, но имели на борту чипы для манипуляций с графикой - а это конец 70-х - начало 80-х. Вообще любая платформа, которая понимала что игры на неё важны - вставляла чип, которые делал что-то аппаратно (а вот Джобс был не такой, ваши игрульки-фигульки это не про Apple, хотя и на них портировали многие игры).

Из раннего псевдо-3D (которыми являлись и тот же Doom и Wolf3D) могу быстро вспомнить Ballblazer 1984 года и Elite 1984 год.

Elite 1984 год

А почему псевдо? По-моему, в Элите как раз 3Д-движок был честным, без всяких компромиссов вроде построчного сканирования как в волфе. Другое дело, что ничего не умел, кроме проволочных моделей, ну так и железо было соответствующее.

Ко всему прочему, в программном 3d рендере неплохо бы смотрелись воксельные спрайты, КМК.

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

Ни чего не мешает добавить вокселям нормали и любые другие параметры.

Такая "годнота", что машинально стал искать плашку [Перевод] :-)

Да, @Panzerschreck давно на gamedev.ru годноту делает. (=

Привет старожилам.

А динамические тени есть/будут ? Я так и не осилил сделать stencil map в своем растеризаторе в середине 90х. Тени получались рваные, не хватало точности. Ну и делал наверняка неправильно.

Динамические тени есть от динамических источников света - через карты теней. Но пока в них не рисуются модели из треугольников, что просто не реализовано, хотя практически вполне возможно.

С тенями от статического света всё не так хорошо. Можно рисовать тёмную кляксу-декаль под ногами, как делалось в Quake III Arena, это просто и быстро. Можно делать фейки, как в Quake II, когда модель проецируется на уровень пола и рисуется чёрным цветом с полупрозрачностью, это чуть сложнее, но тормознутее. В конце концов можно даже находить честное пересечение проекции треугольников модели с геометрией уровня, что будет давать весьма хороший результат, но будет жутко тормозить.

Ну а на счёт стенсильных теней в стиле Doom 3 - такое маловероятно. Для них нужно строить буфер глубины и держать стенсил-буфер, кроме того, надо строить теневые объёмы для моделей, что мало того что медленно, так ещё и требует особой топологии моделей (модели с дыркаи будут отбрасывать кривые тени).

Ну а на счёт стенсильных теней

Да, именно их. Вот например как тут (очень хотел повторить, когда увидел в ~1997 году)

https://youtu.be/tFoJAIM1LXs?t=249

Для алгоритма стенцил-буфера нужен Z-buffer. Я правильно понял что движок из статьи его не использует?

Так точно, Z-buffer не используется для построения картинки.

Скажите, а в игроиндустрии или в ином практическом применении ваши наработки могут пригодиться или это сделано из чувства прекрасного?

Если что я не вкладываю обесценивающие нотки в вопрос. А правда интересно узнать потенциал применимости сегодня.

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

Как вариант - "альтернативная ветка" развития реалтайм-рендеринга. Всё рендерится программно, но в низком разрешении, и затем с помощью аппаратного ускорителя нейросетей апскеилится до нативного разрешения, как это делается при DLSS.

Тогда в качестве аппаратного ускорителя нам нужна уже не большая сложная видеокарта, а только её часть, отвечающая за ускорение нейронных сетей. И разработать такое устройство куда проще, чем полноценную современную видеокарту. Более того, это может быть не универсальный ускоритель нейросетей, а именно жесткий неизменяемый ASIC-DLSS для нейросетевого апскейла. Это ещё проще и ещё дешевле.

Спасибо. И поучительно, и ностальгия...
Запустил на Xeon E5-2640 + 64Gb. Demo_generic выдал 60-80 fps.
Возможно, стОит приделать к демке вывод в лог основных результатов прогона: fps max, fps min и медиану (не среднее). А также кратко параметры системы: процессор, OS, память и т.п.

Вывод конфигурации сцены - однозначно стоит.

На счёт статистики FPS - по-хорошему надо сделать какой-то режим бенчмарка - с запрограммированной траекторией камеры, чтобы результаты были сравнимы между запусками.

Безумная крутотень!

Ещё миллисекунду-две занимает отправка картинки на видеокарту для последующего показа, что опять же может зависеть от ОС, драйверов и прочего.

Я как-то кодил поделие на спрайтах на pygame и захотел приделать перспективу как во второй дьябле. Тоже пришлось собирать все пиксели в кучку на процессоре (1500*1000), и фпс ушел в 20. Передача статичной картинки, если правильно помню, обходилась в 25 фпс, на разных машинах с разными системами. Я думал, это железячное ограничение, связанное с передачей больших данных на видеокарту, но видимо, это что-то с пигеймом.

Примеры относительно-современных игр, с нарочито-старомодной графикой это...

+1: Autumn Night

Напишите пожалуйста еще и про опыт и ощущения при разработке на Rust. Что понравилось, что не понравилось.

А что насчёт производительности WARP? Тоже софтварный растиеризатор.

Насколько я понял, это просто одна из реализаций Direct3D. И, сдаётся мне, работает она столь же медленно, как любая другая эмуляция графического API на центральном процессоре.

Пробовали ray tracing? Для обычения в свободное время пишу на rust алгоритмы из книги https://gabrielgambetta.com/computer-graphics-from-scratch/index.html

Удивительно насколько это простой принцип в сравнении с растеризацией.

В книге код на javascript, удивительно, что на rust выходит примерно столько же кода, только он в разы читаемее из-за типов ;-)

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

Ну кстати есть человек, который выпускает path-traced порты или переделки старых игр. После того как Nvidia сделала path-traced порт Quake 2, этот товарищ сделал:

  • Doom

  • Quake

  • Serious Sam

Дум меня до глубины души впечатлил, особенно тенями (игра выходит на новый уровень просто). Сэм и квейк не очень как-то. И да … это всё мягко говоря не очень торопливо работало на современной видеокарте серии 30*.

Советую еще ray-marching для разнообразия попробовать. Его можно сразу в пиксельном шейдере писать (видео, код).

>При этом многопоточность не даёт кратного прироста производительности, главным образом потому, что местами процесс построения кадра банально упирается в доступ к шине памяти
У вас демка занимает 22 мегабайта, в современных десктопных процессорах кэш L3 достигает 96 мегабайт. Не даром 5800X3D c монструозными 96 мегабайтами до сих пор держит первенство среди процессоров для игр. Но и даже в Интеловские, меньшие кэши, при желании демка поместилась бы целиком.
Так вот к чему я, при минимальном наборе текстур и их низком разрешении узость шины ОЗУ опять же не должна быть значимым фактором, разве нет?
P.s. побегал в демке на ультрабуке с 6/12 5500u, в среднем 110-140 фпс, имея дикое желание просадить его по максимуму, за 10 минут беготни поймал просадку до 37 фпс. При том частота процессора была вполне ультрабучная, 2.1-2.5 ггц, то есть троттлинг я бы и не словил. Потоки стабильно нагружались все 12.

22 Мегабайта - это на диске в пожатом виде. Текстуры при загрузке расжимаются и занимают больше памяти, чем на диске. Данные карты тоже расжимаются. Кроме того динамически выделяется ещё много памяти под самые разные нужды. Итого у меня выходит (по диспетчеру задач) около 160 Мб памяти процесса.

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

Тредриппер в минимальных требованиях к софтрендеру смотрелся бы странновато.

В любом случае латентность доступа к L3 в другом чиплете на уровне или хуже чем к памяти.

https://github.com/nviennot/core-to-core-latency

AMD Ryzen Threadripper 3960X, 3.80GHz, 24 Cores, Zen 2, 3rd Gen, 2019-Q424ns, 94ns

AMD Ryzen Threadripper 1950X, 3.40GHz, 16 Cores, Zen, 1st Gen, 2017-Q325ns, 154ns

AMD Ryzen 9 5950X, 3.40GHz, 16 Cores, Zen3, 4th gen, 2020-Q417ns, 85ns

В серии 5к чуть лучше, как видно по 5950X, но тоже не фонтан.

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

Графика вроде простенькая, но красивая. Как майнкрафт с шейдерами. два e5 2630l v3 со всеми анлоками 107-125фпс

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

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

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

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

А еще вот интересно - нет ли каких-то техник рендера, принципиально не ложащихся на GPU? Если что-то такое заюзать - это может придать работе дополнительный смысл и шарм.

Например был во всяких magic carpet подход с рисованием ландшафта - когда используется карта высот, рисуются столбики снизу вверх, а для отсечение юзают что-то типа одномерного z-буфера. Вот что-то такое не придумывалось/вспоминалось/пробовалось?

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

Sign up to leave a comment.

Articles