Анимированные эффекты теплового искажения с помощью WebGL

Анимация в виде волнового излучения тепла посредством WEB-GL. В нашем демо мы покажем 4 примера, как сделать ваш сайт более живым! Используя наши примеры и инструкции вы сможете понять данную технологию и создать свои варианты данной технологии

# Разработка 13 Сентября 2020 Комментариев: 1

Пособие по созданию фрагментных шейдеров в WebGL для создания эффекта искажения анимированных эффектов горячего воздуха на изображениях и тексте

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

Внимание: демоверсии используют некоторые современные веб-технологии, которые могут не работать в старых браузерах. Обратите внимание, что текущий ZIP-файл - это скомпилированная сборка этого проекта. Работает только на сервере!

Для полноценного описания, мы выберем одну из демонстраций, чтобы дать вам обзор того, как работает анимация WebGL. Со временем мы будем выкладывать больше примеров с использованием WebGL. Сейчас же мы разберем конкретный пример из нашего демо. Для более глубокого изучения перейдите на страницу WebGL Fundamentals или Learning WebGL. Если хорошо изучить данную технологию можно делать даже игры, или найти более применение этим знаниям! Здесь очень много примеров!

Давайте рассмотрим несколько аспектов демонстрации, чтобы понять, как реализован данный пример.

Искажение

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


varying vec2 position;
uniform sampler2D texture;

void main(){
	// Получаем цвет пикселя какртинки в текущей позиции
  vec4 color=texture2D(texture,position);

  gl_FragColor=color;
}
Тепловое искажение WEB GL

Так же мы можем задать смещение координат, откуда следует взять цвет пикселя подложки.

float distortion=position.y*0.2;
vec4 color=texture2D(texture,vec2(position.x+distortion,position.y));
Тепловое искажение WEB GL

Теперь мы получаем волновое искажение основываясь на синусоидальной волне

Тепловое искажение WEB GL

Здесь мы добавим в положение X синусовую кривую, основанную на Y-позиции.

// Возможно отрегулировать величины на свое усмотрение, меняя скорость размер и частоту эффекта
float frequency=100.0;
float amplitude=0.003;
float distortion=sin(position.y*frequency)*amplitude;
vec4 color=texture2D(texture,vec2(position.x+distortion, position.y));
Тепловое искажение WEB GL

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

(function draw(){
  requestAnimationFrame(draw);
}())

Теперь давайте подумаем об оптимизации. Ведь, как и в играх на производительность может повлиять аппаратная часть устройства. Эффект может зависнуть или наоборот ускориться, как если бы запустили старую игру на современном устройстве. Зададим необходимые параметры, которые будут одинаковы вне зависимости от устройства.

var fps=60; // частота кадров в секунду
var frameDuration=1000/fps; // за сколько милисекунд должна происходить отрисовка объекта
var time=0; // значение времени, которое будет отправлено шейдерам
var lastTime=0; // время отрисовки последнего кадра
(function draw(elapsed){
  var delta=elapsed-lastTime;
  lastTime=elapsed;
  var step=delta/frameDuration;
  time+=step;
  ball.x += 20*step;
  requestAnimationFrame(draw);
}(0));

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

Пишем в JS файле
(function draw(elapsed){
  var location=gl.getUniformLocation(program,"time");
  gl.uniform1f(location,time);
})
Пишем в шейдере
float speed=0.03;
float distortion=sin(position.y*frequency+time*speed)*amplitude;
vec4 color=texture2D(texture,vec2(position.x+distortion, position.y));
В результате получаем

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

Тепловое искажение WEB GL

Обратите внимание, что края размыты - это ослабляет эффект и не искажает то, что мы не хотим. Затем мы можем умножить величину искажения на яркость текущего пикселя карты.

float map=texture2D(map,position).r;
vec4 color=texture2D(texture,vec2(position.x+distortion*map, position.y));

Эффект глубины, или 3D эффект

Эффект глубины + параллакс работает примерно так же - получаем значение цвета из другой позиции, основанной на карте и некоторых значениях. В этом случае значения представляют собой координаты X и Y.

...
document.addEventListener('mousemove',function(event){
  var location=gl.getUniformLocation(program,"mouse");
  gl.uniform2f(location,event.clientX/canvas.width,event.clientY/canvas.height);
})
...
vec2 parallax=mouse*0.005;
vec2 distortedPosition=vec2(position.x+distortion*map, position.y);
vec4 color=texture2D(texture,distortedPosition+parallax);

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

Тепловое искажение WEB GL

В коде мы ссылаемся на каждый из цветов по их соответствующим каналам. Нужно иметь хотя бы основы знаний дизайна что бы это понять.

...
vec4 maps=texture2D(mapsTexture,pos);

float depthMap=maps.r;
float distortionMap=maps.g;
...

vec2 distortedPosition=vec2(position.x+distortion*distortionMap, position.y);
vec4 color=texture2D(texture,distortedPosition+parallax*depthMap);

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


Контент

Мы показали, что с изображением можно делать все что угодно, но что же делать с текстом?

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

Для прорисовки текста мы будем использовать SVG. Мы можем нарисовать абсолютно любой объект которому зададим свойства уже в css. Естественно, картинка нарисованная кодом будет загружаться моментально, наряду просто с изображением, которое находится в файле. Да и работать с таким изображением проще.

Это быстрый способ загрузить SVG и нарисовать его как маску:

var canvas=document.createElement('canvas');
loadSVG('file.svg',canvas);

  function loadSVG(file,canvas){
    var svg=new Image();
    svg.addEventListener('load',function(){
      var ctx=canvas.getContext('2d');
      canvas.width=svg.width;
      canvas.height=svg.height;
      ctx.drawImage(svg,0,0);
    })
    svg.src=file;
  }

Теперь мы можем использовать эту маску так же, как и любую другую текстуру.

Трюк для облегчения позиционирования текстуры, которую мы только что создали в контейнере WebGL, состоит в том, чтобы создать и разместить маску точно так же, как любой другой элемент - то есть с использованием HTML и CSS - и задать ему окончательную позицию с помощью getBoundingClientRect, а затем отправить его на обработку в шейдер.

var title=document.querySelector('canvas');
var bounds=title.getBoundingClientRect();
var location=gl.getUniformLocation(program,"contentPosition");
gl.uniform2f(location,bounds.left,bounds.top);
var location=gl.getUniformLocation(program,"contentSize");
gl.uniform2f(location,bounds.width,bounds.height);
Тепловое искажение WEB GL

И вот наш конечный результат! мы взяли его и подробно расписали само построение такой живой картинки. Есть много вариантов применения данной инструкции, как например - живая вода, на последнем примере. Надеемся данная инструкция не покажется слишком сложной и вы сможете расширить свои знания по данному направлению. Пробуйте и продвигайте свои идеи! Напишите в комментария что вы думаете об этом всем.


Демонстрация Скачать ZIP

В своих примерах мы не показываем как подключать те или иные файлы, подразумевается, что вы знакомы с основами html

Комментарии ( )

    Тепловое искажение WEB GL

    cat345

    13 сентября 2020, 16:48

    Хороший урок, хорошая технология, спасибо! Единственное у меня начинает комп сильно шуметь и охлаждать систему) видимо данная технология хорошенько так нагружает комп, на смартфоне лучше естественно отключать, ибо будет лагать.


    Открыть чат
    Нужна помощь?
    Здравствуйте!
    Чем Вам помочь?