WebGL - 快速指南


WebGL - 简介

几年前,Java 应用程序(作为小程序和 JOGL 的组合)用于通过对 GPU(图形处理单元)进行寻址来处理 Web 上的 3D 图形。由于小程序需要 JVM 才能运行,因此依赖 Java 小程序变得很困难。几年后,人们停止使用 Java 小程序。

Adobe 提供的 Stage3D API(Flash、AIR)提供了 GPU 硬件加速架构。使用这些技术,程序员可以在 Web 浏览器以及 IOS 和 Android 平台上开发具有 2D 和 3D 功能的应用程序。由于 Flash 是专有软件,因此并未用作 Web 标准。

2011年3月,WebGL发布。它是一个无需 JVM 即可运行的开放软件。它完全由网络浏览器控制。

新版本的 HTML 5 具有多种支持 3D 图形的功能,例如 2D Canvas、WebGL、SVG、3D CSS 转换和 SMIL。在本教程中,我们将介绍 WebGL 的基础知识。

什么是OpenGL?

OpenGL(开放图形库)是一个跨语言、跨平台的 2D 和 3D 图形 API。它是命令的集合。OpenGL4.5是OpenGL的最新版本。下表列出了一组与 OpenGL 相关的技术。

应用程序编程接口 使用的技术
OpenGL ES 它是嵌入式系统(包括控制台、电话、电器和车辆)上 2D 和 3D 图形的库。OpenGL ES 3.1 是其最新版本。它由 Khronos Group www.khronos.org维护
乔格鲁 它是 OpenGL 的 Java 绑定。JOGL 4.5 是其最新版本,由 jogamp.org 维护
网页GL 它是 OpenGL 的 JavaScript 绑定。WebGL 1.0 是其最新版本,由khronos 组维护。
OpenGLSL OpenGL 着色语言。它是一种编程语言,是 OpenGL 2.0 及更高版本的配套语言。它是核心 OpenGL 4.4 规范的一部分。它是专门为嵌入式系统(例如手机和平板电脑上的嵌入式系统)量身定制的 API。

注意- 在 WebGL 中,我们使用 GLSL 来编写着色器。

什么是 WebGL?

WebGL(Web Graphics Library)是Web上3D图形的新标准,它是为渲染2D图形和交互式3D图形而设计的。它源自 OpenGL 的 ES 2.0 库,该库是用于手机和其他移动设备的低级 3D API。WebGL 提供与 ES 2.0(嵌​​入式系统)类似的功能,并且在现代 3D 图形硬件上表现良好。

它是一个可与 HTML5 一起使用的 JavaScript API。WebGL 代码编写在 HTML5 的 <canvas> 标签内。该规范允许互联网浏览器访问使用它们的计算机上的图形处理单元 (GPU)。

谁开发了 WebGL

一位名叫Vladimir Vukicevic的美籍塞尔维亚软件工程师完成了基础工作并领导了 WebGL 的创建

  • 2007 年,Vladimir 开始为 HTML 文档的 Canvas 元素开发OpenGL原型。

  • 2011 年 3 月,Kronos Group 创建了 WebGL。

渲染

渲染是使用计算机程序从模型生成图像的过程。在图形中,虚拟场景是使用通过渲染程序传递的几何、视点、纹理、照明和阴影等信息来描述的。该渲染程序的输出将是数字图像。

有两种类型的渲染 -

  • 软件渲染- 所有渲染计算都是在 CPU 的帮助下完成的。

  • 硬件渲染- 所有图形计算均由 GPU(图形处理单元)完成。

渲染可以在本地或远程完成。如果要渲染的图像太复杂,则可以在具有渲染复杂场景所需的足够硬件资源的专用服务器上远程完成渲染。它也称为基于服务器的渲染。渲染也可以由CPU本地完成。它被称为基于客户端的渲染

WebGL 采用基于客户端的渲染方法来渲染 3D 场景。获取图像所需的所有处理都是使用客户端的图形硬件在本地执行的。

图形处理器

根据 NVIDIA 的说法,GPU 是“具有集成变换、光照、三角形设置/裁剪和渲染引擎的单芯片处理器,每秒至少能够处理 1000 万个多边形”。与具有针对顺序处理而优化的几个核心的多核处理器不同,GPU 由数千个可有效处理并行工作负载的较小核心组成。因此,GPU 加速了帧缓冲区(RAM 的一部分,包含完整帧数据)中图像的创建,用于输出到显示器。

CPU和GPU

GPU 加速计算

在 GPU 加速计算中,应用程序被加载到 CPU 中。每当遇到代码的计算密集型部分时,该部分代码就会被加载并在 GPU 上运行。它使系统能够有效地处理图形。

GPU 加速计算

GPU 将有一个单独的内存,它一次运行一小部分代码的多个副本。GPU处理本地内存中的所有数据,而不是中央内存。因此,需要GPU处理的数据应该加载/复制到GPU内存中,然后进行处理。

在具有上述架构的系统中,应该减少CPU和GPU之间的通信开销,以实现3D程序的更快处理。为此,我们必须复制所有数据并将其保留在GPU上,而不是重复与GPU通信。

支持的浏览器

下表显示了支持 WebGL 的浏览器列表 -

网络浏览器

浏览器名称 版本 支持
互联网探索者_ _ 11岁及以上 完整支持
谷歌浏览器 39岁及以上 完整支持
苹果浏览器 8 完整支持
火狐浏览器 36岁及以上 部分支持
歌剧 27岁及以上 部分支持

移动浏览器

浏览器名称 版本 支持
安卓版 Chrome 42 部分支持
安卓浏览器 40 部分支持
iOS 浏览器 8.3 完整支持
迷你歌剧院 8 不支持
黑莓浏览器 10 完整支持
IE手机 10 部分支持

WebGL 的优点

以下是使用 WebGL 的优点 -

  • JavaScript 编程- WebGL 应用程序是用 JavaScript 编写的。使用这些应用程序,您可以直接与 HTML 文档的其他元素进行交互。您还可以使用其他 JavaScript 库(例如 JQuery)和 HTML 技术来丰富 WebGL 应用程序。

  • 增加对移动浏览器的支持- WebGL 还支持移动浏览器,例如 iOS safari、Android 浏览器和 Android 版 Chrome。

  • 开源- WebGL 是开源的。您可以访问该库的源代码并了解它的工作原理和开发方式。

  • 无需编译- JavaScript 是一个半编程和半 HTML 组件。要执行此脚本,无需编译文件。相反,您可以使用任何浏览器直接打开文件并检查结果。由于WebGL应用程序是使用JavaScript开发的,因此也无需编译WebGL应用程序。

  • 自动内存管理- JavaScript 支持自动内存管理。无需手动分配内存。WebGL继承了JavaScript的这一特性。

  • 易于设置- 由于 WebGL 集成在 HTML 5 中,因此无需额外设置。要编写 WebGL 应用程序,您只需要一个文本编辑器和一个 Web 浏览器。

环境设置

无需为WebGL设置不同的环境。支持 WebGL 的浏览器有自己的内置 WebGL 设置。

WebGL - Html5 Canvas 概述

为了在 Web 上创建图形应用程序,HTML-5 提供了一组丰富的功能,例如 2D Canvas、WebGL、SVG、3D CSS 转换和 SMIL。为了编写 WebGL 应用程序,我们使用 HTML-5 现有的 canvas 元素。本章概述了 HTML-5 2D canvas 元素。

HTML5 画布

HTML-5 <canvas>提供了一个简单而强大的选项来使用 JavaScript 绘制图形。它可以用来绘制图表、制作照片合成或制作简单(而不是那么简单)的动画。

这是一个简单的<canvas>元素,只有两个特定属性widthheight以及所有核心 HTML-5 属性,如 id、name 和 class。

句法

HTML canvas 标签的语法如下。您必须在双引号(“”)内提及画布的名称。

<canvas id = "mycanvas" width = "100" height = "100"></canvas>

画布属性

canvas标签具有三个属性,即id、width和height。

  • Id - Id 表示文档对象模型 (DOM)中画布元素的标识符。

  • 宽度- 宽度表示画布的宽度。

  • 高度- 高度表示画布的高度。

这些属性决定了画布的大小。如果程序员没有在 canvas 标签下指定它们,则 Firefox、Chrome 和 Web Kit 等浏览器默认提供大小为 300 × 150 的 canvas 元素。

示例 - 创建画布

以下代码展示了如何创建画布。我们使用 CSS 为画布提供彩色边框。

<html>
   <head>
      <style>
         #mycanvas{border:1px solid red;}
      </style>
   </head>
   <body>
      <canvas id = "mycanvas" width = "100" height = "100"></canvas>
   </body>
</html>

执行时,上述代码将产生以下输出 -

渲染上下文

<canvas> 最初是空白的。要在画布元素上显示某些内容,我们必须使用脚本语言。该脚本语言应该访问渲染上下文并在其上进行绘制。

canvas 元素有一个名为getContext()的 DOM 方法,用于获取渲染上下文及其绘图函数。此方法采用一个参数,即上下文2d的类型。

需要编写以下代码来获取所需的上下文。您可以将此脚本编写在 body 标记内,如下所示。

<!DOCTYPE HTML>
<html>
   <body>
      <canvas id = "mycanvas" width = "600" height = "200"></canvas>

      <script>
         var canvas = document.getElementById('mycanvas');
         var context = canvas.getContext('2d');
			
         context.font = '20pt Calibri';
         context.fillStyle = 'green';
         context.fillText('Welcome to Tutorialspoint', 70, 70);
      </script>
   </body>
</html>

执行时,上述代码将产生以下输出 -

有关 HTML-5 2D Canvas 的更多示例,请查看以下链接HTML-5 Canvas

WebGL 上下文

HTML5 Canvas 还用于编写 WebGL 应用程序。要在canvas元素上创建WebGL渲染上下文,您应该将字符串experimental-webgl而不是2d传递给canvas.getContext()方法。某些浏览器仅支持“ webgl ”。

<!DOCTYPE html>
<html>
   <canvas id = 'my_canvas'></canvas>
	
   <script>
      var canvas = document.getElementById('my_canvas');
      var gl = canvas.getContext('experimental-webgl');
      gl.clearColor(0.9,0.9,0.8,1);
      gl.clear(gl.COLOR_BUFFER_BIT);
   </script>
</html>

执行时,上述代码将产生以下输出 -

WebGL - 基础知识

WebGL 主要是低级光栅化 API,而不是 3D API。要使用 WebGL 绘制图像,您必须传递表示图像的向量。然后,它使用 OpenGL SL 将给定矢量转换为像素格式,并将图像显示在屏幕上。编写 WebGL 应用程序涉及一系列步骤,我们将在本章中解释这些步骤。

WebGL - 坐标系

就像任何其他 3D 系统一样,WebGL 中将有 x、y 和 z 轴,其中z轴表示深度。WebGL 中的坐标仅限于 (1, 1, 1) 和 (-1, -1, - 1)。这意味着 - 如果您将投影 WebGL 图形的屏幕视为一个立方体,则立方体的一个角将为 (1, 1, 1),对角将为 (-1, -1, -1)。WebGL 不会显示超出这些边界绘制的任何内容。

下图描述了 WebGL 坐标系。z 轴表示深度。z 的正值表示对象靠近屏幕/观看者,而 z 的负值表示对象远离屏幕。同样,x 的正值表示对象位于屏幕的右侧,负值表示对象位于屏幕的左侧。类似地,y 的正值和负值表示对象是在屏幕的顶部还是底部。

WebGL 坐标系

WebGL 图形

获取画布对象的 WebGL 上下文后,您可以开始使用 JavaScript 中的 WebGL API 绘制图形元素。

以下是开始使用 WebGL 之前需要了解的一些基本术语。

顶点

通常,为了绘制多边形等对象,我们在平面上标记点并将它们连接起来形成所需的多边形。顶点定义 3D 对象边缘连接的点。它由三个浮点值表示,每个浮点值分别代表 x、y、z 轴。

例子

在下面的示例中,我们绘制一个具有以下顶点的三角形 - (0.5, 0.5), (-0.5, 0.5), (-0.5, -0.5)。

顶点示例

注意- 我们必须使用 JavaScript 数组手动存储这些顶点,并使用顶点缓冲区将它们传递到 WebGL 渲染管道。

指数

在 WebGL 中,使用数值来标识顶点。这些数值称为指数。这些索引用于在 WebGL 中绘制网格。

指数

注意- 就像顶点一样,我们使用 JavaScript 数组存储索引,并使用索引缓冲区将它们传递到 WebGL 渲染管道。

数组

与 OpenGL 和 JoGL 不同,WebGL 中没有预定义的方法来直接渲染顶点。我们必须使用 JavaScript 数组手动存储它们。

例子

var vertices = [ 0.5, 0.5, 0.1,-0.5, 0.5,-0.5] 

缓冲器

缓冲区是 WebGL 保存数据的内存区域。有多种缓冲区,即绘图缓冲区、帧缓冲区、顶点缓冲区和索引缓冲区。顶点缓冲区索引缓冲区用于描述和处理模型的几何形状。

顶点缓冲区对象存储有关顶点的数据,而索引缓冲区对象存储有关索引的数据。将顶点存储到数组中后,我们使用这些 Buffer 对象将它们传递到 WegGL 图形管道。

帧缓冲区是图形内存中保存场景数据的一部分。该缓冲区包含表面的宽度和高度(以像素为单位)、每个像素的颜色、深度和模板缓冲区等详细信息。

为了绘制2D或3D对象,WebGL API提供了两种方法,即drawArrays()drawElements()。这两个方法接受一个名为mode的参数,您可以使用该参数选择要绘制的对象。该字段提供的选项仅限于点、线和三角形。

要使用这两种方法绘制 3D 对象,我们必须使用点、线或三角形构造一个或多个原始多边形。此后,使用这些原始多边形,我们可以形成一个网格。

使用原始多边形绘制的 3D 对象称为网格。WebGL 提供了多种绘制 3D 图形对象的方法,但用户通常更喜欢绘制网格。

例子

在下面的示例中,您可以观察到我们使用两个三角形→ {1, 2, 3} 和 {4, 1, 3} 绘制了一个正方形。

网格示例

着色器程序

我们通常使用三角形来构建网格。由于WebGL使用GPU加速计算,因此这些三角形的信息应该从CPU传输到GPU,这需要大量的通信开销。

WebGL 提供了一种减少通信开销的解决方案。由于它使用在 GPU 上运行的 ES SL(嵌入式系统着色器语言),因此我们使用着色器程序(我们使用 OpenGL ES 着色语言/ GLSL编写的程序)编写在客户端系统上绘制图形元素所需的所有程序。

这些着色器是GPU的程序,编写着色器程序的语言是GLSL。在这些着色器中,我们准确定义顶点、变换、材质、灯光和相机如何相互作用以创建特定图像。

简而言之,它是一个实现获取网格像素的算法的片段。我们将在后面的章节中详细讨论着色器。有两种类型的着色器 - 顶点着色器和片段着色器。

顶点着色器

顶点着色器是在每个顶点上调用的程序代码。它用于将几何图形(例如:三角形)从一个地方变换(移动)到另一个地方。它处理每个顶点的数据(per-vertex data),例如顶点坐标、法线、颜色和纹理坐标。

在顶点着色器的ES GL代码中,程序员必须定义属性来处理数据。这些属性指向用 JavaScript 编写的顶点缓冲区对象

可以使用顶点着色器执行以下任务 -

  • 顶点变换
  • 法线变换和归一化
  • 纹理坐标生成
  • 纹理坐标变换
  • 灯光
  • 色料应用

片段着色器(像素着色器)

网格由多个三角形组成,每个三角形的表面称为片段。片段着色器是在每个片段的所有像素上运行的代码。它的编写目的是计算并填充各个像素的颜色。

可以使用片段着色器执行以下任务 -

  • 插值运算
  • 纹理访问
  • 纹理应用
  • 多雾路段
  • 颜色总和
片段着色器

OpenGL ES SL 变量

OpenGL ES SL的完整形式是 OpenGL 嵌入式系统着色语言。为了处理着色器程序中的数据,ES SL 提供了三种类型的变量。它们如下 -

  • 属性- 这些变量保存顶点着色器程序的输入值。属性指向包含每个顶点数据的顶点缓冲区对象。每次调用顶点着色器时,属性都指向不同顶点的VBO。

  • Uniforms - 这些变量保存顶点和片段着色器通用的输入数据,例如灯光位置、纹理坐标和颜色。

  • Varyings - 这些变量用于将数据从顶点着色器传递到片段着色器。

有了这些基础知识,我们现在将继续讨论图形管道。

WebGL - 图形管道

要渲染 3D 图形,我们必须遵循一系列步骤。这些步骤称为图形管道渲染管道。下图描述了 WebGL 图形管道。

图形管线

在下面的部分中,我们将一一讨论管道中每个步骤的作用。

JavaScript

在开发WebGL应用程序时,我们编写Shader语言代码来与GPU进行通信。JavaScript用于编写程序的控制代码,其中包括以下操作 -

  • 初始化 WebGL - JavaScript 用于初始化 WebGL 上下文。

  • 创建数组- 我们创建 JavaScript 数组来保存几何数据。

  • 缓冲区对象- 我们通过将数组作为参数传递来创建缓冲区对象(顶点和索引)。

  • 着色器- 我们使用 JavaScript 创建、编译和链接着色器。

  • 属性- 我们可以使用 JavaScript 创建属性、启用它们并将它们与缓冲区对象关联。

  • 制服- 我们还可以使用 JavaScript 关联制服。

  • 变换矩阵- 使用 JavaScript,我们可以创建变换矩阵。

最初,我们创建所需几何体的数据,并将它们以缓冲区的形式传递给着色器。着色器语言的属性变量指向缓冲区对象,这些对象作为输入传递给顶点着色器。

顶点着色器

当我们通过调用方法drawElements()drawArray()开始渲染过程时,将为顶点缓冲区对象中提供的每个顶点执行顶点着色器。它计算原始多边形每个顶点的位置并将其存储在变化的gl_position中。它还计算其他属性,例如颜色、纹理坐标和通常与顶点关联的顶点。

原始装配

在计算出每个顶点的位置和其他细节之后,下一阶段是图元组装阶段。在这里,三角形被组装并传递到光栅化器。

光栅化

在光栅化步骤中,确定图元的最终图像中的像素。它有两个步骤 -

  • 剔除- 最初确定多边形的方向(正面还是背面?)。所有那些在视图区域中不可见且方向不正确的三角形都将被丢弃。这个过程称为剔除。

  • 裁剪- 如果三角形部分位于视图区域之外,则视图区域之外的部分将被删除。这个过程称为裁剪。

片段着色器

片段着色器得到

  • 来自顶点着色器的不同变量的数据,
  • 来自光栅化阶段的图元,然后
  • 计算顶点之间每个像素的颜色值。

片段着色器存储每个片段中每个像素的颜色值。这些颜色值可以在片段操作期间访问,我们接下来将讨论这一点。

片段操作

确定图元中每个像素的颜色后执行片段操作。这些片段操作可能包括以下内容 -

  • 深度
  • 颜色缓冲混合
  • 抖动

一旦处理完所有片段,就会形成二维图像并显示在屏幕上。帧缓冲区是渲染管线的最终目的地。

片段操作

帧缓冲区

帧缓冲区是图形内存中保存场景数据的一部分。该缓冲区包含表面的宽度和高度(以像素为单位)、每个像素的颜色以及深度和模板缓冲区等详细信息。

WebGL - 示例应用程序

我们已经讨论了 WebGL 和 WebGL 管道(渲染图形应用程序所遵循的过程)的基础知识。在本章中,我们将使用一个示例应用程序来使用 WebGL 创建三角形,并观察应用程序中遵循的步骤。

WebGL应用程序的结构

WebGL应用程序代码是JavaScript和OpenGL着色器语言的组合。

  • 需要 JavaScript 才能与 CPU 通信
  • 与 GPU 通信需要 OpenGL 着色器语言。
WebGL 应用程序结构

申请样本

现在让我们通过一个简单的例子来学习如何使用WebGL绘制一个带有2D坐标的简单三角形。

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>
		
      <script>
         /* Step1: Prepare the canvas and get WebGL context */

         var canvas = document.getElementById('my_Canvas');
         var gl = canvas.getContext('experimental-webgl');

         /* Step2: Define the geometry and store it in buffer objects */

         var vertices = [-0.5, 0.5, -0.5, -0.5, 0.0, -0.5,];

         // Create a new buffer object
         var vertex_buffer = gl.createBuffer();

         // Bind an empty array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         
         // Pass the vertices data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         /* Step3: Create and compile Shader programs */

         // Vertex shader source code
         var vertCode =
            'attribute vec2 coordinates;' + 
            'void main(void) {' + ' gl_Position = vec4(coordinates,0.0, 1.0);' + '}';

         //Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         //Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         //Compile the vertex shader
         gl.compileShader(vertShader);

         //Fragment shader source code
         var fragCode = 'void main(void) {' + 'gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}';

         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode);

         // Compile the fragment shader
         gl.compileShader(fragShader);

         // Create a shader program object to store combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader); 
         
         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /* Step 4: Associate the shader programs to buffer objects */

         //Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         //Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         //point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 2, gl.FLOAT, false, 0, 0);

         //Enable the attribute
         gl.enableVertexAttribArray(coord);

         /* Step5: Drawing the required object (triangle) */

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST); 
         
         // Clear the color buffer bit
         gl.clear(gl.COLOR_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         // Draw the triangle
         gl.drawArrays(gl.TRIANGLES, 0, 3);
      </script>
   </body>
</html>

它将产生以下结果 -

如果您仔细观察上面的程序,我们会按照五个连续的步骤使用 WebGL 绘制一个简单的三角形。步骤如下 -

步骤 1 - 准备画布并获取 WebGL 渲染上下文

我们获取当前的 HTML 画布对象并获取其 WebGL 渲染上下文。

步骤 2 - 定义几何图形并将其存储在缓冲区对象中

我们定义几何体的属性,例如顶点、索引、颜色等,并将它们存储在 JavaScript 数组中。然后,我们创建一个或多个缓冲区对象,并将包含数据的数组传递给相应的缓冲区对象。在示例中,我们将三角形的顶点存储在 JavaScript 数组中,并将该数组传递给顶点缓冲区对象。

步骤 3 - 创建并编译着色器程序

我们编写顶点着色器和片段着色器程序,对其进行编译,并通过链接这两个程序来创建组合程序。

步骤 4 - 将着色器程序与缓冲区对象关联

我们将缓冲区对象和组合的着色器程序关联起来。

步骤 5 - 绘制所需的对象(三角形)

此步骤包括清除颜色、清除缓冲区位、启用深度测试、设置视口等操作。最后,您需要使用以下方法之一绘制所需的图元:drawArrays()或drawElements ( )

本教程将进一步解释所有这些步骤。

WebGL - 上下文

要编写 WebGL 应用程序,第一步是获取 WebGL 渲染上下文对象。该对象与WebGL绘图缓冲区交互并可以调用所有WebGL方法。执行以下操作来获取 WebGL 上下文 -

  • 创建 HTML-5 画布
  • 获取画布ID
  • 获取WebGL

创建 HTML-5 Canvas 元素

在第 5 章中,我们讨论了如何创建 HTML-5 canvas 元素。在 HTML-5 文档的正文中,编写一个画布,为其命名,并将其作为参数传递给属性 id。您可以使用宽度和高度属性(可选)定义画布的尺寸。

例子

以下示例展示了如何创建尺寸为 500 × 500 的画布元素。我们使用 CSS 创建了画布边框以提高可见性。将以下代码复制并粘贴到名为my_canvas.html的文件中。

<!DOCTYPE HTML>
<html>
   <head>
      <style>
         #mycanvas{border:1px solid blue;}
      </style>
   </head>
   <body>
      <canvas id = "mycanvas" width = "300" height = "300"></canvas>
   </body>
</html>

它将产生以下结果 -

获取画布 ID

创建画布后,您必须获取 WebGL 上下文。要获取 WebGL 绘图上下文,首先要做的是获取当前画布元素的 id。

通过调用 DOM(文档对象模型)方法getElementById()获取画布 ID 。该方法接受一个字符串值作为参数,因此我们将当前画布的名称传递给它。

例如,如果画布名称是my_canvas,则获得画布 ID,如下所示:

var canvas = document.getElementById('my_Canvas');

获取 WebGL 绘图上下文

要获取 WebGLRenderingContext 对象(或 WebGL Drawing 上下文对象或简称 WebGL 上下文),请调用当前HTMLCanvasElement的getContext()方法。getContext() 的语法如下 -

canvas.getContext(contextType, contextAttributes);

传递字符串webglexperimental-webgl作为contentType。contextAttributes参数是可选的(继续执行此步骤时,请确保您的浏览器实现WebGL版本 1 (OpenGL ES 2.0))。

以下代码片段展示了如何获取WebGL渲染上下文。这里gl是获取到的上下文对象的引用变量。

var canvas = document.getElementById('my_Canvas');
var gl = canvas.getContext('experimental-webgl');

WebGLContext属性

参数WebGLContextAttributes不是强制性的。此参数提供接受布尔值的各种选项,如下所示 -

先生。 属性和描述
1

Α

如果它的值为 true,它会向画布提供一个 alpha 缓冲区。

默认情况下,其值为 true。

2

深度

如果它的值为 true,您将获得一个绘图缓冲区,其中包含至少 16 位的深度缓冲区。

默认情况下,其值为 true。

3

模版

如果它的值为 true,您将获得一个绘图缓冲区,其中包含至少 8 位的模板缓冲区。

默认情况下,其值为 false。

4

抗锯齿

如果它的值为 true,您将获得一个执行抗锯齿功能的绘图缓冲区。

默认情况下,其值为 true。

5

预乘Alpha

如果它的值为 true,您将获得一个绘图缓冲区,其中包含带有预乘 alpha 的颜色。

默认情况下,其值为 true。

6

保留绘图缓冲区

如果其值为 true,则缓冲区将不会被清除,并将保留其值,直到作者清除或覆盖为止。

默认情况下,其值为 false。

以下代码片段展示了如何使用模板缓冲区创建 WebGL 上下文,该上下文不会执行抗锯齿功能

var canvas = document.getElementById('canvas1');
var context = canvas.getContext('webgl', { antialias: false, stencil: true });

在创建 WebGLRenderingContext 时,会创建一个绘图缓冲区。Context 对象管理 OpenGL 状态并渲染到绘图缓冲区。

WebGL渲染上下文

它是 WebGL 中的主要接口。它代表 WebGL 绘图上下文。该接口包含用于在绘图缓冲区上执行各种任务的所有方法。该接口的属性如下表所示。

先生。 属性和描述
1

帆布

这是对创建此上下文的画布元素的引用。

2

绘图缓冲区宽度

该属性表示绘图缓冲区的实际宽度。它可能与 HTMLCanvasElement 的宽度属性不同。

3

绘图缓冲区高度

该属性表示绘图缓冲区的实际高度。它可能与 HTMLCanvasElement 的 height 属性不同。

WebGL - 几何

获得 WebGL 上下文后,您必须定义图元(要绘制的对象)的几何形状并存储它。在 WebGL 中,我们使用 JavaScript 数组定义几何体的详细信息,例如顶点、索引、图元颜色。要将这些详细信息传递给着色器程序,我们必须创建缓冲区对象并将包含数据的 JavaScript 数组存储(附加)到各个缓冲区中。

注意:稍后,这些缓冲区对象将与着色器程序(顶点着色器)的属性相关联。

定义所需的几何形状

使用顶点绘制的 2D 或 3D 模型称为网格。网格中的每个面称为多边形,多边形由 3 个或更多顶点组成。

要在 WebGL 渲染上下文中绘制模型,您必须使用 JavaScript 数组定义顶点和索引。例如,如果我们想创建一个位于坐标 {(5,5), (-5,5), (-5,-5)} 上的三角形,如图所示,那么您可以创建一个数组顶点为 -

var vertices = [
   0.5,0.5,    //Vertex 1
   0.5,-0.5,   //Vertex 2
   -0.5,-0.5,  //Vertex 3
]; 

几何学

同样,您可以为索引创建一个数组。上述三角形索引的索引将为 [0, 1, 2] 并且可以定义为 -

var indices = [ 0,1,2 ]

为了更好地理解指数,请考虑更复杂的模型,例如 square。我们可以将一个正方形表示为一组两个三角形。如果 (0,3,1) 和 (3,1,2) 是我们打算用来绘制正方形的两个三角形,那么索引将定义为 -

var indices = [0,3,1,3,1,2];

几何示例

注意-

对于绘制基元,WebGL 提供了以下两种方法 -

  • drawArrays() - 使用此方法时,我们使用 JavaScript 数组传递图元的顶点。

  • drawElements() - 使用此方法时,我们使用 JavaScript 数组传递图元的顶点和索引。

缓冲对象

缓冲区对象是WebGL提供的一种机制,表示系统中分配的内存区域。在这些缓冲区对象中,您可以存储要绘制的模型的数据,对应于顶点、索引、颜色等。

使用这些缓冲区对象,您可以通过其属性变量之一将多个数据传递给着色器程序(顶点着色器)。由于这些缓冲区对象驻留在 GPU 内存中,因此可以直接渲染它们,从而提高性能。

为了处理几何图形,有两种类型的缓冲区对象。他们是 -

  • 顶点缓冲区对象(VBO) - 它保存将要渲染的图形模型的每个顶点数据。我们在WebGL中使用顶点缓冲区对象来存储和处理有关顶点的数据,例如顶点坐标、法线、颜色和纹理坐标。

  • 索引缓冲区对象(IBO) - 它保存将要渲染的图形模型的索引(索引数据)。

定义所需的几何图形并将其存储在 JavaScript 数组中后,您需要将这些数组传递到缓冲区对象,数据将从此处传递到着色器程序。将数据存储在缓冲区中需要遵循以下步骤。

  • 创建一个空缓冲区。

  • 将适当的数组对象绑定到空缓冲区。

  • 使用类型化数组之一将数据(顶点/索引)传递到缓冲区。

  • 解除缓冲区绑定(可选)。

创建缓冲区

为了创建一个空的缓冲区对象,WebGL 提供了一个名为createBuffer()的方法。如果创建成功,该方法返回一个新创建的缓冲区对象;否则,如果失败,它将返回空值。

WebGL 作为状态机运行。创建缓冲区后,任何后续缓冲区操作都将在当前缓冲区上执行,直到我们取消绑定它。使用以下代码创建缓冲区 -

var vertex_buffer = gl.createBuffer();

注意- gl是当前 WebGL 上下文的引用变量。

绑定缓冲区

创建空缓冲区对象后,需要为其绑定适当的数组缓冲区(目标)。为此, WebGL 提供了一个名为bindBuffer()的方法。

句法

bindBuffer()方法的语法如下 -

void bindBuffer (enum target, Object buffer)

该方法接受两个参数,下面将讨论它们。

target - 第一个变量是一个枚举值,表示我们要绑定到空缓冲区的缓冲区的类型。您有两个预定义的枚举值作为此参数的选项。他们是 -

  • ARRAY_BUFFER表示顶点数据。

  • ELEMENT_ARRAY_BUFFER表示索引数据。

对象缓冲区- 第二个是上一步中创建的缓冲区对象的引用变量。引用变量可以是顶点缓冲区对象或索引缓冲区对象。

例子

下面的代码片段展示了如何使用bindBuffer()方法。

//vertex buffer
var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

//Index buffer
var Index_Buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);

将数据传递到缓冲区

下一步是将数据(顶点/索引)传递到缓冲区。到目前为止,数据都是数组的形式,在将其传递到缓冲区之前,我们需要将其包装在 WebGL 类型数组之一中。为此, WebGL 提供了一个名为bufferData()的方法。

句法

bufferData() 方法的语法如下 -

void bufferData (enum target, Object data, enum usage)

该方法接受三个参数,下面将讨论它们 -

target - 第一个参数是一个枚举值,表示我们使用的数组缓冲区的类型。此参数的选项是 -

  • ARRAY_BUFFER表示顶点数据

  • ELEMENT_ARRAY_BUFFER表示索引数据

对象数据- 第二个参数是包含要写入缓冲区对象的数据的对象值。这里我们必须使用类型化数组来传递数据。

用法- 此方法的第三个参数是一个枚举变量,指定如何使用缓冲区对象数据(存储的数据)来绘制形状。该参数有以下三个选项。

  • gl.STATIC_DRAW - 数据将被指定一次并使用多次。

  • gl.STREAM_DRAW - 数据将被指定一次并使用几次。

  • gl.DYNAMIC_DRAW - 数据将被重复指定并多次使用。

例子

以下代码片段显示了如何使用bufferData()方法。假设顶点和索引分别是保存顶点和索引数据的数组。

//vertex buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

//Index buffer
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

类型化数组

WebGL 提供了一种称为类型化数组的特殊类型的数组来传输数据元素,例如索引顶点和纹理。这些类型化数组存储大量数据并以本机二进制格式处理它们,从而获得更好的性能。WebGL 使用的类型化数组为 Int8Array、Uint8Array、Int16Array、Uint16Array、Int32Array、UInt32Array、Float32Array 和 Float64Array。

笔记

  • 通常,为了存储顶点数据,我们使用Float32Array;为了存储索引数据,我们使用Uint16Array

  • 您可以使用new关键字创建类型化数组,就像 JavaScript 数组一样。

解除缓冲区绑定

建议您在使用缓冲区后解除绑定。可以通过传递空值来代替缓冲区对象来完成,如下所示。

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

WebGL 提供以下方法来执行缓冲区操作 -

先生。 方法和说明
1

void bindBuffer(枚举目标,对象缓冲区

目标- ARRAY_BUFFER、ELEMENT_ARRAY_BUFFER

2

void bufferData(枚举目标、长尺寸、枚举用法

目标- ARRAY_BUFFER、ELEMENT_ARRAY_BUFFER

用法- STATIC_DRAW、STREAM_DRAW、DYNAMIC_DRAW

3

void bufferData(枚举目标、对象数据、枚举用法

目标用法- 与上面的bufferData相同

4

void bufferSubData(枚举目标,长偏移量,对象数据

目标- ARRAY_BUFFER、ELEMENT_ARRAY_BUFFER

5 对象创建缓冲区()
6 void deleteBuffer(对象缓冲区
7

任何getBufferParameter (枚举target , 枚举pname )

目标- ARRAY_BUFFER、ELEMENT_ ARRAY_BUFFER

pname - BUFFER_SIZE、BUFFER_USAGE

8 bool isBuffer(对象缓冲区

WebGL - 着色器

着色器是在 GPU 上运行的程序。着色器是用 OpenGL ES 着色器语言(称为 ES SL)编写的。ES SL 有自己的变量、数据类型、限定符、内置输入和输出。

数据类型

下表列出了 OpenGL ES SL 提供的基本数据类型。

先生。 类型和描述
1

空白

代表一个空值。

2

布尔值

接受真或假。

3

整数

这是有符号整数数据类型。

4

漂浮

这是浮点标量数据类型。

5

向量2、向量3、向量4

n 分量浮点向量

6

bvec2、bvec3、bvec4

布尔向量

7

ivec2、ivec3、ivec4

有符号整数向量

8

垫2、垫3、垫4

2x2、3x3、4x4 浮点矩阵

9

二维采样器

访问 2D 纹理

10

采样器立方体

访问立方体映射纹理

预选赛

OpenGL ES SL 中有三个主要限定符 -

先生。 限定符和描述
1

属性

此限定符充当顶点着色器和 OpenGL ES 之间针对每个顶点数据的链接。该属性的值会随着顶点着色器的每次执行而变化。

2

制服

此限定符链接着色器程序和 WebGL 应用程序。与属性限定符不同,uniform 的值不会改变。制服是只读的;您可以将它们与任何基本数据类型一起使用来声明变量。

示例- 均匀vec4 lightPosition;

3

变化的

此限定符在顶点着色器和片段着色器之间形成插值数据的链接。它可以与以下数据类型一起使用 - float、vec2、vec3、vec4、mat2、mat3、mat4 或数组。

示例- 改变vec3法线;

顶点着色器

顶点着色器是一段程序代码,在每个顶点上都会被调用。它将几何图形(例如:三角形)从一处变换(移动)到另一处。它处理每个顶点的数据(per-vertex data),例如顶点坐标、法线、颜色和纹理坐标。

在顶点着色器的ES GL代码中,程序员必须定义属性来处理数据。这些属性指向用 JavaScript 编写的顶点缓冲区对象。可以使用顶点着色器和顶点变换来执行以下任务 -

  • 顶点变换
  • 法线变换和归一化
  • 纹理坐标生成
  • 纹理坐标变换
  • 灯光
  • 色料应用

预定义变量

OpenGL ES SL 为顶点着色器提供以下预定义变量 -

先生。 变量和描述
1

highp vec4 gl_Position;

保持顶点的位置。

2

mediump float gl_PointSize;

保存变换后的点大小。该变量的单位是像素。

示例代码

看一下以下顶点着色器的示例代码。它处理三角形的顶点。

attribute vec2 coordinates;

void main(void) {
   gl_Position = vec4(coordinates, 0.0, 1.0);
};

如果你仔细观察上面的代码,我们已经声明了一个属性变量,名称为坐标(此变量将使用getAttribLocation()方法与顶点缓冲区对象关联。属性坐标与着色器程序对象一起作为参数传递给此方法。)

在给定顶点着色器程序的第二步中,定义了gl_position变量。

gl_位置

gl_Position 是预定义变量,仅在顶点着色器程序中可用。它包含顶点位置。在上面的代码中,坐标属性以向量的形式传递。由于顶点着色器是逐顶点操作,因此为每个顶点计算 gl_position 值。

随后,gl_position 值被图元组装、裁剪、剔除和其他在顶点处理结束后对图元进行操作的固定功能操作使用。

我们可以为顶点着色器的所有可能操作编写顶点着色器程序,我们将在本教程中单独讨论。

片段着色器

网格由多个三角形组成,每个三角形的表面称为片段。片段着色器是在每个片段上的每个像素上运行的代码。这是为了计算和填充各个像素的颜色而编写的。可以使用片段着色器执行以下任务 -

  • 插值运算
  • 纹理访问
  • 纹理应用
  • 多雾路段
  • 颜色总和

预定义变量

OpenGL ES SL 为片段着色器提供以下预定义变量 -

先生。 变量和描述
1

mediump vec4 gl_FragCoord ;

保存帧缓冲区内的片段位置。

2

布尔 gl_FrontFacing;

保存属于前置基元的片段。

3

mediump vec2 gl_PointCoord;

保留点内的片段位置(仅限点光栅化)。

4

mediump vec4 gl_FragColor;

保存着色器的输出片段颜色值

5

mediump vec4 gl_FragData[n]

保存颜色附件n的片段颜色。

示例代码

以下片段着色器的示例代码展示了如何将颜色应用于三角形中的每个像素。

void main(void) {
   gl_FragColor = vec4(0, 0.8, 0, 1);
}

在上面的代码中,颜色值存储在变量 中gl.FragColor。片段着色器程序使用固定函数变量将输出传递到管道;FragColor 就是其中之一。该变量保存模型像素的颜色值。

存储和编译着色器程序

由于着色器是独立的程序,因此我们可以将它们编写为单独的脚本并在应用程序中使用。或者,您可以直接以字符串格式存储它们,如下所示。

var vertCode =
   'attribute vec2 coordinates;' +
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 0.0, 1.0);' +
   '}';

编译着色器

编译涉及以下三个步骤 -

  • 创建着色器对象
  • 将源代码附加到创建的着色器对象
  • 编译程序

创建顶点着色器

为了创建空着色器,WebGL 提供了一个名为createShader()的方法。它创建并返回着色器对象。其语法如下 -

Object createShader (enum type)

正如语法中所观察到的,此方法接受预定义的枚举值作为参数。我们对此有两种选择 -

  • gl.VERTEX_SHADER用于创建顶点着色器

  • gl.FRAGMENT_SHADER用于创建片段着色器。

将源附加到着色器

您可以使用方法shaderSource()将源代码附加到创建的着色器对象。其语法如下 -

void shaderSource(Object shader, string source)

该方法接受两个参数 -

  • 着色器- 您必须将创建的着色器对象作为一个参数传递。

  • - 您必须以字符串格式传递着色器程序代码。

编译程序

要编译程序,您必须使用compileShader()方法。其语法如下 -

compileShader(Object shader)

该方法接受着色器程序对象作为参数。创建着色器程序对象后,将源代码附加到该对象并将该对象传递给此方法。

以下代码片段展示了如何创建和编译顶点着色器以及片段着色器来创建三角形。

// Vertex Shader
var vertCode =
   'attribute vec3 coordinates;' +
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
   '}';

var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
 
// Fragment Shader
var fragCode =
   'void main(void) {' +
      ' gl_FragColor = vec4(0, 0.8, 0, 1);' +
   '}';

var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);

联合计划

创建并编译两个着色器程序后,您需要创建一个包含两个着色器(顶点和片段)的组合程序。需要遵循以下步骤 -

  • 创建程序对象
  • 连接两个着色器
  • 链接两个着色器
  • 使用程序

创建程序对象

使用createProgram()方法创建程序对象。它将返回一个空的程序对象。这是它的语法 -

createProgram();

连接着色器

使用方法AttachShader()将着色器附加到创建的程序对象。其语法如下 -

attachShader(Object program, Object shader);

该方法接受两个参数 -

  • 程序- 将创建的空程序对象作为一个参数传递。

  • Shader - 传递已编译的着色器程序之一(顶点着色器、片段着色器)

注意- 您需要使用此方法附加两个着色器。

链接着色器

使用linkProgram()方法链接着色器,方法是传递已附加着色器的程序对象。其语法如下 -

linkProgram(shaderProgram);

使用该程序

WebGL 提供了一个名为useProgram()的方法。您需要将链接的程序传递给它。其语法如下 -

useProgram(shaderProgram);

以下代码片段展示了如何创建、链接和使用组合着色器程序。

var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram); 

关联属性和缓冲区对象

顶点着色器程序中的每个属性都指向一个顶点缓冲区对象。创建顶点缓冲区对象后,程序员必须将它们与顶点着色器程序的属性相关联。每个属性仅指向一个顶点缓冲区对象,从中提取数据值,然后将这些属性传递给着色器程序。

要将顶点缓冲区对象与顶点着色器程序的属性相关联,您必须遵循以下步骤 -

  • 获取属性位置
  • 将属性指向顶点缓冲区对象
  • 启用该属性

获取属性位置

WebGL 提供了一个名为getAttribLocation()的方法,该方法返回属性位置。其语法如下 -

ulong getAttribLocation(Object program, string name)

该方法接受顶点着色器程序对象和顶点着色器程序的属性值。

以下代码片段展示了如何使用此方法。

var coordinatesVar = gl.getAttribLocation(shader_program, "coordinates"); 

这里,shader_program是着色器程序的对象,坐标是t