D:\sideБлогРазвитие взаимодействия с графическими системами

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

Так вот. Когда-то, в стародавние времена, компьютеры предполагалось использовать не для игр. Они существовали, преимущественно, как «рабочие лошадки»: производили разнообразные вычисления, к примеру. В графике потребностей почти не было, и дисплеи часто были неспособны показывать что-либо, кроме текста. Когда-то не было даже дисплеев! Но о графике в то время говорить, по-моему, несколько странно. Да, были попытки устраивать различные непотребства на осциллографах, но широкого распространения подобные творения не получили.

Спустя какое-то время, получила распространение простейшая графическая система. Я самолично работал с ней под операционной системой DOS. Условно: представьте, что весь ваш экран - сетка из пикселей. Ну, сейчас очевидно, что это так. Но сейчас это запомните - это важно.

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

Откуда такие данные? Простые вычисления. Один бит: 0 или 1, представляет два значения. Два бита - в два раза больше: 00, 01, 10, 11. Больше бит - больше удвоений. В байте 8 бит. Поместится в них \( 2^{8} = 256 \) разных значений. Просто? Вроде да. О всяких трюках в двоичной системе мы поговорим позже.

Итак. У нас есть таблица из \( 640 \cdot 480 = 307200\) пикселей. Как она представлена в оперативной памяти? Очень просто - одной большой строкой. Как понять, что это таблица? Никак, предполагается, что это мы уже знаем из того факта, что включен некоторый графический режим. Пронумеруем строчки сверху вниз от 0 до 479, а столбцы от 0 до 639. Окей. Если таблица представлена построчно, то адрес пикселя в координатах \((a, b)\) - base + a + b*640 (a›ый столбец, b›ая строчка). base - адрес левого верхнего пикселя, и он обычно считается известным.

Но менять напрямую эту область памяти - так себе затея. Почему? Потому что изменения будут отражаться на ближайшем кадре незамедлительно. Вы его не дорисовали? ХРЯСЬ. Рисуете сложную фигуру? ХРЯСЬ, на части. Перерисовываете весь кадр с нуля каждый раз? ХРЯСЬ, мигание. Для этого придуман трюк «двойная буфферизация». Трюк предельно прост - сделаем буфер такого же размера, как буфер экрана, и рисовать новый кадр будем в нём. Доделаем - скопируем получившееся в область видеопамяти.

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

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

Разумеется, появились видеокарты с ускорением графики. Нас больше всего интересует тот момент, когда на сцену вышла 3dfx. Изначально эта компания занималась производством графических чипов для игровых автоматов. Но затем они спровоцировали взрыв объёмных компьютерных игр своей серией 3D-ускорителей VooDoo. Была у них одна особенность - использовать их можно было только при наличии обычной видеокарты. То есть, это была всего лишь надбавка для игр, для игровых компьютеров. Поэтому 3dfx не выдержала конкуренции с nVidia и впоследствии была куплена последней.

Итого, видеокарточки умели оперативно работать и с 2D-графикой, и с 3D, но по-разному. Но если прикинуть, 2D-графику можно без потерь представить и в 3D. Вот и получилось, что дальнейшее развитие графики ушло именно в объёмную графику.

Конечно, здесь очень хотелось применить подход, аналогичный 2D-графике. Просто разбить всю сцену на отдельные элементы пространства (воксели). Близкая аналогия - кубы в Minecraft, за тем лишь исключением, что из них состоит вообще всё. Ещё более близкая - Cube World. Но раз за разом люди убеждаются, что на текущем технологическом уровне смысла в этом мало, для хранения даже небольших сцен требуется колоссальное количество памяти. Сейчас мы потихоньку приближаемся к уровню, когда это может быть оправдано. Джон Кармак не так давно этим занимался в новой версии движка id Tech, но отложил это дело до лучших времён.

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

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

Что дальше? Появились «вычислительные шейдеры», или же «геометрические», которые могут создавать на сцене новые геометрические фигуры. Для чего? К примеру, для генерации сложных фигур «на лету». Это пока сравнительно новая тема, и поддерживается только мощными видеокартами на ПК и аналогичных, мобильные устройства ещё только стоят в очереди на получение этой фичи. Используется она, например, в проекте SpaceEngine для планет. Очень рекомендую с ним ознакомиться, потрясающая штука.

Далее графика выходит за рамки построения движущейся картинки в реальном времени.

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

Некоторые занимались построением фотореалистичных картин, выражая их математически, что позволяет их произвольно масштабировать. Подобный проект демонстрировала организация под названием Euclideon, называя это Unlimited Detail. Однако в ответ они получили только тонну опровержений, о том, что в реальном времени этой технологией сейчас картинку не нарисовать. Что примечательно - никак на них не ответили. До сих пор они не продемонстрировали ни одной интерактивной демо-версии, что даёт основания полагать, что это просто раздутая из ничего история, и этой технологии просто нет.

Уже упомянутый Джон Кармак, скорее всего, вернётся к воксельной графике, когда компьютеры станут помощнее.

Посмотрим, что будет дальше!