【愚公系列】2023年08月 WEBGL专题-3D特效-雾化


前言

在3D图形渲染中,雾化是一种用于创建深度感的技术。它模拟了真实世界中的雾气效果,使远距离的物体看起来模糊不清,并且与背景融合在一起,从而增强了场景的真实感。

实现3D中的雾化技术,通常通过在场景中添加一层透明的雾气效果,即雾层。这个雾层可以是线性的或指数的,具体的数学公式可以用来计算每个像素的雾化强度。这种技术使得远距离的物体看起来像是遮盖在一层雾气中,从而增强了场景的深度感。

雾化技术在电影、游戏等领域广泛应用,能够提高场景的视觉效果和真实感。

一、雾化

1.概念

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

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

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

2.雾化的实现流程

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

  1. 创建shader:在WebGL中,我们使用着色器(shader)来描述如何绘制一个物体,因此,我们需要创建雾化的shader。在shader中,我们需要定义雾化程度的变量,以及计算每个像素颜色时需要使用的公式等。

  2. 定义雾化区域:定义哪些区域需要雾化。对于这一步,我们可以依据相机的距离,将远离相机的物体进行雾化处理,或者根据场景中物体的坐标确定需要雾化的区域。

    扫描二维码关注公众号,回复: 16101700 查看本文章
  3. 计算雾化程度:计算每个像素的雾化程度,这个程度可以用雾化公式进行计算,可以使用线性雾化公式或者指数雾化公式等。

  4. 应用雾化:将计算得到的雾化程度,应用到相应的像素上,最终实现雾化效果。

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

在这里插入图片描述

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

3.案例

<!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;
      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 = getPerspective(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>

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/aa2528877987/article/details/132149173