Efek blur progresif menggunakan WebGL dengan shader OGL dan GLSL – Beragampengetahuan
Hai, saya Jorge Toloza, seorang pengembang kreatif lepas di Kolombia, dan saya menyambut baik kesempatan untuk menginspirasi, mengajar, dan belajar dengan beragampengetahuan.
Dalam tutorial ini, kita akan membuat efek blur gradien progresif yang berubah secara dinamis berdasarkan posisi gambar. Kita akan menggunakan CSS untuk memposisikan elemen dan kemudian mendapatkan koordinat objek di WebGL.
Sebagian besar logikanya ada pada shader, sehingga Anda dapat mengonversi efek ini ke pustaka WebGL lainnya (seperti Three.js) atau bahkan menerapkannya di WebGL biasa.
Contents
struktur HTML
Pertama kita perlu mendefinisikan wadah gambar kita, yang sangat sederhana; <figure> Dan <img> Label.
<figure class="media">
<img src="/img/8.webp" alt="tote bag">
</figure>
gaya
Penataannya juga sangat sederhana, kami menyembunyikan gambar saat akan ditampilkan di kanvas.
.media
img
width: 100%;
visibility: hidden;
tingkat GL
Sekarang di JS, kami memiliki beberapa kelas penting. yang pertama adalah GL.jsyang berisi sebagian besar logika untuk merender gambar menggunakan OGL.
import Renderer, Camera, Transform, Plane from 'ogl'
import Media from './Media.js';
export default class GL
constructor ()
this.images = [...document.querySelectorAll('.media')]
this.createRenderer()
this.createCamera()
this.createScene()
this.onResize()
this.createGeometry()
this.createMedias()
this.update()
this.addEventListeners()
createRenderer ()
this.renderer = new Renderer(
canvas: document.querySelector('#gl'),
alpha: true
)
this.gl = this.renderer.gl
createCamera ()
this.camera = new Camera(this.gl)
this.camera.fov = 45
this.camera.position.z = 20
createScene ()
this.scene = new Transform()
createGeometry ()
this.planeGeometry = new Plane(this.gl,
heightSegments: 50,
widthSegments: 100
)
createMedias ()
this.medias = this.images.map(item =>
return new Media(
gl: this.gl,
geometry: this.planeGeometry,
scene: this.scene,
renderer: this.renderer,
screen: this.screen,
viewport: this.viewport,
$el: item,
img: item.querySelector('img')
)
)
onResize ()
this.screen =
width: window.innerWidth,
height: window.innerHeight
this.renderer.setSize(this.screen.width, this.screen.height)
this.camera.perspective(
aspect: this.gl.canvas.width / this.gl.canvas.height
)
const fov = this.camera.fov * (Math.PI / 180)
const height = 2 * Math.tan(fov / 2) * this.camera.position.z
const width = height * this.camera.aspect
this.viewport =
height,
width
if (this.medias)
this.medias.forEach(media => media.onResize(
screen: this.screen,
viewport: this.viewport
))
this.onScroll(scroll: window.scrollY)
onScroll(scroll)
if (this.medias)
this.medias.forEach(media => media.onScroll(scroll))
update()
if (this.medias)
this.medias.forEach(media => media.update())
this.renderer.render(
scene: this.scene,
camera: this.camera
)
window.requestAnimationFrame(this.update.bind(this))
addEventListeners ()
window.addEventListener('resize', this.onResize.bind(this))
untuk manusia
Kami mengimpor beberapa objek penting, mengambil semua gambar dari DOM, dan membuat Media objek untuk setiap gambar. Terakhir, kami memperbarui media dan penyaji update Penggunaan metode requestAnimationFrame.
kelas media
Ini yang paling keren. Pertama kita atur semua pilihannya, lalu atur shadernya dan tunggu teksturnya diatur uImageSize seragam. Jaringannya akan menjadi Plane. di dalam onResize Pada caranya, kita mengatur posisi media pada kanvas berdasarkan posisinya pada CSS. Saya menggunakan metode yang sama dari tutorial Bizarro, Anda dapat melihatnya jika Anda ingin tahu lebih banyak tentang posisi gambar dan perilaku sampul.
import Mesh, Program, Texture from 'ogl'
import vertex from '../../shaders/vertex.glsl';
import fragment from '../../shaders/fragment.glsl';
export default class Media {
constructor ( gl, geometry, scene, renderer, screen, viewport, $el, img )
this.gl = gl
this.geometry = geometry
this.scene = scene
this.renderer = renderer
this.screen = screen
this.viewport = viewport
this.img = img
this.$el = $el
this.scroll = 0
this.createShader()
this.createMesh()
this.onResize()
createShader ()
const texture = new Texture(this.gl,
generateMipmaps: false
)
this.program = new Program(this.gl,
depthTest: false,
depthWrite: false,
fragment,
vertex,
uniforms:
tMap: value: texture ,
uPlaneSize: value: [0, 0] ,
uImageSize: value: [0, 0] ,
uViewportSize: value: [this.viewport.width, this.viewport.height] ,
uTime: value: 100 * Math.random() ,
,
transparent: true
)
const image = new Image()
image.src = this.img.src
image.onload = _ =>
texture.image = image
this.program.uniforms.uImageSize.value = [image.naturalWidth, image.naturalHeight]
createMesh ()
this.plane = new Mesh(this.gl,
geometry: this.geometry,
program: this.program
)
this.plane.setParent(this.scene)
onScroll (scroll)
this.scroll = scroll
this.setY(this.y)
update ()
this.program.uniforms.uTime.value += 0.04
setScale (x, y)
x = x
setX(x = 0)
this.x = x
this.plane.position.x = -(this.viewport.width / 2) + (this.plane.scale.x / 2) + (this.x / this.screen.width) * this.viewport.width
setY(y = 0)
this.y = y
this.plane.position.y = (this.viewport.height / 2) - (this.plane.scale.y / 2) - ((this.y - this.scroll) / this.screen.height) * this.viewport.height
onResize ( screen, viewport = )
if (screen)
this.screen = screen
if (viewport)
this.viewport = viewport
this.plane.program.uniforms.uViewportSize.value = [this.viewport.width, this.viewport.height]
this.setScale()
this.setX(this.$el.offsetLeft)
this.setY(this.$el.offsetTop)
}
puncak
Implementasinya sangat sederhana, kita mendapatkan UV dan posisi untuk merender gambar.
precision highp float;
attribute vec3 position;
attribute vec2 uv;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
varying vec2 vUv;
void main()
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
pecahan
Oke, inilah dokumen terakhirnya; kita hampir sampai.
precision highp float;
uniform vec2 uImageSize;
uniform vec2 uPlaneSize;
uniform vec2 uViewportSize;
uniform float uTime;
uniform sampler2D tMap;
varying vec2 vUv;
/*
by @arthurstammet
*/
float tvNoise (vec2 p, float ta, float tb)
return fract(sin(p.x * ta + p.y * tb) * 5678.);
vec3 draw(sampler2D image, vec2 uv)
return texture2D(image,vec2(uv.x, uv.y)).rgb;
float rand(vec2 co)
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
/*
inspired by
@anastadunbar
*/
vec3 blur(vec2 uv, sampler2D image, float blurAmount)
vec3 blurredImage = vec3(0.);
float d = smoothstep(0.8, 0.0, (gl_FragCoord.y / uViewportSize.y) / uViewportSize.y);
#define repeats 40.
for (float i = 0.; i < repeats; i++)
vec2 q = vec2(cos(degrees((i / repeats) * 360.)), sin(degrees((i / repeats) * 360.))) * (rand(vec2(i, uv.x + uv.y)) + blurAmount);
vec2 uv2 = uv + (q * blurAmount * d);
blurredImage += draw(image, uv2) / 2.;
q = vec2(cos(degrees((i / repeats) * 360.)), sin(degrees((i / repeats) * 360.))) * (rand(vec2(i + 2., uv.x + uv.y + 24.)) + blurAmount);
uv2 = uv + (q * blurAmount * d);
blurredImage += draw(image, uv2) / 2.;
return blurredImage / repeats;
void main()
vec2 ratio = vec2(
min((uPlaneSize.x / uPlaneSize.y) / (uImageSize.x / uImageSize.y), 1.0),
min((uPlaneSize.y / uPlaneSize.x) / (uImageSize.y / uImageSize.x), 1.0)
);
vec2 uv = vec2(
vUv.x * ratio.x + (1.0 - ratio.x) * 0.5,
vUv.y * ratio.y + (1.0 - ratio.y) * 0.5
);
float t = uTime + 123.0;
float ta = t * 0.654321;
float tb = t * (ta * 0.123456);
vec4 noise = vec4(1. - tvNoise(uv, ta, tb));
vec4 final = vec4(blur(uv, tMap, 0.08), 1.0);
final = final - noise * 0.08;
gl_FragColor = final;
Mari kami jelaskan. Pertama, kami menerapkan pemotongan pada gambar untuk menjaga proporsi:
vec2 ratio = vec2(
min((uPlaneSize.x / uPlaneSize.y) / (uImageSize.x / uImageSize.y), 1.0),
min((uPlaneSize.y / uPlaneSize.x) / (uImageSize.y / uImageSize.x), 1.0)
);
vec2 uv = vec2(
vUv.x * ratio.x + (1.0 - ratio.x) * 0.5,
vUv.y * ratio.y + (1.0 - ratio.y) * 0.5
);
Selanjutnya, kita menggunakan waktu untuk mendapatkan noise TV dari gambar tersebut tvNoise Fungsi:
float t = uTime + 123.0;
float ta = t * 0.654321;
float tb = t * (ta * 0.123456);
vec4 noise = vec4(1. - tvNoise(uv, ta, tb));
Untuk blur, saya menggunakan blur Fungsi ini didasarkan pada Gaussian blur yang diterapkan oleh @anastadunbar. Kami pada dasarnya mendapatkan rata-rata relatif terhadap piksel saat ini dan pengulangannya.
Bagian yang penting adalah variabel gradien. kami menggunakan gl_FragCoord Dan uViewportSize Menghasilkan gradien tetap di bagian bawah viewport sehingga kita dapat menerapkan blur berdasarkan kedekatan setiap piksel ke tepi.
vec3 blur(vec2 uv, sampler2D image, float blurAmount)
vec3 blurredImage = vec3(0.);
float gradient = smoothstep(0.8, 0.0, (gl_FragCoord.y / uViewportSize.y) / uViewportSize.y);
#define repeats 40.
for (float i = 0.; i < repeats; i++)
vec2 q = vec2(cos(degrees((i / repeats) * 360.)), sin(degrees((i / repeats) * 360.))) * (rand(vec2(i, uv.x + uv.y)) + blurAmount);
vec2 uv2 = uv + (q * blurAmount * gradient);
blurredImage += draw(image, uv2) / 2.;
q = vec2(cos(degrees((i / repeats) * 360.)), sin(degrees((i / repeats) * 360.))) * (rand(vec2(i + 2., uv.x + uv.y + 24.)) + blurAmount);
uv2 = uv + (q * blurAmount * gradient);
blurredImage += draw(image, uv2) / 2.;
return blurredImage / repeats;
Lalu kita bisa mengembalikan warna terakhir
vec4 final = vec4(blur(uv, tMap, 0.08), 1.);
final = final - noise * 0.08;
gl_FragColor = final;
Anda harus mendapatkan beberapa tautan:
Itu dia! Terima kasih sudah membaca. Saya harap tutorial ini bermanfaat bagi Anda 🥰.
Foto oleh @jazanadipatocu.
Dimana digital bertemu dengan fisik: Pencetakan risograf menggunakan WebGL
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Efek #blur #progresif #menggunakan #WebGL #dengan #shader #OGL #dan #GLSL