Визуальные эффекты на GPU Стандартные модели освещения - Гуро 1. Падающий равномерно рассеивается по всем направлением верхней полусферы 2. Энергия, уходящая в заданном направлении пропорциональна только плотности световой энергии, падающей в данную точку l P Визуальные эффекты на GPU Стандартные модели освещения - диффузная n v l n - вектор нормали в точке Р v - вектор на наблюдателя l - вектор на источник света P Уравнение освещенности: Cres Csurf k a k d I light max 0, n, l Визуальные эффекты на GPU Стандартные модели - освещение по Блинну n h v h - бисектор векторов l и v l h l v l v P Уравнение освещенности: Cres Csurf ka kd I light max 0, n, l ks I light max 0, n, h p Визуальные эффекты на GPU Стандартные модели - освещение по Блинну C surf - цвет поверхности I light - цвет источника света ka - вклад фонового освещения k d - вклад диффузной освещенности k s - вклад бликовой освещенности p - гладкость поверхности Визуальные эффекты на GPU Стандартные модели - освещение по Блинну struct VertexData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float3 n : NORMAL; }; struct FragmentData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float3 n; float3 l; float3 h; float3 v; }; Вход вершинной программы Вход фрагментной программы Визуальные эффекты на GPU Освещение по Блинну. Вершинная программа FragmentData main ( VertexData IN, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, uniform float4x4 mv, uniform float4x4 mvi ) { FragmentData OUT; float3 p = mul ( mv, OUT.n OUT.l OUT.v OUT.h OUT.pos OUT.texCoord return OUT; } = = = = = = IN.pos ).xyz; mul ( mvi, float4 ( IN.n, 0 ) ).xyz; normalize ( lightPos.xyz - p ); normalize ( eyePos.xyz - p ); normalize ( OUT.l + OUT.v ); mul ( mvp, IN.pos ); IN.texCoord; Визуальные эффекты на GPU Освещение по Блинну. Фрагментная программа float4 main ( FragmentData IN ) : COLOR { const float4 diffColor = float4 ( 0.5, 0.0, 0.0, 1.0 ); const float4 specColor = float4 ( 0.7, 0.7, 0.0, 1.0 ); const float specPower = 30.0; float3 float3 float3 float4 float4 n2 l2 h2 diff spec = = = = = normalize normalize normalize diffColor specColor return diff + spec; } ( ( ( * * IN.n ); IN.l ); IN.h ); max ( dot ( n2, l2 ), 0.0 ); pow ( max ( dot ( n2, h2 ), 0.0 ), specPower ); Визуальные эффекты на GPU Стандартные модели - освещение по Фонгу n r - отражение вектора v относительно нормали n r v l P Уравнение освещенности: Cres Csurf ka kd I light max 0, n, l k s I light max 0, l , r p Визуальные эффекты на GPU Стандартные модели - освещение по Фонгу Визуальные эффекты на GPU Освещение по Фонгу. Вершинная программа FragmentData main ( VertexData IN, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, uniform float4x4 mv, uniform float4x4 mvi ) { FragmentData OUT; float3 p = mul ( mv, OUT.n OUT.l OUT.v OUT.pos OUT.texCoord return OUT; } = = = = = IN.pos ).xyz; mul ( mvi, float4 ( IN.n, 0 ) ).xyz; normalize ( lightPos.xyz - p ); normalize ( eyePos.xyz - p ); mul ( mvp, IN.pos ); IN.texCoord; Визуальные эффекты на GPU Освещение по Фонгу. Фрагментная программа float4 main ( FragmentData { const float4 diffColor = const float4 specColor = const float specPower = float3 float3 float3 float3 float4 float4 n2 l2 v2 r diff spec = = = = = = float4 ( 0.5, 0.0, 0.0, 1.0 ); float4 ( 0.7, 0.7, 0.0, 1.0 ); 30.0; normalize normalize normalize reflect ( diffColor specColor return diff + spec; } IN ) : COLOR ( IN.n ); ( IN.l ); ( IN.v ); -v2, n2 ); * max ( dot ( n2, l2 ), 0.0 ); * pow ( max ( dot ( l2, r ), 0.0 ), specPower ); Визуальные эффекты на GPU Bumpmapping L При использовании описанных моделей освещения поверхности получаются слишком гладкими Хочется добавить микрорельеф, не изменяя при этом саму геометрию объектов J Попробуем просто в каждой точке отклонять вектор нормали. Тогда изменится освещенность в точке и создастся иллюзия микрорельефа Визуальные эффекты на GPU Bumpmapping n n n' n' n' n n - истинный вектор нормали n’ - искаженный вектор нормали Визуальные эффекты на GPU Bumpmapping Результат искажения нормалей - при рендеринге обычного тора было произведено искажение векторов нормали Визуальные эффекты на GPU Bumpmapping ? - в каком пространстве n задавать искажения вектора нормали n b ! - будем задавать в искажения вектора (точнее, сам вектор нормали n) в пространстве “приклеенном” к выводимой грани t Визуальные эффекты на GPU Bumpmapping Для каждой грани определим следующие попарно ортогональных единичных вектора n b t - касательный вектор к грани n - нормаль грани b - бинормаль (b = [n,t]) Эта тройка векторов образует ортонормированный базис в пространстве и, соответственно, систему координат, называемую касательной (tangent space) t Визуальные эффекты на GPU Bumpmapping Как кодировать искажения нормали - переведем единичный вектор нормали (в касательном пространстве) в цвет и поместим в текстуру Неискаженная нормаль в касательном пространстве всегда равна n=(0,01) r 0.5 (nx 1), g 0.5 (n y 1), b 0.5 (nz 1) Визуальные эффекты на GPU Bumpmapping Вершинная программа: Фрагментная программа: • вычислить вектора l, v, h (или r) • перевести их в касательное пространство • передать фрагментной программе вычисленные вектора и текстурные координаты • нормировать вектора l, v, h (или r) • взять из карты нормалей нормаль • перевести нормаль из RGBпредставления • вычислить освещенность с использованием полученного вектора нормали и векторов l, v, h (или r) Визуальные эффекты на GPU Bumpmapping - передаваемые данные struct VertexData { float4 pos float2 texCoord float3 n float3 t float3 b }; : : : : : POSITION; TEXCOORD0; NORMAL; TEXCOORD1; TEXCOORD2; struct FragmentData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float3 lt; float3 ht; }; Визуальные эффекты на GPU Bumpmapping - вершинная программа FragmentData main ( VertexData IN, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, // modelview*projection uniform float4x4 mv, // modelview uniform float4x4 mvi ) { FragmentData OUT; float3 p = mul ( mv, IN.pos ).xyz; float3 l = normalize ( lightPos.xyz - p ); float3 v = normalize ( eyePos.xyz - p ); float3 h = l + v; float3 n = mul ( mvi, float4 ( IN.n, 0 ) ).xyz; float3 t = mul ( mvi, float4 ( IN.t, 0 ) ).xyz; float3 b = mul ( mvi, float4 ( IN.b, 0 ) ).xyz; OUT.pos OUT.texCoord OUT.lt OUT.ht return OUT; } = = = = mul ( mvp, IN.pos ); IN.texCoord; float3 ( dot ( l, t ), dot ( l, b ), dot ( l, n ) ); float3 ( dot ( h, t ), dot ( h, b ), dot ( h, n ) ); Визуальные эффекты на GPU Bumpmapping - фрагментная программа float4 main ( FragmentData IN, uniform sampler2D bumpMap, uniform sampler2D diffuseMap ) : COLOR { const float4 specColor = float4 ( 1, 1, 1, 1 ); float3 float3 float3 float3 float float n nt l2 h2 diff spec = = = = = = tex2D ( bumpMap, IN.texCoord ).xyz; normalize ( 2.0*n - 1.0 ); normalize ( IN.lt ); normalize ( IN.ht ); max ( dot ( nt, l2 ), 0.0 ) + 0.2; pow ( max ( dot ( nt, h2 ), 0.0 ), 30.0 ); return diff * tex2D ( diffuseMap, IN.texCoord ) + spec * specColor; } Визуальные эффекты на GPU Анизотропные модели освещения n l v Поворот вокруг вектора нормали не изменяет векторов l и v, поэтому ранее рассмотренные модели освещения не зависят от подобных поворотов Однако встречаются поверхности, для это свойство не выполняется (поверхность CD, полированный металл). Такие поверхности называются анизотропными Визуальные эффекты на GPU Анизотропные модели освещения n t n n t t Проще всего моделировать такие поверхности, считая их состоящими из множества ориентированных бесконечно-тонких нитей. Тогда в каждой точке поверхности можно задать касательный t вектор к такой нити, т.е. на поверхности объекта возникает поле касательных векторов t (P) Визуальные эффекты на GPU Анизотропные модели освещения t lt ln ? - как освещать подобную нить, ведь для нее неоднозначно определен вектор нормали n l ! - рассмотрим диффузную и бликовую n составляющие раздельно и для каждой из них выберем такой вектор n (ортогональный вектору t), максимизирующий соответствующую компоненту освещенности Визуальные эффекты на GPU Анизотропные модели освещения Разложим вектор l на ортогональные части: l t n* , n*t Умножим скалярно на t и найдем параметры разложения l , t , n* l l , t t n * 2 n , n 1 l , t * * 2 Легко убедиться, что максимум (l,n) достигается на следующем векторе: n* n* * 2 n 1 l , t Визуальные эффекты на GPU Анизотропные модели освещения Таким образом получаем следующее уравнение освещенности для анизотропной поверхности: Cres Csurf k a k d I light 1 l , t k s I light 1 h, t 0.5 p 2 Визуальные эффекты на GPU Анизотропные модели освещения Передаваемые данные struct VertexData { float4 pos float2 texCoord float3 n float3 t float3 b }; : : : : : POSITION; TEXCOORD0; NORMAL; TEXCOORD1; TEXCOORD2; struct FragmentData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float3 lt; float3 ht; }; Визуальные эффекты на GPU Анизотропные модели освещения. Вершинная программа FragmentData main ( VertexData IN, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, uniform float4x4 mv, uniform float4x4 mvi ) { FragmentData OUT; float3 p = mul ( mv, IN.pos ).xyz; float3 l = normalize ( lightPos.xyz - p ); float3 v = normalize ( eyePos.xyz - p ); float3 h = normalize ( l + v ); float3 n = mul ( mvi, float4 ( IN.n, 0 ) ).xyz; float3 t = mul ( mvi, float4 ( IN.t, 0 ) ).xzy; float3 b = mul ( mvi, float4 ( IN.b, 0 ) ).xyz; OUT.lt OUT.ht OUT.pos OUT.texCoord return OUT; } = = = = float3 ( dot ( l, t ), dot ( l, b ), dot ( l, n ) ); float3 ( dot ( h, t ), dot ( h, b ), dot ( h, n ) ); mul ( mvp, IN.pos ); IN.texCoord; Визуальные эффекты на GPU Анизотропные модели освещения Фрагментная программа float4 main ( FragmentData IN, uniform sampler2D tangentMap, uniform sampler2D decalMap, uniform sampler2D anisoMap ) : COLOR { const float3 specColor = float3 ( 0, 0, 1 ); float3 float float float2 float2 float3 tang dot1 dot2 arg ds color = = = = = = normalize dot dot float2 tex2D tex2D ( ( ( ( ( ( 2*tex2D( tangentMap, IN.texCoord ).xyz-1); normalize ( IN.lt ), tang ); normalize ( IN.ht ), tang ); dot1, dot2 ); anisoMap, arg*arg ).xy; decalMap, IN.texCoord ).xyz; return float4 ( color * ds.x + specColor * ds.y, 1.0 ); } Визуальные эффекты на GPU Модель Уорда (Ward) Простейший вид модели освещения Уорда: Cres h, t Csurf ka k s I light exp k h, n k - гладкость поверхности t - касательный вектор h - бисектор векторов l и v Визуальные эффекты на GPU Модель Уорда (Ward) Фрагментная программа float4 main ( FragmentData IN, uniform sampler2D uniform sampler2D { const float4 specColor const float3 n const float roughness = 5.0; float4 float3 float float float color tang dot1 dot2 p = = = = = tangentMap, decalMap ) : COLOR = float4 ( 0, 0, 1, 0 ); = float3 ( 0, 0, 1 ); tex2D ( decalMap, IN.texCoord ); normalize ( 2*tex2D ( tangentMap, IN.texCoord ).xyz-1 ); dot ( IN.ht, tang ) * roughness; dot ( IN.ht, n ); dot1 / dot2; return float4 ( color.rgb + specColor.rgb * exp ( -p*p ), 1 ); } Визуальные эффекты на GPU Моделирование преломления n r v t Визуальные эффекты на GPU Моделирование преломления ! - используем встроенную функцию refract для нахождения преломленного вектора и по этому вектору возьмем значение из кубической карты отражения. const float float3 float3 float3 float3 en nn r t = = = = eta = 0.9; // Find reflection/refraction vectors normalize ( IN.e ); normalize ( IN.n ); reflect ( en, nn ); refract ( en, nn, eta ); // Do a lookup into the environment map float3 envColor = texCUBE ( envMap, r ).xyz; float3 refractionColor = texCUBE ( envMap, t ).xyz; Визуальные эффекты на GPU Моделирование преломления - учет длины волны Визуальные эффекты на GPU Моделирование преломления - учет длины волны ! - функция refract зависит также и от коэффициента преломления eta. Данный коэффициент зависит от длины волны. Поэтому возьмем три коэффициента преломления (по одному для каждой цветовой компоненты), найдем преломленный вектор и соответствующий цвет. Из каждого из трех полученных цветов возьмем по одной цветовой компоненте (соответствующей длине волны) и построим из них новый цвет. n r v tred t green tblue Визуальные эффекты на GPU Свечение (glow) Визуальные эффекты на GPU Свечение (glow) Нарисовать светящийся объект в текстуру Применить «размытие» к этой текстуре Нарисовать всю сцену Наложить на изображение сцены размытую текстуру Визуальные эффекты на GPU Дифракция Визуальные эффекты на GPU Дифракция n n v v l l C A d B Разность фаз можно приближенно записать как du, где u= sin 1 - sin 2. Если эта разница кратна длине волны, то происходит усиление света, в противном случае - ослабевание. Визуальные эффекты на GPU Дифракция. Вершинная программа. FragmentData main ( VertexData IN, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, uniform float4x4 mv, uniform float4x4 mvi ) { FragmentData OUT; float3 p = mul ( mv, IN.pos ); float3 l = normalize ( lightPos.xyz - p ); float3 v = normalize ( eyePos.xyz - p ); float3 h = normalize ( l + v ); float3 n = mul ( mvi, float4 ( IN.n, 0 ) ).xyz; float3 t = mul ( mvi, float4 ( IN.t, 0 ) ).xzy; float3 b = mul ( mvi, float4 ( IN.b, 0 ) ).xyz; OUT.lt OUT.ht OUT.vt OUT.pos OUT.texCoord return OUT; } = = = = = float3 ( dot ( l, float3 ( dot ( h, float3 ( dot ( v, mul ( mvp, IN.pos IN.texCoord; t ), dot ( l, b ), dot ( l, n ) ); t ), dot ( h, b ), dot ( h, n ) ); t ), dot ( v, b ), dot ( v, n ) ); ); Визуальные эффекты на GPU Дифракция. Фрагментная программа. float4 main ( FragmentData IN, uniform sampler2D tangentMap, uniform sampler2D decalMap, uniform sampler1D rainbowMap ) : COLOR { const float3 specColor = float3 ( 0, 0, 1 ); const float3 n = float3 ( 0, 0, 1 ); const float roughness = 30.0; const float d = 40.0; float3 tang = normalize ( 2*tex2D(tangentMap, IN.texCoord).xyz-1); float u = dot ( IN.ht, tang ); float w = dot ( IN.ht, n ); float e = roughness * u / w; float3 color = float3 ( 0, 0, 0 ); float u1 = d * abs ( u ); for ( int n = 1; n <= 8; n++ ) color += tex1D ( rainbowMap, u1 / float ( n ) ).rgb; return float4 ( specColor * exp ( -e*e ) + color * 0.25, alpha ); } Визуальные эффекты на GPU Рендеринг меха Визуальные эффекты на GPU Рендеринг меха Текстура Слои 1, 2, 3 и т.д. Визуальные эффекты на GPU Рендеринг меха Визуальные эффекты на GPU Рендеринг меха Модель освещения - анизотропная (для волосков) Вершинный шейдер - сдвинуть вершины для слоя и подготовить все величины для расчета освещенности Фрагментный шейдер - непосредственно расчет освещенности n pos+n*3*d pos+n*2*d pos+n*d pos Вершинная программа также обеспечивает перенос вершин для очередного слоя и анимацию меха путем изменения текстурных координат в зависимости от времени Визуальные эффекты на GPU Рендеринг меха. Вершинная программа FragmentData main ( VertexData IN, uniform float tl, uniform float time, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, uniform float4x4 mv, uniform float4x4 mvi ) { FragmentData OUT; float4 pp = IN.pos + tl * float4 ( IN.n, 0 ); float3 p = mul ( mv, pp ).xyz; float3 l = normalize ( lightPos.xyz - p ); float3 v = normalize ( eyePos.xyz - p ); float3 h = l + v; float3 n = mul ( mvi, float4 ( IN.n, 0 ) ).xyz; float3 t = mul ( mvi, float4 ( IN.t, 0 ) ).xyz; float3 b = mul ( mvi, float4 ( IN.b, 0 ) ).xyz; OUT.pos = mul ( mvp, pp ); OUT.texCoord = IN.texCoord + float2 ( 0, 0.02 * sin ( time ) * tl ); OUT.color = IN.color; OUT.lt = float3 ( dot ( l, t ), dot ( l, b ), dot ( l, n ) ); OUT.ht = float3 ( dot ( h, t ), dot ( h, b ), dot ( h, n ) ); return OUT; } Визуальные эффекты на GPU Рендеринг меха. Фрагментная программа float4 main ( FragmentData IN, uniform sampler2D furMap, uniform sampler2D anisoMap ) : COLOR { const float3 float3 float float float2 float2 float4 tang dot1 dot2 arg ds color specColor = float3 ( 1, 1, 1 ); = = = = = = normalize dot dot float2 tex2D tex2D ( ( ( ( ( ( IN.furDir ); normalize ( IN.lt ), tang ); normalize ( IN.ht ), tang ); dot1, dot2 ); anisoMap, arg*arg ).xy; furMap, IN.texCoord * float2 ( 6, 12 ) ); return IN.color * float4 ( color.xyz + specColor * ds.y*0.5, color.w*color.w ); } Визуальные эффекты на GPU Рендеринг меха. Главная программа glEnable glBlendFunc // draw shells from innermost to outermost ( GL_BLEND ); ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // draw every layer for ( int i = 0; i < numLayers; i++ ) { float t = (float) i / MAX_LAYERS; float shadow = 0.7*(shadowMin * (1 - t) + shadowMax * t); glColor4f ( shadow, shadow, shadow, shadow ); vertexProgram.setParameter ( "tl", t * scale ); object -> render (); } // restore state glDisable ( GL_BLEND ); Визуальные эффекты на GPU Сгорание как в DooM III Визуальные эффекты на GPU Сгорание как в DooM III. Вершинная программа FragmentData main ( VertexData IN, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, uniform float4x4 mv, uniform float4x4 mvi ) { FragmentData OUT; OUT.p OUT.n OUT.l OUT.v OUT.h OUT.pos OUT.texCoord return OUT; } = = = = = = = mul ( mv, IN.pos ).xyz; mul ( mvi, float4 ( IN.n, 0 ) ).xyz; normalize ( lightPos.xyz - OUT.p ); normalize ( eyePos.xyz - OUT.p ); normalize ( OUT.l + OUT.v ); mul ( mvp, IN.pos ); IN.texCoord; Визуальные эффекты на GPU Сгорание как в DooM III. Фрагментная программа float4 main ( FragmentData IN, uniform float time, uniform sampler3D noiseMap ) : COLOR { float3 float float3 float3 float3 float4 float4 float4 noise t n2 l2 h2 diff spec color = = = = = = = = turbulence ( noiseMap, 0.1 * IN.p ); -0.3 - noise.x + fmod ( time, 15 ) / 15; normalize ( IN.n ); normalize ( IN.l ); normalize ( IN.h ); diffColor * max ( dot ( n2, l2 ), 0.5 ); specColor * pow ( max ( dot ( n2, h2 ), 0 ), 30 ); diff + spec; if ( t > 0 ) if ( t > 0.02 ) color = float4 ( 0, 0, 0, 0 ); else color = lerp ( darkRed, brightRed, t * 50 ); return color; }