【愚公系列】2023年08月 WEBGL专题-2D特效-放大镜

文章目录


前言

放大镜是一种透明的光学镜片,经常用于放大视野中的细节。它有一个凸透镜或一个复合透镜,可以通过透镜的曲面来聚焦光线,使得物体在放大镜的另一端显得更大和更清晰。放大镜通常用于读取小字或细节,检查小物品或难以观察的事物,如昆虫、病毒等。

一、放大镜

WebGL可以用于实现放大镜效果,具体实现步骤如下:

  1. 创建一个WebGL上下文,并设置canvas的宽度和高度。

  2. 加载需要放大的图片,并创建WebGL纹理对象。可以使用gl.texImage2D函数将图片转换为纹理。

  3. 编写着色器程序,包括顶点着色器和片元着色器。顶点着色器负责将顶点位置传递给片元着色器。片元着色器负责将纹理坐标传递给gl_FragColor输出颜色。

  4. 绘制一个矩形,上面贴上纹理。这个矩形的大小应该和放大镜的大小一致。

  5. 监听鼠标移动事件,获取当前鼠标的位置,并将其转换为纹理坐标。

  6. 使用gl.scissor函数设置裁剪区域,并使用gl.drawArrays函数绘制放大镜区域。

  7. 最后使用requestAnimationFrame函数循环更新画面,实现动态放大镜效果。

案例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="../lib/index.js"></script>
  <style>
    * {
      
      
      margin: 0;
      padding: 0;
    }

    canvas{
      
      
      margin: 50px auto 0;
      display: block;
    }
  </style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
  此浏览器不支持canvas
</canvas>
</body>
</html>
<script>

  const ctx = document.getElementById('canvas')

  const gl = ctx.getContext('webgl')

  // 创建着色器源码
  const VERTEX_SHADER_SOURCE = `
    // 只传递顶点数据
    attribute vec4 aPosition;

    attribute vec4 aTex;

    varying vec2 vTex;
    varying vec4 vPosition;

    void main() {
      vPosition = aPosition;
      gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
      vTex = vec2(aTex.x, aTex.y);
    }
  `; // 顶点着色器

  const FRAGMENT_SHADER_SOURCE = `
    precision lowp float;
    uniform sampler2D uSampler;
    varying vec2 vTex;
    uniform vec2 lookAt; // 观察点的坐标
    varying vec4 vPosition;

    void main() {
      vec2 uv = vTex;

      float fOpacity = 0.0;
      // 1. 判断当前点和 lookAt 的距离
      float dis = distance(lookAt, vec2(vPosition));
      if (dis > 0.2) {
        fOpacity = 0.05;
      } else {
        fOpacity = 1.0;

        vec2 diff = vPosition.xy - lookAt;
        uv.x -= diff.x * 0.2;
        uv.y += diff.y * 0.2;
      }

      vec4 color = texture2D(uSampler, uv);

      gl_FragColor = vec4(color.xyz * fOpacity, fOpacity);
    }
  `; // 片元着色器

  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

  const aPosition = gl.getAttribLocation(program, 'aPosition');
  const aTex = gl.getAttribLocation(program, 'aTex');
  const uSampler = gl.getUniformLocation(program, 'uSampler');
  const lookAt = gl.getUniformLocation(program, 'lookAt');

  const points = new Float32Array([
    -0.9,  0.9, 0.0, 1.0,
    -0.9, -0.9, 0.0, 0.0,
     0.9,  0.9, 1.0, 1.0,
     0.9, -0.9, 1.0, 0.0,
  ])

  const buffer = gl.createBuffer();
  const BYTES = points.BYTES_PER_ELEMENT;

  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

  gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

  gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 4, 0);

  gl.enableVertexAttribArray(aPosition)

  gl.vertexAttribPointer(aTex, 2, gl.FLOAT, false, BYTES * 4, BYTES * 2);

  gl.enableVertexAttribArray(aTex)

  const img = new Image();
  img.onload = function() {
      
      
    // 创建纹理对象
    const texture = gl.createTexture();

    // 翻转 图片 Y轴
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)

    // 开启一个纹理单元
    gl.activeTexture(gl.TEXTURE0);

    // 绑定纹理对象
    gl.bindTexture(gl.TEXTURE_2D, texture);

    // 处理放大缩小的逻辑
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)

    // 横向 纵向 平铺的方式
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

    // 配置纹理图像
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);

    gl.uniform1i(uSampler, 0);
  }

  img.src = '../assets/content.png'

  function draw() {
      
      

    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    requestAnimationFrame(draw)
  }

  draw()

  document.onmousemove = (ev) => {
      
      
    // 坐标
    const x = ev.clientX
    const y = ev.clientY

    const domPosition = ev.target.getBoundingClientRect();

    const domx = x - domPosition.left
    const domy = y - domPosition.top;
    const halfWidth = ctx.offsetWidth / 2
    const halfHeight = ctx.offsetHeight / 2

    const clickX = (domx - halfWidth) / halfWidth
    const clickY = (halfHeight - domy) / halfHeight

    gl.uniform2fv(lookAt, [clickX, clickY]);
  }

</script>

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/aa2528877987/article/details/132111978
今日推荐