「译」WebGL 系列 01 初识 WebGL 使用

码农天地 -
「译」WebGL 系列 01 初识 WebGL 使用
原文地址:Day 1. Intro原文作者:Andrei Lesnitsky

这是 WebGL 系列的第 1 天教程,每天都有新文章发布。

订阅或者加入邮件列表以便及时获取更新内容。

源代码在这里

欢迎来到 WebGL 系列教程的第一天。在本文中,我们将介绍高级渲染概念,这些概念对后面实际使用 WebGL API 来说是很重要的。

WebGL API 通常被视为 3D 渲染 API,这其实是不正确的,那么 WebGL 是做什么的呢?

为了回答这个问题,让我们尝试用平面画布 canvas 2d 渲染某物。

我们先简单准备一个 html

? index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>WebGL Month</title>
  </head>
  <body></body>
</html>

还有 canvas 画布

? index.html

      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <title>WebGL Month</title>
    </head>
-   <body></body>
+   <body>
+     <canvas></canvas>
+   </body>
  </html>

不要忘记心爱的 JS

? index.html

    </head>
    <body>
      <canvas></canvas>
+     <script src="./src/canvas2d.js"></script>
    </body>
  </html>

? src/canvas2d.js

console.log('Hello WebGL month');

Let's grab a reference to canvas and get 2d context
接着我们得到对 canvas 的引用,并获取对应的上下文

? src/canvas2d.js

- console.log('Hello WebGL month');+ console.log('Hello WebGL month');
+ 
+ const canvas = document.querySelector('canvas');
+ const ctx = canvas.getContext('2d');

非常简单的操作,就像画一个黑色的矩形

? src/canvas2d.js

  
  const canvas = document.querySelector('canvas');
  const ctx = canvas.getContext('2d');
+ 
+ ctx.fillRect(0, 0, 100, 50);

是吧,这很简单对吧?
但是,让我们考虑一下这一行代码的实际干了什么。
它用黑色填充矩形内部的每个像素。

有没有办法不使用 fillRect 就做到这一点呢?
答案是肯定的。

让我们实现自己的版本

? src/canvas2d.js

  const canvas = document.querySelector('canvas');
  const ctx = canvas.getContext('2d');
  
- ctx.fillRect(0, 0, 100, 50);
+ function fillRect(top, left, width, height) {
+ 
+ }

因此,基本上每个像素都是 4 个整数编码的颜色。R、G、B 通道和 Alpha

要存储有关画布的每个像素信息,我们需要一个 Uint8ClampedArray 数组。
该数组的大小为 canvas.width * canvas.height(像素数)* 4(每个像素有 4 个通道)。

? src/canvas2d.js

  const ctx = canvas.getContext('2d');
  
  function fillRect(top, left, width, height) {
- 
+     const pixelStore = new Uint8ClampedArray(canvas.width * canvas.height * 4);
  }

现在我们可以用颜色填充每个像素存储。请注意,与 CSS 不同,alpha 组件也在范围内

? src/canvas2d.js

  
  function fillRect(top, left, width, height) {
      const pixelStore = new Uint8ClampedArray(canvas.width * canvas.height * 4);
+ 
+     for (let i = 0; i < pixelStore.length; i += 4) {
+         pixelStore[i] = 0; // r
+         pixelStore[i + 1] = 0; // g
+         pixelStore[i + 2] = 0; // b
+         pixelStore[i + 3] = 255; // alpha
+     }
  }

但是我们如何渲染这个像素呢?有一个特殊的 canvas 能够渲染

? src/canvas2d.js

          pixelStore[i + 2] = 0; // b
          pixelStore[i + 3] = 255; // alpha
      }
+ 
+     const imageData = new ImageData(pixelStore, canvas.width, canvas.height);
+     ctx.putImageData(imageData, 0, 0);
  }
+ 
+ fillRect();

哇?,我们使用一个手动迭代每个像素的颜色,对画布进行填充,但是我们没有考虑传递参数,需要对其进行修复。

计算矩形内的像素索引

? src/canvas2d.js

  const canvas = document.querySelector('canvas');
  const ctx = canvas.getContext('2d');
  
+ function calculatePixelIndices(top, left, width, height) {
+     const pixelIndices = [];
+ 
+     for (let x = left; x < left + width; x++) {
+         for (let y = top; y < top + height; y++) {
+             const i =
+                 y * canvas.width * 4 + // pixels to skip from top
+                 x * 4; // pixels to skip from left
+ 
+             pixelIndices.push(i);
+         }
+     }
+ 
+     return pixelIndices;
+ }
+ 
  function fillRect(top, left, width, height) {
      const pixelStore = new Uint8ClampedArray(canvas.width * canvas.height * 4);
  

同时遍历这些像素,而不是整个画布。

? src/canvas2d.js

  
  function fillRect(top, left, width, height) {
      const pixelStore = new Uint8ClampedArray(canvas.width * canvas.height * 4);
+     
+     const pixelIndices = calculatePixelIndices(top, left, width, height);
  
-     for (let i = 0; i < pixelStore.length; i += 4) {
+     pixelIndices.forEach((i) => {
          pixelStore[i] = 0; // r
          pixelStore[i + 1] = 0; // g
          pixelStore[i + 2] = 0; // b
          pixelStore[i + 3] = 255; // alpha
-     }
+     });
  
      const imageData = new ImageData(pixelStore, canvas.width, canvas.height);
      ctx.putImageData(imageData, 0, 0);
  }
  
- fillRect();
+ fillRect(10, 10, 100, 50);

酷 ? 我们刚刚重新实现了 fillRect! 但是,它与 WebGL 有何共同点?

这正是 WebGL API 所做的 – 它计算每个像素的颜色,并用计算出的颜色进行填充。

下一步是什么?

在下一篇文章中,我们将开始使用 WebGL API 并呈现 WebGL “ Hello world”。明天见!

作业

扩展自定义 fillRect 以支持自定义颜色。

特别申明:本文内容来源网络,版权归原作者所有,如有侵权请立即与我们联系(cy198701067573@163.com),我们将及时处理。

Tags 标签

加个好友,技术交流

1628738909466805.jpg