【愚公系列】2023年08月 WEBGL专题-光的使用


前言

WebGL中的光与现实世界中的光类似,是一种物理光线模拟。WebGL使用光线追踪算法来模拟光在3D场景中的反射、折射和散射等现象,以实现逼真的光影效果。

在WebGL中,光源是光的来源,可以模拟不同类型的光源,如平行光、点光源、聚光灯等。光线从光源发出,经过场景中的各个物体后,被摄像机捕捉,生成最终的渲染图像。

通过调整光源的位置、颜色、强度等参数,可以在WebGL中实现不同的光影效果,如明暗对比、高光反射、镜面反射等。这些光影效果可以增强3D场景的真实感和表现力。

一、光的使用

1.光的类型

1.1 点光源

在WEBGL中,点光源是一种光源类型,它模拟了一个点光源所产生的光照效果。点光源通常被用于模拟距离比较近的光源,如灯泡、蜡烛等。与其他类型的光源相比,点光源的特点是能够让它照亮的物体在距离光源越远的地方越暗,同时由于它只能照亮一些特定的空间位置,所以不适合照射整个场景或某个区域。点光源可以通过设置其位置、强度和颜色等属性来控制其光照效果,并可以与其他类型的光源组合使用,从而实现更加复杂的照明效果。在WEBGL中,通过使用点光源,可以在3D场景中模拟各种不同的光照效果,从而使场景更加真实和生动。

在这里插入图片描述

1.2 平行光

WEBGL(Web Graphics Library)是一种用于在Web浏览器上渲染交互式3D和2D图形的JavaScript API。平行光是WEBGL中的一种光源类型,它是平行于一条虚拟光线的光源,类似于太阳光线,其所有光线方向都是相同的,因此所有的阴影投射都是平行的。

在WEBGL中,我们可以使用gl.LIGHT0来创建平行光源。平行光源有一个方向,这个方向决定了所有的光线向哪个方向发射。创建平行光源需要设置它的位置和方向,通常我们使用gl.uniformMatrix4fv()函数来设置平行光源的位置和方向矩阵。

平行光源可以用于模拟室外场景中的太阳光线,它可以创建出阴影效果,并且也可以用于创建一些特殊效果,比如材质的高光部分。

在这里插入图片描述

1.3 环境光

在WebGL中,环境光(Ambient Light)是一种广泛存在于整个场景中的光照,是对物体表面的基础亮度补充。环境光不会直接照亮物体的表面,而是提供一种背景光,使场景看起来更加自然。在WebGL中,环境光通常由固定方向或固定颜色的灯光所产生,也可以通过调整特定材质的环境颜色来改变环境光的颜色和强度。环境光也可以混合到其他类型的光照中,以创建更加复杂的光照效果。

在这里插入图片描述

2.光的反射

1、光线方向

“光线方向”指的是⼊射方向的反方向,即从入射点指向光源方向。

在这里插入图片描述

2.1 环境反射

在WebGL中,光的环境反射是指在物体表面所反射的环境光线对物体外观的影响。环境光线是指从各个方向反射的周围光线的平均值。它通常是非常微弱的,但是当它集中在表面细节上时,它可以显著影响物体的外观。

环境反射可以通过设置物体的材质属性来实现。在WebGL中,可以使用材质颜色(ambient color)属性来控制环境反射的影响。这个属性通常被设置为物体的基本颜色,它会被乘以环境光线的颜色来计算最终颜色。

除了材质颜色之外,WebGL还支持使用环境贴图(ambient map)来模拟环境反射。环境贴图是一张图像,其中包含了环境光线的颜色和辐射度信息。在渲染物体时,环境贴图的信息会被用来计算环境反射的影响。

环境反射可以使物体看起来更真实和逼真,因为它可以模拟周围光线的影响。它通常被用于创建高质量的场景和游戏中的现实感效果。

环境反射是针对环境光而言的,在环境反射中,环境光照射物体是各方面均匀、强度相等的,反射的方向就是入射光的反方向,最终物体的颜色只跟入射光颜色和基底色有关。

<环境反射光颜色>=<入射光颜色>*表面基底色>

在这里插入图片描述

2.2 漫反射

WebGL中光的漫反射是指,当光线照射在表面上时,由于表面的粗糙程度和物体本身的材质等因素,部分光线会被散射到各个方向,而非像镜面反射一样按同一角度反射出去。这种散射过程就是漫反射。漫反射的效果更加自然,可以使物体表面更加真实,增强场景的立体感。在WebGL中,漫反射的实现可以通过计算光线与表面法线之间的夹角以及表面材质的散射系数来实现。

漫反射中反射光的颜色除了取决于入射光的颜色、表面的基底色,还有入射光与物体表面的法向量形成的入射角。

入射角a 可以通过 光线方向和法线方向 的点积来计算:
1<光线方向>·<法线方向> = cosa
2<漫反射光颜色>=<入射光颜色>*<表面基底色>(<光线方向>*<法
线方向>)

在这里插入图片描述

2.3 混合反射

当漫反射和环境反射同时存在时,将两者加起来,就会得到物体最终被观察到的颜色

<表面的反射光颜色> = <漫反射光颜色>+<环境反射光颜色>

3.案例

3.1 固定法向量

<!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;

    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, aColor.a);
    }
  `; // 顶点着色器

  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);

  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));
  gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);

</script>

在这里插入图片描述

3.2 计算法向量

<!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;

    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, aColor.a);
    }
  `; // 顶点着色器

  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)

  // 通过顶点来计算法向量 start =============
  const res = []
  for (let i = 0; i < vertices.length; i+=12) {
      
      
    const item = cross(
      [
        vertices[i],
        vertices[i+1],
        vertices[i+2]
      ],
      [
        vertices[i+3],
        vertices[i+4],
        vertices[i+5]
      ])
    for (let j = 0; j < 4; j++) {
      
      
      res.push(...item)
    }
  }
  // 法向量
  const normals = new Float32Array(res)
  // 通过顶点来计算法向量  end =============

  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 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));
  gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);

</script>

在这里插入图片描述

猜你喜欢

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