Страницы

Поиск по вопросам

воскресенье, 9 февраля 2020 г.

Canvas / Создание эффекта (маски) миража для изображения

#javascript #svg #canvas #webgl #svg_animation


Подскажите, есть ли легкий способ создать эффекта миража(марева) на технологиях canvas
/ svg для изображения. В приведенном примере используется перемещение текстур, можно
ли проще, к примеру для анимации эффекта огня?  
    


Ответы

Ответ 1



Выбираем картинку с объемными объектами на дальнем плане, чтобы эффект миража лучше проявился. Применение фильтров сильно нагружает видеокарту, поэтому при реальном применении этого эффекта старайтесь уменьшить размеры зоны действия фильтра. Применяем к ней два фильтра feTurbulenceи feDisplacementMap Подробно о фильтрах, на русском, с многочисленными примерами можно почитать здесь За основу создания эффекта миража в фильтре отвечают несколько параметров, но основной параметр это baseFrequency Частота генерации шума, которая представляется в виде двух чисел, определяющих горизонтальное и вертикальное направления. Если задается одно число, то оно по умолчанию принимается для обоих направлений. Вот этот параметр фильтра и будем анимировать с помощью скрипта Остальные параметры фильтра можете регулировать по своему вкусу. scale="6" - увеличивает размер волн seed="53" - отвечает за слоистость структуры frames += 0.3 - увеличение прироста фраймов, увеличивает скорость волновых эффектов var img = document.querySelector("#displacementFilter feTurbulence"); var frames = 0; var rad = Math.PI / 180; function AnimateBaseFrequency() { //baseFrequency="0.01 .1" bfx = 0.01; bfy = 0.1; frames += 0.8 bfx += 0.01 * Math.cos(frames * rad); bfy += 0.01 * Math.sin(frames * rad); bf = bfx.toString() + ' ' + bfy.toString(); img.setAttributeNS(null, 'baseFrequency', bf); window.requestAnimationFrame(AnimateBaseFrequency); } window.requestAnimationFrame(AnimateBaseFrequency); .container { width:50%; height:50%; }
Пламя Чтобы получить тот или иной эффект нужно подобрать несколько однотипных картинок, например горящего огня и после экспериментов с изменением атрибутов фильтров и переменных скрипта пробовать их по очереди, какая картинка будет давать лучший эффект. var img = document.querySelector("#displacementFilter feTurbulence"); var frames = 0; var rad = Math.PI / 180; function AnimateBaseFrequency() { //baseFrequency="0.01 .1" bfx = 0.1; bfy = 0.1; frames += .4 bfx += 0.05 * Math.cos(frames * rad); bfy += 0.1 * Math.sin(frames * rad); bf = bfx.toString() + ' ' + bfy.toString(); img.setAttributeNS(null, 'baseFrequency', bf); window.requestAnimationFrame(AnimateBaseFrequency); } window.requestAnimationFrame(AnimateBaseFrequency); .container { width:50%; height:50%; }
Огонь и вода При обработке изображений необходимо уменьшить их размеры в любом растровом редакторе. Лучше в фотошопе, у которого есть недокументированная возможность уменьшать поэтапно на 10% размеры изображения без заметной потери качества. таким образом можно получить из изображения весом 2.2 Mb картинку весом 100kb В шапке svg файла необходимо выставить размер viewBox с атрибутами равными размеру используемой картинки. Например в этом примере - viewBox="0 0 670 526" var img = document.querySelector("#displacementFilter feTurbulence"); var frames = 0; var rad = Math.PI / 180; function AnimateBaseFrequency() { //baseFrequency="0.01 .1" bfx = 0.1; bfy = 0.1; frames += .31 bfx += 0.1 * Math.cos(frames * rad); bfy += 0.1 * Math.sin(frames * rad); bf = bfx.toString() + ' ' + bfy.toString(); img.setAttributeNS(null, 'baseFrequency', bf); window.requestAnimationFrame(AnimateBaseFrequency); } window.requestAnimationFrame(AnimateBaseFrequency); .container { width:50%; height:50%;


Ответ 2



Вот смастерил WebGL версию такого фильтра, за основу взят код вот из этого поста, там я уже делал фильтр для текстуры. Алгоритм: // запоминаем текущую текстурную координату vec2 p_d = uv; // прибавляем dt (сколько прошло времени с прошлого кадра) p_d.y += t * 0.1; // берем для полученного значения текстурных координат сэмпл 2d шума // (тут могла быть текстура вместо вызова математического метода, и это будет быстрее) vec2 offset = vec2(noise(p_d * 22.)); // плавно уменьшаем это значения до 0 (0 сверху) offset *= uv.y * 0.01; // берем цвет оригинальным координатам + смещение return sample(uv.xy + offset); let t = new Date().getTime(); let url = "https://webgl2fundamentals.org/webgl/resources/images/computer-history-museum/pos-z.jpg"; let filter = webglFilter(url, ` uniform float t; float rand(vec2 n) { return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); } float noise(vec2 p){ vec2 ip = floor(p); vec2 u = fract(p); u = u*u*(3.0-2.0*u); float res = mix( mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); return res*res; } vec4 frag (vec2 uv) { vec2 p_d = uv; p_d.y += t * 0.1; vec2 offset = vec2(noise(p_d*22.)); offset *= uv.y * 0.01; return sample(uv.xy + offset); } `); filter.ready = function() { let c = filter.canvas; document.body.append(c); animate(); } function animate() { filter.uniform('1f', 't', (new Date().getTime() - t)/1000) .apply(); requestAnimationFrame(animate); }

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

Отправить комментарий