ТЕХНИЧЕСКИЕ СРЕДСТВА КОМПЬЮТЕРНОЙ ГРАФИКИ

Если вспомнить такие легендарные игры, как Doom и Quake, то в них весь расчет графики целиком выполнялся центральным процессором (Central Processor Unit, CPU). Графический ускоритель для персонального компьютера в те времена обеспечивал только доступ процессора к видеопамяти и ее отображение на экране. Точно так же многие спецэффекты для кино рассчитывали исключительно на центральном процессоре (точнее, на большом количестве процессоров). Однако со времен Quake возможности графических ускорителей принципиально изменились, они стали программируемыми, и для них сейчас используется термин графический процессор (Graphics Processing Unit, GPU).

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

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

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

Вначале логика обработки вершин и фрагментов задавалась на специальном ассемблере, однако довольно быстро произошел переход на высокоуровневые языки для написания шейдеров. Сейчас в OpenGL для написания шейдеров служит основанный на С язык GLSL (OpenGL Shading Language).

Современный GPU содержит большое количество вычислительных блоков, способных параллельно обрабатывать большой объем данных, снабжен аппаратным растеризатором, аппаратной поддержкой удаления невидимых поверхностей с помощью буфера глубины и блоками доступа к текстурам. Однако передача данных между CPU и GPU происходит через шину PCI Express, что обычно и является узким местом, ограничивающим скорость передачи данных.

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

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

Графический конвейер

Рис. 9.1. Графический конвейер

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

Подробнее шейдеры и работа с ними рассмотрены в гл. 13.

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

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

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

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

Фрагмент, поступающий во фреймбуфер (обычно под этим термином понимают набор буферов, в которые идет запись), может изменить любой из этих буферов, например записать значения цвета и глубины и увеличить на единицу значение в буфере трафарета. Соответственно, современные GPU имеют аппаратную поддержку растеризации примитивов, удаления невидимых поверхностей с помощью 2-буфера, наложения цветов с учетом полупрозрачное™ (альфа-блендинг).

Современный GPU является массивно-параллельным вычислительным устройством — он способен параллельно выполнять большое число операций. В состав GPU входит большое количество процессоров (ядер), способных параллельно обрабатывать вершины и фрагменты.

Несмотря на множество таких процессоров, количество блоков декодирования команд и управления намного меньше, чем число ядер. Соответственно, обычно ядра группируются, и на группу ядер приходится всего один блок декодирования и управления. Это означает, что каждая такая группа ядер одновременно выполняет одну и ту же команду (подобная архитектура обычно называется SIMD, Single Instruction Multiple Data). Для графических процессоров подобная архитектура оказывается очень удобной: обычно обработка всех вершин осуществляется одним вершинным шейдером (или небольшим числом шейдеров), а обработка фрагментов — одним фрагментным шейдером.

На рис. 9.2 изображено устройство мультипроцессора для GPU GeForce 8800 — весь GPU состоит из набора таких мультипроцессоров. На рисунке нс отображены блоки растеризации и работы с текстурами, показана только вычислительная часть. Каждый такой мультипроцессор состоит из набора скалярных ядер (SP), блока декодирования очередной команды (Instruction Fetch), разделяемой памяти (Shared Memory), которая может совместно использоваться ядрами, набора регистров (Register File) и специальных ядер для работы с трансцендентными функциями (SFU).

Несмотря на то что архитектура GPU может отличаться от приведенной на рис. 9.2, общий подход остается таким же.

Устройство потокового мультипроцессора для GPU

Рис. 9.2. Устройство потокового мультипроцессора для GPU

GeForce 8800

За счет наличия большого количества процессоров современные GPU в десятки раз обходят по быстродействию традиционные CPU. Появилось целое направление — GPGPU, посвященное использованию GPU для решения различных неграфических задач. Для этой цели существуют специализированные API, такие как OpenCL и CUD А.

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

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

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

Контрольные вопросы

  • 1. Назовите основные стадии конвейера рендеринга.
  • 2. В чем состоит задача вершинного шейдера?
  • 3. В чем состоит задача фрагментного шейдера?
 
Посмотреть оригинал
< Пред   СОДЕРЖАНИЕ   ОРИГИНАЛ     След >