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

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

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

Часть 1: Изображения

Компьютеры работают с изображениями как с наборами значений, называемых пикселями (сокращение от «элемент изображения»). Эти пиксели часто имеют в общей сложности 24 бита и разделены на три канала, по одному для каждого основного цвета (красный, зеленый и синий). Как вы, возможно, знаете, 8 бит могут представлять число до 255, что кажется очень маленьким, но поскольку у нас их три, это означает, что мы можем представить 256 * 256 * 256 (~ 16 миллионов) различных цветов для пикселя. .

Обратите внимание, что хотя вы ожидаете, что пиксель со значениями R:128 G:128 B:128 будет примерно наполовину ярким, на самом деле это не так. Объяснение этого выходит за рамки этой статьи, но вкратце вы должны знать, что значения пикселей обычно не обрабатываются линейно. Если вы хотите узнать больше об этой теме, я рекомендую вам перейти по ссылке о гамма-коррекции в разделе ресурсов [1].

Одного изображения часто бывает недостаточно. Видеоиграм, например, нужно много изображений в секунду. Эти изображения, называемые «кадрами», нужно обрабатывать очень быстро. Большинство современных видеоигр работают со скоростью 30 или 60 кадров в секунду.

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

Часть 2: Формы

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

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

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

Слово растеризация происходит от латинского слова «грабли». Растеризация «прочесывает» треугольник, строка за строкой, и проверяет для каждого пикселя, находится ли он внутри треугольника [3].

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

Возможно, вы захотите переместить треугольники (например, для анимации), задать перспективу, изменить соотношение сторон экрана или переместить камеру в сцене. Для этого к вершинам применяются матрицы. Если вы никогда не сталкивались с матрицами (или забыли о них), не волнуйтесь! На самом деле они довольно просты и очень удобны, когда вы к ним привыкнете. Я включил ссылку на линейную алгебру в раздел ресурсов для получения дополнительной информации [2].

Одно измерение, глубина, полностью удаляется при переходе от 3D к 2D. Часто сохраняется еще один буфер с одной записью на пиксель. Этот буфер отслеживает глубину и поэтому называется буфером глубины или z-буфером. Это полезно, если в сцене есть несколько треугольников, и они оба будут отображаться для одного пикселя. Затем мы можем использовать значение z-буфера, которое первый из двух записал в z-буфер, чтобы определить, находится ли новый треугольник поверх старого. Прозрачные объекты, к сожалению, являются ахиллесовой пятой этой техники.

Это все хорошо, но если вы будете рендерить с плоскими цветами, вы не сможете многого добиться. Чтобы иметь возможность видеть контур и иметь подобие реализма, нам нужен свет.

Часть 3: Свет

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

Локальное освещение имеет дело со светом, который непосредственно падает на объект. Представьте, что вы идете по улице ночью, и прямо на вас светит фонарный столб. Это будет пример точечного света. Существуют и другие типы источников света, такие как точечные источники света, которые излучают свет вокруг себя (например, лампочка), и направленные источники света, которые можно рассматривать как точечные источники света с бесконечной силой на бесконечном расстоянии в одном направлении (например, лампочка). , солнце).

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

Существует множество формул для затенения объектов, наиболее распространенной из которых является модель затенения Блинна-Фонга, которая разделена на три части:

  • Рассеянный свет, то есть свет, отражающийся во всех направлениях, как от глины.
  • Зеркальный свет — это свет, который отражается только в общем направлении, например, от металла.
  • Окружающий свет, который в основном является приближением к глобальному освещению.

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

Часть 4: Текстуры

Вместо того, чтобы рисовать тысячи треугольников, каждый со своим цветом вершин, для создания деталей на поверхности объекта, вместо этого можно выбрать текстурирование указанных объектов. Текстуры — это 2D-изображения, которые применяются к моделям с использованием так называемых текстурных координат или UV-координат. Последнее название происходит от того факта, что два измерения текстуры называются U и V (вместо, скажем, X и Y). Таким образом, каждая вершина имеет набор UV-координат.

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

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

Вы, наверное, уже заметили, что это требует много работы. Экран HDTV имеет разрешение 1920x1080. Это означает, что для каждого кадра необходимо вычислять около двух миллионов пикселей. Если вам нужны плавные 60 кадров в секунду, вам нужно отрисовывать все за 1/60 или ~0,16 секунды. Если бы вы обрабатывали один пиксель за другим, вам нужно было бы обрабатывать один пиксель примерно за 100 наносекунд. Это невыполнимо, и здесь на помощь приходят графические процессоры.

Часть 5: GPU

Графический процессор (термин, введенный Sony в 1994 г.) — это специализированное аппаратное обеспечение, оптимизированное для графических задач. В ЦП большая часть работы выполняется последовательно (одна операция следует за другой). В графическом процессоре большая часть работы выполняется параллельно.

Вы можете представить, что GPU состоит из сотен крошечных ядер CPU, выполняющих одно и то же. Графические процессоры оптимизированы для одной задачи, которую они конвейеризируют. Мы называем конвейер графического процессора графическим конвейером.

Графическому конвейеру можно было бы посвятить целую статью [4]. Короче говоря, рендеринг разделен на части, называемые «этапами».

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

Наиболее важными этапами являются:

  • Этап вершинного шейдера, который вычисляет данные для каждой вершины (например, умножение позиции на матрицу).
  • Этап растеризации, который, что неудивительно, связан с растеризацией.
  • Этап пиксельного (или фрагментного) шейдера, который вычисляет цвет пикселей (например, путем сэмплирования текстур или расчета освещения, как показано выше).

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

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

Спасибо за чтение!

Дополнение: полезные ресурсы

Кредиты

Благодарим Эммануэля Лаженесса за его предложения по улучшению ясности этой статьи.