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

Размываем изображения объектов

Первый вопрос, который вы можете задать — зачем мне размывать изображение цели визуализации? Вы задали правильный вопрос; размытие изображения цели визуализации применяется достаточно редко. Однако оно служит в качестве хорошего введения в манипуляции с пикселями и фильтры сверток (convolution filter). Я также буду использовать технику размытия изображения в следующей главе при знакомстве с эффектом глубины резкости.

Существует много типов фильтров, или ядер фильтров, которые можно применять для размытия текстур. Мы начнем с того, который известен как четырехточечная свертка или фильтр коробки (box filter). В этом фильтре, показанном на рис. 5.13, пиксель вычисляется как среднее значение его четырех соседних пикселей: верхнего, нижнего, левого и правого. В шейдере это может быть легко выполнено путем четырех выборок из цели визуализации и вычисления для них среднего значения.


Рис. 5.13. Иллюстрация к реализации фильтра коробки

Рис. 5.13. Иллюстрация к реализации фильтра коробки


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

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

const float4 samples[4] = {
    -1.0,  0.0, 0, 0.25,
     1.0,  0.0, 0, 0.25,
     0.0,  1.0, 0, 0.25,
     0.0, -1.0, 0, 0.25
};

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

// Выборка и вычисление среднего цвета коробки
for(int i = 0; i < 4; i++)
    col += samples[i].w * tex2D(Texture0,texCoord +
            float2(samples[i].x * viewport_inv_width,
                   samples[i].y * viewport_inv_height));

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

float fInverseViewportWidth;
float fInverseViewportHeight;
sampler Texture0;
const float4 samples[4] = {
    -1.0,  0.0, 0, 0.25,
     1.0,  0.0, 0, 0.25,
     0.0,  1.0, 0, 0.25,
     0.0, -1.0, 0, 0.25
};

float4 ps_main(float2 texCoord: TEXCOORD0) : COLOR
{
    float4 col = float4(0,0,0,0);

    // Выборка и вычисление среднего цвета
    for(int i = 0; i < 4; i++)
        col += samples[i].w * tex2D(Texture0,texCoord +
                  float2(samples[i].x * fInverseViewportWidth,
                         samples[i].y * fInverseViewportHeight));

    return col;
}

Когда код будет скомпилирован, в окне предварительного просмотра вы должны увидеть сцену с размытием изображения, показанную на рис. 5.14. Можно заметить, что размытие не настолько сильное, как ожидалось. Это объясняется тем, что фильтр коробки при выполнении операции просматривает только ближайших соседей пикселя. Полную версию рассматриваемого шейдера вы найдете на CD-ROM в файле shader_6.rfx.


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

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


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

Улучшаем фильтр

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

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

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


Рис. 5.15. Ядра фильтров для выявления ребер и увеличения резкости

Рис. 5.15. Ядра фильтров для выявления ребер и увеличения резкости


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

const float4 samples[6] = {
    -1.0,  1.0, 0,  1.0,
     0.0,  1.0, 0,  2.0,
     1.0,  1.0, 0,  1.0,
    -1.0, -1.0, 0, -1.0,
     0.0, -1.0, 0, -2.0,
     1.0, -1.0, 0, -1.0
};

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

const float4 samples[5] = {
     0.0,  0.0, 0, 11.0/3.0,
     0.0,  1.0, 0, -2.0/3.0,
     0.0, -1.0, 0, -2.0/3.0,
    -1.0,  0.0, 0, -2.0/3.0,
     1.0,  0.0, 0, -2.0/3.0
};

На рис. 5.16 показан результат визуализации для обоих шейдеров. Как видите, разработка фильтров свертки с использованием преимуществ новых инструкций, предоставляемых моделью шейдеров 2.0, позволяет сделать код вашего шейдера гибким и допускающим многократное использование. Полные версии обоих фильтров вы найдете на CD-ROM в файлах shader_6a.rfx и shader_6b.rfx.


Рис. 5.16. Результат визуализации для шейдеров выявления ребер и увеличения резкости

Рис. 5.16. Результат визуализации для шейдеров выявления ребер и увеличения резкости



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

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