Efek blur progresif menggunakan WebGL dengan shader OGL dan GLSL

 – Beragampengetahuan
8 mins read

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

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *