3 230 Codepen

Солнечные лучи

Солнечные лучи на изображении. При движении изображения создается эффект прохождения лучей сквозь листву дерева. Сделано с использованием шейдеров и jаvascript


HTML

<script type="x-shader/x-fragment" id="mask">
  precision highp float;

  uniform vec2 resolution;
  uniform sampler2D image;
  uniform vec2 imageResolution;
  uniform float progress;

  vec2 adjustRatio(vec2 uv, vec2 inputResolution, vec2 outputResolution) {
    vec2 ratio = vec2(
      min((outputResolution.x / outputResolution.y) / (inputResolution.x / inputResolution.y), 1.),
      min((outputResolution.y / outputResolution.x) / (inputResolution.y / inputResolution.x), 1.)
    );
    return uv * ratio + (1. - ratio) * 0.5;
  }

  vec2 getZoomedUv(vec2 uv, float zoom, vec2 origin) {
    origin.x = -origin.x;
    uv += origin;
    float scale = 1. / zoom;
    vec2 zoomedUv = uv * scale;
    zoomedUv -= 0.5 * (scale - 1.);
    return zoomedUv;
  }
  
  const float maxMove = 0.032;

  void main() {
    vec2 uv = gl_FragCoord.st / resolution;
    uv.y = 1. - uv.y;
    uv = adjustRatio(uv, imageResolution, resolution);
    uv = getZoomedUv(uv, 1. + maxMove, vec2((progress - 0.5) * maxMove, 0.));

    gl_FragColor = texture2D(image, uv);
  }
</script>

CSS

html,
body {
  height: 100%;
  margin: 0;
}

JS

Дополнительный скрипт
https://unpkg.com/@ko-yelie/kgl@0.2.3/dist/index.iife.min.js
и
// using original library https://github.com/ko-yelie/kgl

/**
 * utils
 */
function loadImage(srcs, isCrossOrigin) {
  if (!(typeof srcs === 'object' && srcs.constructor.name === 'Array')) {
    srcs = [srcs]
  }
  let promises = []
  srcs.forEach(src => {
    const img = document.createElement('img')
    promises.push(
      new Promise(resolve => {
        img.addEventListener('load', () => {
          resolve(img)
        })
      })
    )
    if (isCrossOrigin) img.crossOrigin = 'anonymous'
    img.src = src
  })
  return Promise.all(promises)
}

function mix(x, y, a) {
  return x * (1 - a) + y * a
}

/**
 * main
 */
; (async function main() {
  const image = 'https://images.unsplash.com/photo-1519567770579-c2fc5436bcf9?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1200&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ' // https://unsplash.com/photos/qwHHOC2z5Xs
  const speed = 0.4
  const maxStrength = 13
  const minStrength = 3
  const maxRadius = window.innerWidth < 768 ? 0.08 : 0.3
  const minRadius = window.innerWidth < 768 ? 0.01 : 0.05

  const [img] = await loadImage(image, true)

  const webgl = new Kgl({
    programs: {
      mask: {
        fragmentShaderId: 'mask',
        uniforms: {
          image: img,
          imageResolution: [img.width, img.height],
          progress: 0,
        }
      },
    },
    effects: [
      'godray',
    ],
    framebuffers: [
      'mask',
      'cache',
      'output'
    ],
    tick: time => {
      const progress = Math.sin(time * speed) * 0.5 + 0.5;

      webgl.bindFramebuffer('mask')
      webgl.programs['mask'].draw({
        progress
      })

      webgl.effects['godray'].draw(
        'mask',
        'cache',
        'output',
        mix(maxStrength, minStrength, progress),
        [
          mix(webgl.canvas.width * -0.2, webgl.canvas.width * 0.3, progress),
          webgl.canvas.height * 1.2,
        ],
        mix(maxRadius, minRadius, progress),
        true
      )
    },
  })
})()

Комментарии

  • Facebook
  • Вконтакте

Похожие статьи