Математика в Gamedev по-простому
506 subscribers
66 photos
1 video
3 files
30 links
Как на самом деле работают стрельба, толпа NPC, графика, физика тканей.

Канал про то, что ИИ не заменит: понимание.

Разборы на пальцах, рабочий код, интерактивы. dev-math.ru

Сотрудничество: @it_bizdev
Download Telegram
Квиз: Как игры рисуют кадр?
https://youtube.com/shorts/F6enWvT6lQk?si=pXOCdl87qk5F6BiC

А что вы думали? Статьи просто так? А ну взяли ручки и проходим контрольную. По статье по дроуколлам и пространствам.

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

#геймдев #игры #графика #квиз #devmath
🔥5
Как вы получаете пиксель

Далёкие провода ЛЭП, перила моста, ванты, тонкая антенна на крыше — вроде на месте, но стоит чуть повести камерой, и линия начинает рваться: тут кусок есть, тут его нет, и всё это дрожит. Первое объяснение, которое приходит в голову: «ну, деталь мельче пикселя, разрешения не хватает». Оно почти верное, но только почти.

Растеризатор (узел, который превращает три вершины в закрашенные пиксели) не считает, какую долю пикселя накрыл треугольник. Для него пиксель — вообще не маленький квадратик, который можно зацепить краем. Пиксель — это одна точка в его центре. И треугольник закрашивает пиксель тогда и только тогда, когда накрывает эту точку. Так записано в правилах растеризации Direct3D, так же ведут себя OpenGL и Vulkan.

Собственно, отсюда и мерцание. Он не «слишком маленький» — он проходит между центрами пикселей. Камера сдвинулась на полпикселя: где-то центры попали внутрь, где-то нет — по линии побежали мерцающие дырки.

Вывод: тонкую геометрию не чинят геометрией. Добавить проводу полигонов бесполезно — толще в экранных пикселях он от этого не станет. Работает другое: держать деталь шириной хотя бы в пиксель на экране (билборд или шейдер, который не даёт ширине упасть ниже пикселя), брать несколько точек покрытия на пиксель (MSAA), либо увести деталь из геометрии в текстуру, где у неё есть альфа и фильтрация. Если у вас мерцают перила — разговор не про меш, а про то, чем вы их рисуете.

Ну и крайний случай: треугольник может не нарисоваться совсем. Мелкий треугольник или длинный тонкий слайвер проскакивает в зазоре между центрами и не даёт в этом кадре ни одного фрагмента, хотя площадь у него есть и вершины на месте. Сдвинулись на полпикселя — даёт. Для геометрии размером около пикселя вопрос «виден или нет» решает субпиксельная позиция.

И из того же правила растёт вопрос, о котором обычно не думают. А если точка-сэмпл легла ровно на ребро, общее у двух соседних треугольников меша? Закрасить у обоих — по шву пройдёт двойная краска, на прозрачности это заметный тёмный рубец. Не закрасить ни у кого — по мешу побежит строчка дырок. Железо этот случай разруливает, и разруливает одинаково у всех.

Как именно растеризатор решает «внутри или снаружи», как он поступает с сэмплом на общем ребре и кто из тысячи треугольников, попавших в один пиксель, остаётся на экране — разберём в пятницу.

#devmath #графика #математика #рендер #растеризация #gamedev
🔥5
Поговорим про Overdraw

Сложная сцена проседает по fps. Первым делом вы идёте резать полигоны: упрощаете меши, расставляете LOD'ы, чистите вершины. Треугольников стало заметно меньше — а кадр не полегчал. Профайлер показывает, что видеокарта всё время сидит в пиксельном шейдере, а не в геометрии.

Дело в том, что один пиксель за кадр закрашивается не по разу. Поставьте три объекта друг за другом по глубине: дальше всех стена, перед ней ящик, перед ящиком стойка. Рисуются они в том порядке, в каком их отдал движок, — и если дальняя стена попадёт под кисть первой, она закрасит свои пиксели и целиком отработает шейдер: текстуры, свет, туман. Потом поверх ляжет ящик — и тоже целиком. Потом стойка. На экране виден только ближний слой, а пиксельный шейдер отработал по числу слоёв. Это и есть overdraw — работа, оплаченная за пиксели, которые в итоге никто не увидит.

Плотный интерьер, свалка пропов, наслоённые полупрозрачные эффекты — там overdraw набегает быстро. Горит fillrate. Fillrate (филлрейт) — это скорость, с которой видеокарта заливает пиксели в буфер кадра: её пропускная способность на закраску, отдельная от пропускной способности на геометрию.

Поймать довольно просто. Подведите камеру вплотную к скоплению объектов: если fps проседает там, где в кадр влезает даже меньше геометрии, но она плотно перекрывается по глубине, — вы уперлись в overdraw, а не в треугольники. А ещё лучше — включите режим overdraw: он есть у Unity, Unreal и мобильных профайлеров. Места, перекрашенные много раз, светятся всё ярче. Это буквально ваш счёт за кадр, нарисованный поверх сцены, — один раз увидев его, вы перестаёте гоняться за полигонами.

Видеокарта умеет выбрасывать перекрытые пиксели ещё ДО того, как посчитает их шейдер. Но у этого механизма есть условие, и когда объекты рисуются «как легли в сцене», условие не выполняется — и каждый невидимый слой всё равно оплачен. Что это за условие и что делать, когда шейдеры настолько дорогие, что и сортировка не спасает, — разберём в пятницу.

#devmath #графика #математика #рендер #оптимизация #gamedev
🔥11
Мобильный рендер: Дело не в мощности GPU
https://youtube.com/shorts/6WtXTERxLaI?si=65mL_ZQZno6b0fdO

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

Про шину помнить очень важно. Как она загружается и тому подобное. В статье про дроуколлы это разбиралось. Просто где-то с 2017 года я в основном разбирался в том, как запустить красивую картинку на «калькуляторе». Если взять даже этот проект, где нужно было под вебгл с поддержкой мобилки сделать рабочую красивую 3д сцену. Когда нужно завести миллионы полигонов в вебе или что еще страшнее в WebAR, то нужно знать что вертексные параметры можно подрезать.

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

#devmath #gamedev #unity3d #оптимизацияигр #мобильныйгеймдев
🔥5
Треугольник становится пикселями
https://dev-math.ru/articles/raster/

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

Разбираем:

— почему тонкий провод вдали мерцает и рвётся;
— и почему добавить ему полигонов бесполезно;
— отчего далёкие поверхности дерутся полосами друг сквозь друга — и при чём тут near, а не far;
— как один и тот же кадр выходит то в пять слоёв, то в один — не тронув ни единого полигона;
— почему на PlayStation 1 текстуры «плыли» — и что за одно деление это лечит.

#devmath #графика #математика #рендер #растеризация
🔥6🤝1
Please open Telegram to view this post
VIEW IN TELEGRAM
Фильтрация текстур

Подошли в игре вплотную к стене — и вместо кирпича размытое пятно. Можно подумать: «фильтрация мылит, поставлю point». Ставите — мыло уходит, приходят квадратики. Ощущение, что выбираешь между двумя видами испорченной картинки. Так и есть, и вот почему.

Текстура для шейдера — не картинка, а функция: подали координату, получили цвет. Только в памяти лежит конечный массив, и значение каждого текселя — это точка в центре его клетки. Ровно как пиксель на экране, про который мы говорили на прошлой неделе.

Когда вы подошли к стене вплотную, на один тексель приходится несколько экранных пикселей — и между известными точками надо что-то придумать. Nearest придумывает «возьму ближайшую» — получаются квадратики. Билинейная фильтрация придумывает «смешаю четыре соседние точки по расстоянию» — получается плавно, то есть мыло. Новой информации ни там, ни там не появилось: обе читают одну и ту же текстуру. Фильтр не создаёт новых деталей — он выбирает, как будет выглядеть их нехватка.

Отсюда вывод, который экономит время: если текстура мылит вблизи, крутить фильтр бесполезно. Мылит — значит текселей на этот кусок экрана просто мало. Лечится плотностью текселей: крупнее текстура, мельче тайл на поверхности, отдельная detail-карта поверх — или, если так и задумано, point-фильтр и пиксель-арт как художественное решение (Minecraft именно поэтому и не мылит).

И бонус. Если картинка не столько мылит, сколько «чуть мылит и чуть съехала» — обычно это забытые полтекселя. Значение текселя лежит в центре клетки, поэтому при переводе координаты в текселе-пространство вычитают 0.5. Забыли — и вся текстура уехала на полклетки; на кирпичной стене незаметно, на UI и атласах иконок видно сразу.

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

#devmath #графика #математика #рендер #текстуры #gamedev
🔥7👍3
10 000$ за баг в GTA
https://youtube.com/shorts/3iJa_wPmGKM?si=pdZ1e-VWVgGtrMhh

Семь лет GTA Online грузилась по пять минут — на любом железе. Чинить взялся не Rockstar, а игрок под ником t0st взялся, и хватило ему выходных. И получил за это 10 000$.

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

#devmath #разработкаигр #gta #программирование #gamedev
🔥2👎1
Пол вдали превратился в кашу?

Смотрите вдоль пола — и метрах в десяти плитка перестаёт быть плиткой. Не мерцает, не рябит, а ровно и мутно мылит, как будто туда забыли положить текстуру. При этом та же самая текстура на стене прямо перед носом читается прекрасно, и под ногами всё резко. Мылит именно то, что уходит вдаль под острым углом: пол, дорога, длинная стена сбоку.

Дальше начинается знакомый заход. Раз мыло — значит мало разрешения. Дорогу поднимают с 1024 до 2048, потом до 4096, память растёт вчетверо на каждый шаг, стриминг начинает захлёбываться на въезде в локацию — а дорога вдали всё такая же мутная. Ловили такое?

Собственно, вот в чём дело. Пиксель накрывает на поверхности не точку, а кусок. Когда поверхность стоит к вам лицом, этот кусок — аккуратный квадратик. А когда пол наклонён почти под ноль, он превращается в длинную узкую полосу: поперёк взгляда пиксель накрывает пару текселей, а вглубь — несколько десятков. И подробность сэмплер выбирает одним числом на весь след — по его длинной стороне. Тридцать два текселя вглубь — берётся версия текстуры, уменьшенная в тридцать два раза. Поперечная деталь, которой разрешения хватало с большим запасом, усредняется вместе со всем остальным. Вот и мыло.

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

По сути отсюда и ответ, почему апскейл ассета не помогает: текстура крупнее — обе оси следа растут одинаково, вытянутость остаётся той же. Вы отыгрываете один уровень подробности ценой вчетверо большей памяти, а перекос как был, так и остался. Настройка, которая чинит именно перекос, называется анизотропная фильтрация — та самая строчка «Anisotropic Filtering: 16x» в настройках графики. Она щупает вытянутый след не одной точкой, а несколькими вдоль длинной оси.

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

Ну а самое интересное — дальше. Откуда вообще берётся то самое одно число на весь след и почему в нём стоит именно максимум; сколько на деле стоит ×16 и почему не в шестнадцать раз дороже; и почему выкрученная анизотропия при выключенных мипмапах не работает вовсе — а такую комбинацию встречаешь регулярно. Разберём в пятницу. Там же пол, который можно наклонить ползунком и кликнуть по нему: видно и сам след пикселя в пространстве текстуры, и как в нём выстраиваются точки выборки, когда вы переключаете ×1 → ×16.

#геймдев #графика #математика #рендер #текстуры #gamedev
🔥43🫡2
Статья немного задерживается, так как автор в ловушке
17😱5
Почему далёкая текстура мерцает
https://dev-math.ru/articles/textures/

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

В статье разберем:

- почему тексель — не пиксель и что на самом деле делают nearest и билинейная фильтрация;
- откуда берётся мерцание вдали — алиасинг при уменьшении — и как его лечат мипмапы;
- почему при взгляде вдоль пола картинка мылится и что с этим делает анизотропная фильтрация;
- сколько всё это весит в памяти и как устроено блочное сжатие BCn и ASTC;
- и как эти решения складываются в один запрос к сэмплеру.

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

#devmath #графика #математика #рендер #текстуры
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13