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.8. Полный код шейдера находится на CD-ROM в файле shader_3.rfx.
Рис. 5.8. Шейдер для черно-белой визуализации в действии
В первом упражнении из раздела «Ваш ход!», находящегося в конце главы, вам будет предложено изменить шейдер для черно-белой визуализации, чтобы реализовать эффект, называемый сепия.
Всегда хорошо, если мы можем взять отдельный эффект и обобщить его таким образом, чтобы использовать различными способами. Что если нам взять на вооружение рассмотренный в предыдущем разделе подход к манипуляциям с цветами и обобщить его таким образом, чтобы один и тот же шейдер мог выполнять различные базовые манипуляции с цветами? В данном разделе я вернусь к шейдеру черно-белой визуализации и покажу вам как модифицировать его и выполнять произвольные манипуляции с цветами через простой шейдер с возможностью многократного использования.
Ранее я показал, как преобразование в оттенки серого может быть выполнено с помощью скалярного произведения. Я также упомянул, что скалярное произведение является частью операции умножения матриц. Фактически, если вы умножаете матрицу на вектор, то операция может быть разделена на отдельные операции скалярного произведения, применяемые к каждой строке матрицы. Такая декомпозиция в деталях показана на рис. 5.9.
Рис. 5.9. Декомпозиция умножения матриц на набор скалярных произведений
Для рассмотренного ранее примера шейдера черно-белой визуализации, можно получить те же самые значения красной, зеленой и синей компонент, воспользовавшись представлением операции в виде простой матрицы, показанной на рис. 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.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 | < Назад | Оглавление | Далее > |