netlib.narod.ru< Назад | Оглавление | Далее >

Не настраивайте ваш телевизор!

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

Черно-белый, как в старые времена

Следуя духу начинания с чего-нибудь легкого, как насчет того, чтобы создать черно-белую визуализацию? Это очень просто и вам потребуется лишь определить интенсивность пикселя и использовать ее для вывода градаций серого цвета. Первый этап — вычисление интенсивности пикселя. Первым приходящим на ум вариантом является определение интенсивности как среднего арифметического красной, зеленой и синей компонент текстуры: Интенсивность = (Красный + Зеленый + Синий) / 3.

Хотя мы и получим значение оттенка серого, оно будет не совсем правильным, поскольку подразумевалось, что все компоненты цвета имеют одинаковый вес. Неправильно предполагать, что человеческий глаз одинаково реагирует на все составляющие цвета. В действительности ваш глаз видит составляющие цвета по-разному, будучи более чувствительным к зеленому и менее чувствительным к синему. Ряд исследователей определили оценочные веса цветовой восприимчивости человеческого глаза и решили, что интенсивность должна вычисляться следующим образом: Интенсивность = 0.299 * Красный + 0.587 * Зеленый + 0.184 * Синий.

Начнем с разработанного в предыдущем разделе шаблона шейдера, который вы можете загрузить из расположенного на CD-ROM файла shader_2.rfx, и модифицируем пиксельный шейдер прохода копирования цели визуализации, чтобы он брал исходный пиксель текстуры и модифицировал его согласно приведенной выше формуле. После этого простого усовершенствования вы должны получить такой код шейдера:

sampler Texture0;
float4 ps_main(float2 texCoord: TEXCOORD0) : COLOR
{
    // Читаем цвет источника
    float4 col = tex2D(Texture0, texCoord);
    float Intensity;

    // Вычисляем интенсивность черно-белого пикселя по формуле
    // I = 0.299*R + 0.587*G + 0.184*B
    Intensity = 0.299*col.r + 0.587*col.g + 0.184*col.r;

    // Обратите внимание, что можно было использовать и скалярное произведение
    // Intensity = dot(col,float4(0.299,0.587,0.184,0));

    // Возвращаем интенсивность в итоговом цвете
    return float4(Intensity.xxx,col.a);
}

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


Рис. 5.7. Разворачивание операции скалярного произведения в итоговую формулу

Рис. 5.7. Разворачивание операции скалярного произведения в итоговую формулу


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

Результат работы шейдера показан на рис. 5.8. Полный код шейдера находится на CD-ROM в файле shader_3.rfx.


Рис. 5.8. Шейдер для черно-белой визуализации в действии

Рис. 5.8. Шейдер для черно-белой визуализации в действии


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

ПРИМЕЧАНИЕ
Поскольку все рисунки в книге черно-белые и не всегда позволяют увидеть эффект во всей его красе, я записал на CD-ROM цветные иллюстрации в высоком разрешении. Чтобы узнать, как найти эти иллюстрации на компакт-диске, обратитесь к приложению С.

Обобщение — это хорошо!

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

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


Рис. 5.9. Декомпозиция умножения матриц на набор скалярных произведений

Рис. 5.9. Декомпозиция умножения матриц на набор скалярных произведений


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


Рис. 5.10. Преобразование в оттенки серого, представленное в виде матрицы

Рис. 5.10. Преобразование в оттенки серого, представленное в виде матрицы


Пришло время, держа это в памяти, обобщить ваш шейдер для манипуляций с цветами, воспользовавшись преимуществами, даваемыми матрицей преобразования цветов. Первый шаг — добавление к вашему узлу группы эффектов новой матричной переменной, путем щелчка правой кнопкой мыши по узлу и выбора из меню команды Add Variable. Выберите тип переменной MATRIX, а затем размер 4 × 4, назовите ее color_filter, и установите свойства, заполнив значениями, необходимыми для шейдера черно-белой визуализации, показанными на рис. 5.10.

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

float4x4 color_filter;
sampler Texture0;

float4 ps_main(float2 texCoord: TEXCOORD0) : COLOR
{
    // Читаем исходный цвет
    float4 col = tex2D(Texture0, texCoord);

    // Применяем матрицу к исходному цвету
    return mul(color_filter,col);
}

Полный шейдер находится на CD-ROM в файле shader_4.rfx.

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

Базируясь на моем описании, вы можете придти к заключению, что итоговый цвет должен определяться как-то так (помните, что коэффициенты в приведенных ниже формулах получены экспериментальным путем):

Color.r = 0.1495*RT.r + 0.2935*RT.g + 0.057*RT.b + 0.5;
Color.g = 0.1495*RT.r + 0.2935*RT.g + 0.057*RT.b + 0.25;
Color.b = 0.1495*RT.r + 0.2935*RT.g + 0.057*RT.b;

После того, как матрица определена, просто отредактируйте значения, дважды щелкнув по матрице цветового преобразования и введя соответствующие числа. После того, как значения заданы, вы увидите результат в окне предварительного просмотра (рис. 5.11). Полный код шейдера находится на CD-ROM в файле shader_5.rfx.


Рис. 5.11. Результат визуализации шейдера тепловидения

Рис. 5.11. Результат визуализации шейдера тепловидения


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

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


Рис. 5.12. Матрицы цветовых преобразований, используемые для стандартных операций

Рис. 5.12. Матрицы цветовых преобразований, используемые для стандартных операций


Вещи не всегда линейны

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

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

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

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

Наш предыдущий пиксельный шейдер, измененный для использования текстуры преобразования, выглядел бы так:

float4x4 color_filter;
sampler Texture0;

float4 ps_main(float2 texCoord: TEXCOORD0) : COLOR
{
    // Читаем исходный цвет
    float4 col = tex2D(Texture0, texCoord);
    float Intensity;

    // Вычисляем интенсивность или температуру пикселя
    // Другими словами, вычисляем значение оттенка серого
    // для пикселя.
    Intensity = dot(col,float4(0.299,0.587,0.184,0));

    // Используем интенсивность для выборки
    // из таблицы преобразования цвета
    return tex1D(Texture_Heat,Intensity);
}

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


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz