Array ( )
Вход:




Главная | OpenGL | GLSL | AI | Сеть | Примеры | Библиотека

GLSL: Практика: Toon-шейдер - Версия II






GLSL имеет доступ к части функционала OpenGL. В этом уроке вы увидите, как прочитать
цвет, установленный OpenGL с помощью glColor..().

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

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

toon.vert
    varying vec3 normal;
   
    void main()
    {
        normal = gl_Normal;
   
        gl_Position = ftransform();
    }


Теперь в пикселном шейдере нужно обьявить переменную lightDir, так как интенсивность основывается
на этой переменной. Также нужно обьявить новую varying-переменную для хранения интерполированных
нормалей. Код для пикселного шейдера:


uniform vec3 lightDir;

varying vec3 normal;

void main()
{

float intensity;
vec4 color;

intensity = dot(lightDir,normal);

if (intensity > 0.95)
color = vec4(1.0,0.5,0.5,1.0);
else if (intensity > 0.5)
color = vec4(0.6,0.3,0.3,1.0);
else if (intensity > 0.25)
color = vec4(0.4,0.2,0.2,1.0);
else
color = vec4(0.2,0.1,0.1,1.0);

gl_FragColor = color;
}



И в результате:


Нет, это не баг! Такой же результат, как и в предыдущем разделе. Так в чем же дело?

Давайте взглянем повнимательнее на эти две версии. В первой версии мы вычислили интенсивность
в вершинном шейдере и использовали интерполированное значение в пикселном. Во второй версии
мы интерполируем нормаль в вершинном шейдере для пикселного шейдера, в котором уже
вычисляем dot product. Интерполяция и dot product - линейные операции, так что не важно, какая
из них выполняется раньше.

Неверно здесь использование интерполированной нормали для формулы dot() в пикселном шейдере!
И это неправильно, потому что нормаль, даже если имеет верное направление, скорее всего не
имеет длинны.

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

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

Файл toon.frag
    uniform vec3 lightDir;
   
    varying vec3 normal;
   
    void main()
    {
   
        float intensity;
        vec4 color;

       
        intensity = dot(lightDir,normalize(normal));
       
        if (intensity > 0.95)
            color = vec4(1.0,0.5,0.5,1.0);
        else if (intensity > 0.5)
            color = vec4(0.6,0.3,0.3,1.0);
        else if (intensity > 0.25)
            color = vec4(0.4,0.2,0.2,1.0);
        else
            color = vec4(0.2,0.1,0.1,1.0);
       
        gl_FragColor = color;
    }


Результирующее изображение показано ниже. Оно выглядит уже лучше, но всё ещё не идеально.
К тому же на нём присутствует алиасинг, но его устранение выходит за рамки этого урока ;)











Комментарии:

Войдите, чтобы оставить комментарий:












Яндекс.Метрика
 Яндекс цитирования.