WebGL雾化与半透明效果

一、雾化

WEBGL中的雾化是一种视觉效果,可以模拟远处物体出现“雾气”或“烟雾”等效果,使场景更加真实。雾化可以让场景中的物体看起来模糊或透明,从而创建出一种深度感和距离感,增强场景的立体感。

WEBGL中的雾化通常使用线性或指数函数来计算出物体的深度,然后根据深度来进行颜色和透明度的调整,从而实现雾化效果。调整透明度的方式有多种,例如将物体的alpha值进行平滑过渡,或者使用特定的shader程序来自定义透明度的计算方式。

在实际的WEBGL应用中,雾化可以被用于增强场景的真实感,同时也可以用于创建特定的氛围和视觉风格。

雾化的实现流程

雾化实现是通过某点和视点之间的距离,距离越远雾化程度越⾼。这种雾化也称为线性雾化。某一点的雾化程度也成为雾化因⼦,WebGL中实现雾化的一般流程如下:

  1. 创建shader:在WebGL中,我们使用着色器(shader)来描述如何绘制一个物体,因此,我们需要创建雾化的shader。在shader中,我们需要定义雾化程度的变量,以及计算每个像素颜色时需要使用的公式等。
  2. 定义雾化区域:定义哪些区域需要雾化。对于这一步,我们可以依据相机的距离,将远离相机的物体进行雾化处理,或者根据场景中物体的坐标确定需要雾化的区域。
  3. 计算雾化程度:计算每个像素的雾化程度,这个程度可以用雾化公式进行计算,可以使用线性雾化公式或者指数雾化公式等。
  4. 应用雾化:将计算得到的雾化程度,应用到相应的像素上,最终实现雾化效果。

实现雾化效果时,需要考虑场景中物体的深度,而且雾化程度也可能会随着距离的远近而变化,因此,需要综合考虑这些因素,调整雾化效果。
在这里插入图片描述

雾化因子计算:雾化因子 = (终点 - 当前点) / (终点 - 起点)
物体颜色计算:颜色 = 物体颜色 * 雾化因子 + 雾化颜色 * ( 1 - 雾化因子)

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

    canvas{
      margin: 50px auto 0;
      display: block;
      background: yellow;
    }
  </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 aNormal;
    varying vec4 vColor;

    varying float vDist;

    uniform mat4 mat;
    void main() {
      // 定义点光源的颜色
      vec3 uPointLightColor = vec3(1.0,1.0,0.0);

      // 点光源的位置
      vec3 uPointLightPosition = vec3(-5.2,5.6,5.0);

      // 环境光
      vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);

      // 物体表面的颜色
      vec4 aColor = vec4(1.0,0.0,0.0,1.0);

      // 顶点的世界坐标
      vec4 vertexPosition = mat * aPosition;

      // 点光源的方向
      vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));

      // 环境反射
      vec3 ambient = uAmbientLightColor * vec3(aColor);

      // 计算入射角 光线方向和法线方向的点积
      float dotDeg = dot(lightDirection, vec3(aNormal));

      // 漫反射光的颜色
      vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;

      gl_Position = vertexPosition;
      vColor = vec4(ambient + diffuseColor, aColor.a);
      vDist = gl_Position.w;
    }
  `; // 顶点着色器

  const FRAGMENT_SHADER_SOURCE = `
    precision lowp float;
    varying vec4 vColor;
    varying float vDist;

    // 雾化颜色
    uniform vec3 uFogColor;
    // 起点到终点的距离 第一个参数是起点   第二个是终点
    uniform vec2 uFogDist;

    void main() {
      // 计算雾化因子
      float fogFactor = (uFogDist.y - vDist) / (uFogDist.y - uFogDist.x);

      // 看到的颜色是什么 物体颜色*雾化因子+雾化颜色*(1-雾化因子)
      // mix 线性混合计算  mix(x,y,a) => { x * (1-a) + y * a }
      vec3 color = mix(uFogColor, vec3(vColor), fogFactor);
      gl_FragColor = vec4(color, vColor.a);
    }
  `; // 片元着色器

  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

  const aPosition = gl.getAttribLocation(program, 'aPosition');
  const aNormal = gl.getAttribLocation(program, 'aNormal');
  const mat = gl.getUniformLocation(program, 'mat');

  const vertices = new Float32Array([
    // 0123
    1, 1, 1,
    -1, 1, 1,
    -1,-1, 1,
    1,-1, 1,
    // 0345
    1, 1, 1,
    1,-1, 1,
    1,-1,-1,
    1, 1,-1,
    // 0156
    1, 1, 1,
    1, 1, -1,
    -1, 1,-1,
    -1, 1,1,
    // 1267
    -1, 1, 1,
    -1,1, -1,
    -1, -1,-1,
    -1,-1,1,
    // 2347
    -1,-1, 1,
    1,-1, 1,
    1,-1,-1,
    -1,-1,-1,
    // 4567
    1,-1,-1,
    1, 1,-1,
    -1, 1,-1,
    -1,-1,-1,
  ])

  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(aPosition)

  // 法向量
  const normals = new Float32Array([
    0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,
    0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,
    -1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,
    1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,
    0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,
    0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,
  ])
  const normalBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(aNormal)

  const indeces = new Uint8Array([
    0,1,2,0,2,3,
    4,5,6,4,6,7,
    8,9,10,8,10,11,
    12,13,14,12,14,15,
    16,17,18,16,18,19,
    20,21,22,20,22,23,
  ])
  const indexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);

  const start = 0;
  const end = 200;
  const fogColor = new Float32Array([0.0,0.0,0.0]);
  const fogDist = new Float32Array([start, end]);

  const uFogColor = gl.getUniformLocation(program, 'uFogColor');
  const uFogDist = gl.getUniformLocation(program, 'uFogDist');

  gl.uniform3fv(uFogColor, fogColor);

  const vm = getViewMatrix(3,3,5,0.0,0.0,0.0,0.0,0.6,0.0);
  const perspective = getPerspectiveMatrix(30, ctx.width / ctx.height, 100, 1);
  gl.enable(gl.DEPTH_TEST);
  gl.uniformMatrix4fv(mat, false, mixMatrix(perspective, vm));


  function draw() {
    fogDist[1] -= 1;
    if (fogDist[1] < start) {
      fogDist[1] = end;
    }
    gl.uniform2fv(uFogDist, fogDist);

    gl.clearColor(0.0,0.0,0.0,1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.enable(gl.DEPTH_TEST);
    gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);

    requestAnimationFrame(draw)
  }

  draw();
</script>

请添加图片描述

二、半透明

半透明物体是指在光线透过时,可以看到模糊的影像,但不完全透明的物体。这种物体通常具有一定的透明性,但不完全透明,仍然存在某种程度上的不透明性。

半透明物体的意义在于它可以在不影响视线的情况下,让光线穿过物体,使得我们能够观察到物体背后或者内部的部分。这种特性在许多应用中都非常有用,比如建筑物中的玻璃窗、医疗设备中的透明塑料、以及电子设备中的显示屏。此外,半透明物体还具有一定的艺术效果,可以让物体呈现出半透明的质感和神秘感,增强视觉体验。

半透明的实现流程

  1. 在片元着色器中定义透明度属性,并使用gl_FragColor来设置颜色和透明度值。
  2. 使用gl.enable(gl.BLEND)启用混合模式,这样可以将透明物体正确地混合到场景中。
  3. 使用gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)设置混合函数,指定源颜色因子和目标颜色因子,以便正确地混合透明物体。
  4. 在渲染透明物体时,确保先渲染不透明的物体,再渲染半透明的物体。这样可以避免混合时受到前面物体的影响而导致不正确的结果。

gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA):源因子和目标因子是可以通过gl.blendFunc函数来进行设置的,gl.blendFunc有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。
gl.ZERO:表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
gl.ONE:表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
gl.SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
gl.DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
gl.ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
gl.ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。

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

    canvas{
      margin: 50px auto 0;
      display: block;
      background: yellow;
    }
  </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 aNormal;
    varying vec4 vColor;

    uniform mat4 mat;
    void main() {
      // 定义点光源的颜色
      vec3 uPointLightColor = vec3(1.0,1.0,0.0);

      // 点光源的位置
      vec3 uPointLightPosition = vec3(-5.0,6.0,10.0);

      // 环境光
      vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);

      // 物体表面的颜色
      vec4 aColor = vec4(1.0,0.0,0.0,1.0);

      // 顶点的世界坐标
      vec4 vertexPosition = mat * aPosition;

      // 点光源的方向
      vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));

      // 环境反射
      vec3 ambient = uAmbientLightColor * vec3(aColor);

      // 计算入射角 光线方向和法线方向的点积
      float dotDeg = dot(lightDirection, vec3(aNormal));

      // 漫反射光的颜色
      vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;

      gl_Position = vertexPosition;
      vColor = vec4(ambient + diffuseColor, 0.5);
    }
  `; // 顶点着色器

  const FRAGMENT_SHADER_SOURCE = `
    precision lowp float;
    varying vec4 vColor;

    void main() {
      gl_FragColor = vColor;
    }
  `; // 片元着色器

  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

  const aPosition = gl.getAttribLocation(program, 'aPosition');
  const aNormal = gl.getAttribLocation(program, 'aNormal');
  const mat = gl.getUniformLocation(program, 'mat');

  const vertices = new Float32Array([
    // 0123
    1, 1, 1,
    -1, 1, 1,
    -1,-1, 1,
    1,-1, 1,
    // 0345
    1, 1, 1,
    1,-1, 1,
    1,-1,-1,
    1, 1,-1,
    // 0156
    1, 1, 1,
    1, 1, -1,
    -1, 1,-1,
    -1, 1,1,
    // 1267
    -1, 1, 1,
    -1,1, -1,
    -1, -1,-1,
    -1,-1,1,
    // 2347
    -1,-1, 1,
    1,-1, 1,
    1,-1,-1,
    -1,-1,-1,
    // 4567
    1,-1,-1,
    1, 1,-1,
    -1, 1,-1,
    -1,-1,-1,
  ])

  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(aPosition)

  // 法向量
  const normals = new Float32Array([
    0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,
    0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,
    -1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,
    1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,
    0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,
    0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,
  ])
  const normalBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
  gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(aNormal)

  const indeces = new Uint8Array([
    0,1,2,0,2,3,
    4,5,6,4,6,7,
    8,9,10,8,10,11,
    12,13,14,12,14,15,
    16,17,18,16,18,19,
    20,21,22,20,22,23,
  ])
  const indexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);

  gl.enable(gl.BLEND);

  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

  const vm = getViewMatrix(3,3,5,0.0,0.0,0.0,0.0,0.6,0.0);
  const perspective = getPerspectiveMatrix(30, ctx.width / ctx.height, 100, 1);

  gl.uniformMatrix4fv(mat, false, mixMatrix(perspective, vm));
  gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);

</script>

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码上生花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值