Визуальные эффекты на GPU Стандартные модели освещения - Гуро 1. Падающий равномерно рассеивается по всем направлением верхней полусферы 2. Энергия, уходящая в заданном направлении пропорциональна только плотности световой энергии, падающей в данную точку
Визуальные эффекты на GPU Стандартные модели освещения - диффузная n - вектор нормали в точке Р v - вектор на наблюдателя l - вектор на источник света Уравнение освещенности:
Визуальные эффекты на GPU Стандартные модели - освещение по Блинну Уравнение освещенности: h - бисектор векторов l и v
Визуальные эффекты на GPU Стандартные модели - освещение по Блинну - цвет поверхности - цвет источника света - вклад фонового освещения - вклад диффузной освещенности - вклад бликовой освещенности - гладкость поверхности
Визуальные эффекты на GPU Стандартные модели - освещение по Блинну structVertexData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float3 n : NORMAL; }; structFragmentData { 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; float3p = mul ( mv, IN.pos ).xyz; OUT.n = mul ( mvi, float4 ( IN.n, 0 ) ).xyz; OUT.l = normalize ( lightPos.xyz - p ); OUT.v = normalize ( eyePos.xyz - p ); OUT.h = normalize ( OUT.l + OUT.v ); OUT.pos = mul ( mvp, IN.pos ); OUT.texCoord = IN.texCoord; return OUT; }
Визуальные эффекты на 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 n2 = normalize ( IN.n ); float3 l2 = normalize ( IN.l ); float3 h2 = normalize ( IN.h ); float4 diff = diffColor * max ( dot ( n2, l2 ), 0.0 ); float4 spec = specColor * pow ( max ( dot ( n2, h2 ), 0.0 ), specPower ); return diff + spec; }
Визуальные эффекты на GPU Стандартные модели - освещение по Фонгу r - отражение вектора v относительно нормали n Уравнение освещенности:
Визуальные эффекты на GPU Стандартные модели - освещение по Фонгу
Визуальные эффекты на GPU Освещение по Фонгу. Вершинная программа FragmentData main ( VertexData IN, uniform float4 lightPos, uniform float4 eyePos, uniform float4x4 mvp, uniform float4x4 mv, uniform float4x4 mvi ) { FragmentData OUT; float3p = mul ( mv, IN.pos ).xyz; OUT.n = mul ( mvi, float4 ( IN.n, 0 ) ).xyz; OUT.l = normalize ( lightPos.xyz - p ); OUT.v = normalize ( eyePos.xyz - p ); OUT.pos = mul ( mvp, IN.pos ); OUT.texCoord = IN.texCoord; return OUT; }
Визуальные эффекты на 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; float3n2 = normalize ( IN.n ); float3l2 = normalize ( IN.l ); float3v2 = normalize ( IN.v ); float3r = reflect ( -v2, n2 ); float4diff = diffColor * max ( dot ( n2, l2 ), 0.0 ); float4spec = specColor * pow ( max ( dot ( l2, r ), 0.0 ), specPower ); return diff + spec; }
Визуальные эффекты на GPU Bumpmapping При использовании описанных моделей освещения поверхности получаются слишком гладкими Хочется добавить микрорельеф, не изменяя при этом саму геометрию объектов Попробуем просто в каждой точке отклонять вектор нормали. Тогда изменится освещенность в точке и создастся иллюзия микрорельефа
Визуальные эффекты на GPU Bumpmapping n - истинный вектор нормали n - искаженный вектор нормали
Визуальные эффекты на GPU Bumpmapping Результат искажения нормалей - при рендеринге обычного тора было произведено искажение векторов нормали
Визуальные эффекты на GPU Bumpmapping ? - в каком пространстве задавать искажения вектора нормали n ! - будем задавать в искажения вектора (точнее, сам вектор нормали n) в пространстве приклеенном к выводимой грани
Визуальные эффекты на GPU Bumpmapping Для каждой грани определим следующие попарно ортогональных единичных вектора t - касательный вектор к грани n - нормаль грани b - бинормаль (b = [n,t]) Эта тройка векторов образует ортонормированный базис в пространстве и, соответственно, систему координат, называемую касательной (tangent space)
Визуальные эффекты на GPU Bumpmapping Как кодировать искажения нормали - переведем единичный вектор нормали (в касательном пространстве) в цвет и поместим в текстуру Неискаженная нормаль в касательном пространстве всегда равна n=(0,01)
Визуальные эффекты на GPU Bumpmapping Вершинная программа: вычислить вектора l, v, h (или r) перевести их в касательное пространство передать фрагментной программе вычисленные вектора и текстурные координаты Фрагментная программа: нормировать вектора l, v, h (или r) взять из карты нормалей нормаль перевести нормаль из RGB- представления вычислить освещенность с использованием полученного вектора нормали и векторов l, v, h (или r)
Визуальные эффекты на GPU Bumpmapping - передаваемые данные structVertexData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float3 n : NORMAL; float3 t : TEXCOORD1; float3 b : TEXCOORD2; }; structFragmentData { 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 = mul ( mvp, IN.pos ); OUT.texCoord = IN.texCoord; 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 Bumpmapping - фрагментная программа float4 main ( FragmentData IN, uniform sampler2D bumpMap, uniform sampler2D diffuseMap ) : COLOR { constfloat4specColor = float4 ( 1, 1, 1, 1 ); float3n = tex2D ( bumpMap, IN.texCoord ).xyz; float3nt = normalize ( 2.0*n ); float3l2 = normalize ( IN.lt ); float3h2 = normalize ( IN.ht ); floatdiff = max ( dot ( nt, l2 ), 0.0 ) + 0.2; floatspec = pow ( max ( dot ( nt, h2 ), 0.0 ), 30.0 ); return diff * tex2D ( diffuseMap, IN.texCoord ) + spec * specColor; }
Визуальные эффекты на GPU Анизотропные модели освещения Поворот вокруг вектора нормали не изменяет векторов l и v, поэтому ранее рассмотренные модели освещения не зависят от подобных поворотов Однако встречаются поверхности, для это свойство не выполняется (поверхность CD, полированный металл). Такие поверхности называются анизотропными
Визуальные эффекты на GPU Анизотропные модели освещения Проще всего моделировать такие поверхности, считая их состоящими из множества ориентированных бесконечно-тонких нитей. Тогда в каждой точке поверхности можно задать касательный t вектор к такой нити, т.е. на поверхности объекта возникает поле касательных векторов t (P)
Визуальные эффекты на GPU Анизотропные модели освещения ? - как освещать подобную нить, ведь для нее неоднозначно определен вектор нормали n ! - рассмотрим диффузную и бликовую составляющие раздельно и для каждой из них выберем такой вектор n (ортогональный вектору t), максимизирующий соответствующую компоненту освещенности
Визуальные эффекты на GPU Анизотропные модели освещения Разложим вектор l на ортогональные части: Умножим скалярно на t и найдем параметры разложения Легко убедиться, что максимум (l,n) достигается на следующем векторе:
Визуальные эффекты на GPU Анизотропные модели освещения Таким образом получаем следующее уравнение освещенности для анизотропной поверхности:
Визуальные эффекты на GPU Анизотропные модели освещения Передаваемые данные structVertexData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float3 n : NORMAL; float3 t : TEXCOORD1; float3 b : TEXCOORD2; }; structFragmentData { 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 = float3 ( dot ( l, t ), dot ( l, b ), dot ( l, n ) ); OUT.ht = float3 ( dot ( h, t ), dot ( h, b ), dot ( h, n ) ); OUT.pos = mul ( mvp, IN.pos ); OUT.texCoord = IN.texCoord; return OUT; }
Визуальные эффекты на GPU Анизотропные модели освещения Фрагментная программа float4 main ( FragmentData IN, uniform sampler2D tangentMap, uniform sampler2D decalMap, uniform sampler2D anisoMap ) : COLOR { const float3 specColor = float3 ( 0, 0, 1 ); float3 tang = normalize ( 2*tex2D( tangentMap, IN.texCoord ).xyz-1); float dot1 = dot ( normalize ( IN.lt ), tang ); float dot2 = dot ( normalize ( IN.ht ), tang ); float2 arg = float2 ( dot1, dot2 ); float2 ds = tex2D ( anisoMap, arg*arg ).xy; float3 color = tex2D ( decalMap, IN.texCoord ).xyz; return float4 ( color * ds.x + specColor * ds.y, 1.0 ); }
Визуальные эффекты на GPU Модель Уорда (Ward) Простейший вид модели освещения Уорда: - гладкость поверхности - касательный вектор - бисектор векторов l и v
Визуальные эффекты на GPU Модель Уорда (Ward) Фрагментная программа float4 main ( FragmentData IN, uniform sampler2D tangentMap, uniform sampler2D decalMap ) : COLOR { const float4specColor = float4 ( 0, 0, 1, 0 ); const float3n = float3 ( 0, 0, 1 ); const floatroughness = 5.0; float4 color = tex2D ( decalMap, IN.texCoord ); float3 tang = normalize ( 2*tex2D ( tangentMap, IN.texCoord ).xyz-1 ); float dot1 = dot ( IN.ht, tang ) * roughness; float dot2 = dot ( IN.ht, n ); float p = dot1 / dot2; return float4 ( color.rgb + specColor.rgb * exp ( -p*p ), 1 ); }
Визуальные эффекты на GPU Моделирование преломления
! - используем встроенную функцию refract для нахождения преломленного вектора и по этому вектору возьмем значение из кубической карты отражения. const floateta = 0.9; // Find reflection/refraction vectors float3en = normalize ( IN.e ); float3nn = normalize ( IN.n ); float3r = reflect ( en, nn ); float3 t = refract ( en, nn, eta ); // Do a lookup into the environment map float3 envColor = texCUBE ( envMap, r ).xyz; float3 refractionColor = texCUBE ( envMap, t ).xyz;
Визуальные эффекты на GPU Моделирование преломления - учет длины волны
! - функция refract зависит также и от коэффициента преломления eta. Данный коэффициент зависит от длины волны. Поэтому возьмем три коэффициента преломления (по одному для каждой цветовой компоненты), найдем преломленный вектор и соответствующий цвет. Из каждого из трех полученных цветов возьмем по одной цветовой компоненте (соответствующей длине волны) и построим из них новый цвет.
Визуальные эффекты на GPU Свечение (glow)
Нарисовать светящийся объект в текстуру Применить «размытие» к этой текстуре Нарисовать всю сцену Наложить на изображение сцены размытую текстуру
Визуальные эффекты на GPU Дифракция
Разность фаз можно приближенно записать как d u, где 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 = float3 ( dot ( l, t ), dot ( l, b ), dot ( l, n ) ); OUT.ht = float3 ( dot ( h, t ), dot ( h, b ), dot ( h, n ) ); OUT.vt = float3 ( dot ( v, t ), dot ( v, b ), dot ( v, n ) ); OUT.pos = mul ( mvp, IN.pos ); OUT.texCoord = IN.texCoord; return OUT; }
Визуальные эффекты на 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
Визуальные эффекты на GPU Рендеринг меха
Слои 1, 2, 3 и т.д. Текстура
Визуальные эффекты на GPU Рендеринг меха
Модель освещения - анизотропная (для волосков) Вершинный шейдер - сдвинуть вершины для слоя и подготовить все величины для расчета освещенности Фрагментный шейдер - непосредственно расчет освещенности Вершинная программа также обеспечивает перенос вершин для очередного слоя и анимацию меха путем изменения текстурных координат в зависимости от времени
Визуальные эффекты на 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 float3specColor = float3 ( 1, 1, 1 ); float3 tang = normalize ( IN.furDir ); float dot1 = dot ( normalize ( IN.lt ), tang ); float dot2 = dot ( normalize ( IN.ht ), tang ); float2 arg = float2 ( dot1, dot2 ); float2 ds = tex2D ( anisoMap, arg*arg ).xy; float4 color = tex2D ( furMap, IN.texCoord * float2 ( 6, 12 ) ); return IN.color * float4 ( color.xyz + specColor * ds.y*0.5, color.w*color.w ); }
Визуальные эффекты на GPU Рендеринг меха. Главная программа // draw shells from innermost to outermost glEnable ( GL_BLEND ); glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // draw every layer for ( int i = 0; i < numLayers; i++ ) { floatt = (float) i / MAX_LAYERS; floatshadow = 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 = mul ( mv, IN.pos ).xyz; OUT.n = mul ( mvi, float4 ( IN.n, 0 ) ).xyz; OUT.l = normalize ( lightPos.xyz - OUT.p ); OUT.v = normalize ( eyePos.xyz - OUT.p ); OUT.h = normalize ( OUT.l + OUT.v ); OUT.pos = mul ( mvp, IN.pos ); OUT.texCoord = IN.texCoord; return OUT; }
Визуальные эффекты на GPU Сгорание как в DooM III. Фрагментная программа float4 main ( FragmentData IN, uniform float time, uniform sampler3D noiseMap ) : COLOR { float3noise = turbulence ( noiseMap, 0.1 * IN.p ); floatt = noise.x + fmod ( time, 15 ) / 15; float3n2 = normalize ( IN.n ); float3l2 = normalize ( IN.l ); float3h2 = normalize ( IN.h ); float4diff = diffColor * max ( dot ( n2, l2 ), 0.5 ); float4spec = specColor * pow ( max ( dot ( n2, h2 ), 0 ), 30 ); float4color = diff + spec; if ( t > 0 ) if ( t > 0.02 ) color = float4 ( 0, 0, 0, 0 ); else color = lerp ( darkRed, brightRed, t * 50 ); return color; }