Главная Статьи Ссылки Скачать Скриншоты Юмор Почитать Tools Проекты Обо мне Гостевая |
Одним из простейших шейдеров, применяющихся в нефотореалистическом рендеринге (NPR), является т.н. cartoon-шейдер. Этот шейдер часто используют для получения изображений, выглядящих как персонажи из мультипликационных фильмов.
Обычно cartoon-шейдер использует диффузную освещенность объекта для получения итогового цвета. Простейший пример такого шейдера приводится ниже.
varying vec3 l; varying vec3 h; varying vec3 v; varying vec3 n; void main (void) { const vec4 diffColor = vec4 ( 0.5, 0.0, 0.0, 1.0 ); vec3 n2 = normalize ( n ); vec3 l2 = normalize ( l ); float diff = 0.2 + max ( dot ( n2, l2 ), 0.0 ); vec4 clr; if ( diff < 0.4 ) clr = diffColor * 0.3; else if ( diff < 0.7 ) clr = diffColor ; else clr = diffColor * 1.3; gl_FragColor = clr; }
На следующем рисунке приводится изображение полученное при помощи этого шейдера.
Рис 1. Изображение, получаемое при помощи классического cartoon-шейдера
Иногда вместо явного задания нескольких уровней яркости (которым соответствуют определенные цвета) используется одномерная текстура. В этом случае найденное значение диффузной освещенности используется для индексирования в эту текстуру и получения из нее результирующего цвета.
Как легко заметить подобный подход не всегда дает желаемое изображение - довольно часто он оказывается слишком простым и скрывает ряд деталей, которые хочется показать.
Так одним из его недостатков является то, что получаемые при его использовании цвета, не зависят от положения наблюдателя (т.к. диффузное освещенность определяется только углом между нормалью к поверхности и направлением на источник света).
В статье "X-Toon: An Extended Toon Shader" авторами был предложен интересный способ, позволяющий расширить возможности традиционного cartoon-шейдера.
С этой целью вводится дополнительный параметр, влияющий на выбор цвета, - т.н. уровень абстракции (LOA, Level Of Abstraction).
При этом для задания способа закрашивания объекта используется уже двухмерная текстура, для индексирования в которую используется диффузная освещенность и вводимый уровень абстракции.
На следующем рисунке приведены примеры подобных текстур.
Рис 2. Двухмерные текстуры, используемые при закрашивании объектов.
В качестве уровня абстракции могут выступать самые различные параметры, но мы рассмотрим только несколько простейших случаев.
Приведенная глубина объекта (доступная в фрагментном шейдера как gl_FragCoord.z) может выступать в качестве уровня абстракции. Возможно использование не самой глубины, а некоторой ее функции (обычно - линейной), в качестве уровня абстракции.
Такой выбор уровня абстракции позволяет по-разному раскрашивать близко расположенные и удаленные объекты.
Например, можно взять обычную одномерную текстуру, соответствующую классическому cartoon-шейдеру и "вытянуть" ее во второе измерение (превратив в двухмерную текстуру). После этого к это текстуре применяется операция размытия (blur) так, чтобы удаленным объектам соответствовала большая степень размытия, нежели близким.
Ниже приводится пример фрагментного шейдера, использующего глубину фрагмента в качестве уровня абстракции, и получающиеся при этом изображения.
varying vec3 l; varying vec3 h; varying vec3 v; varying vec3 n; uniform sampler2D toonMap; void main (void) { vec3 n2 = normalize ( n ); vec3 l2 = normalize ( l ); float diff = 0.2 + max ( dot ( n2, l2 ), 0.0 ); gl_FragColor = texture2D ( toonMap, vec2 ( diff, gl_FragCoord.z ) ); }
Рис 3. Изображения, получающиеся при использовании LOA, зависящего от глубины.
В качестве LOA можно также использовать и бликовую освещенность фрагмента (вычисляемую по модели Фонга или Блинна).
Это позволяет получать отчетливые блики на поверхности объекта, сохраняя при этом общий "мультяшный" вид.
Ниже приводится соответствующий фрагментный шейдер и получающиеся при таком подходе изображения.
varying vec3 l; varying vec3 h; varying vec3 v; varying vec3 n; uniform sampler2D toonMap; void main (void) { const float specPower = 10.0; vec3 n2 = normalize ( n ); vec3 l2 = normalize ( l ); vec3 h2 = normalize ( h ); float diff = 0.2 + max ( dot ( n2, l2 ), 0.0 ); float spec = pow ( max ( dot ( n2, h2 ), 0.0 ), specPower ); gl_FragColor = texture2D ( toonMap, vec2 ( diff, spec ) ); }
Рис 4. Изображения, получающиеся при использовании LOA, зависящего от бликовой освещенности.
Еще одной величиной, которая может с успехом применяться в качестве уровня абстракции, является степень близости обрабатываемого фрагмента к силуэту (контурной линии) на поверхности выводимого объекта.
В качестве такого параметра обычно используется следующая величина:
r = pow ( 1.0 - abs ( dot ( n, v ) ), p )
За счет аккуратного подбора текстуры можно получать различные эффекты связанные с силуэтом, такие как подсветка сзади или "размывание" краев.
Соответствующий шейдер приводится ниже.
varying vec3 l; varying vec3 h; varying vec3 v; varying vec3 n; uniform sampler2D toonMap; void main (void) { const float edgePower = 3.0; vec3 n2 = normalize ( n ); vec3 l2 = normalize ( l ); vec3 h2 = normalize ( h ); vec3 v2 = normalize ( v ); float diff = 0.1 + max ( dot ( n2, l2 ), 0.0 ); float edge = pow ( abs ( dot ( n2, v2 ) ), edgePower ); gl_FragColor = texture2D ( toonMap, vec2 ( diff, edge ) ); }
На следующих рисунках приводятся получаемые при этом изображения.
Рис. 5. Изображение, получающееся при использовании LOA, зависящего от близости к силуэту.
По этой ссылке можно скачать весь исходный код к этой статье. Также доступны для скачивания откомпилированные версии для M$ Windows, Linux и Mac OS X.