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 即可运行的开源软件。它完全由 Web 浏览器控制。

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

什么是 OpenGL?

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

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

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

什么是 WebGL?

WebGL(Web 图形库)是 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 场景。获取图像所需的所有处理都是使用客户端的图形硬件在本地执行的。

GPU

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

CPU 和 GPU

GPU 加速计算

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

GPU 加速计算

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

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

支持的浏览器

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

Web 浏览器

浏览器名称 版本 支持
Internet Explorer 11 及以上 完整支持
Google Chrome 39 及以上 完全支持
Safari 8 完全支持
Firefox 36 及以上 部分支持
Opera 27 及以上 部分支持

移动浏览器

浏览器名称 版本 支持
Android 版 Chrome 42 部分支持
Android 浏览器 40 部分支持
iOS Safari 8.3 完全支持
Opera Mini 8 不支持
Blackberry浏览器 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 的现有画布元素。本章概述了 HTML-5 2D 画布元素。

HTML5 Canvas

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

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

语法

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

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

Canvas 属性

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

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

  • Width − Width 表示画布的宽度。

  • Height − Height 表示画布的高度。

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

示例 - 创建画布

以下代码显示如何创建画布。我们使用 CSS 为画布添加彩色边框。

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

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

渲染上下文

<canvas> 最初是空白的。要在 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)。

Vertices Example

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

索引

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

Indices

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

数组

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

示例

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

缓冲区

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

顶点缓冲区对象存储有关顶点的数据,而索引缓冲区对象存储有关索引的数据。将顶点存储到数组中后,我们使用这些缓冲区对象将它们传递给 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。在这些着色器中,我们精确定义了顶点、变换、材质、灯光和相机如何相互作用以创建特定图像。

简而言之,它是一个实现算法以获取网格像素的代码片段。我们将在后面的章节中讨论有关着色器的更多信息。有两种类型的着色器:顶点着色器和片段着色器。

顶点着色器

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

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

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

  • 顶点变换
  • 法线变换和规范化
  • 纹理坐标生成
  • 纹理坐标变换
  • 照明
  • 颜色材料应用

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

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

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

  • 对插值的操作
  • 纹理访问
  • 纹理应用
  • 颜色总和
Fragment Shader

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 中。它还计算通常与顶点关联的其他属性,例如颜色、纹理坐标顶点

图元组装

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

光栅化

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

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

  • 裁剪 − 如果三角形有一部分在视图区域之外,则视图区域之外的部分将被移除。这个过程称为裁剪。

片段着色器

片段着色器获取

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

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

片段操作

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

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

处理完所有片段后,将形成 2D 图像并显示在屏幕上。帧缓冲区是渲染管道的最终目的地。

片段操作

帧缓冲区

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

WebGL - 示例应用程序

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

WebGL 应用程序的结构

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

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

示例应用程序

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

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>
		
      <script>
        /* 步骤 1:准备画布并获取 WebGL 上下文 */
        
        var canvas = document.getElementById('my_Canvas');
        
        var gl = canvas.getContext('experimental-webgl');
        
        /* 步骤 2:定义几何图形并将其存储在缓冲区对象中 */
        
        var vertices = [-0.5, 0.5, -0.5, -0.5, 0.0, -0.5,];
        
        // 创建一个新的缓冲区对象
        var vertex_buffer = gl.createBuffer();
        
        // 将一个空数组缓冲区绑定到它
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 将顶点数据传递到缓冲区
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        // 解除缓冲区绑定
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        /* 步骤 3:创建并编译 Shader 程序 */
        
        // 顶点着色器源代码
        var vertCode =
        'attribute vec2 coordinates;' +
        'void main(void) {' + ' gl_Position = vec4(coordinates,0.0, 1.0);' + '}';
        
        //创建顶点着色器对象
        var vertShader = gl.createShader(gl.VERTEX_SHADER);
        
        //附加顶点着色器源代码
        gl.shaderSource(vertShader, vertCode);
        
        //编译顶点着色器
        gl.compileShader(vertShader);
        
        //片段着色器源代码
        var fragCode = 'void main(void) {' + 'gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}';
        
        // 创建片段着色器对象
        var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
        
        // 附加片段着色器源代码
        gl.shaderSource(fragShader, fragCode);
        
        // 编译片段着色器
        gl.compileShader(fragShader);
        
        // 创建着色器程序对象,用于存储组合着色器程序
        var shaderProgram = gl.createProgram();
        
        // 附加顶点着色器
        gl.attachShader(shaderProgram, vertShader);
        
        // 附加片段着色器
        gl.attachShader(shaderProgram, fragShader);
        
        // 链接两个程序
        gl.linkProgram(shaderProgram);
        
        // 使用组合着色器程序对象
        gl.useProgram(shaderProgram);
        
        /* 步骤 4:将着色器程序关联到缓冲区对象 */
        
        //绑定顶点缓冲区对象
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        //获取属性位置
        var coord = gl.getAttribLocation(shaderProgram, "coordinates");
        
        //将属性指向当前绑定的 VBO
        gl.vertexAttribPointer(coord, 2, gl.FLOAT, false, 0, 0);
        
        //启用属性
        gl.enableVertexAttribArray(coord);
        
        /* 步骤 5:绘制所需对象(三角形) */
        
        // 清除画布
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        
        // 启用深度测试
        gl.enable(gl.DEPTH_TEST);
        
        // 清除颜色缓冲区位
        gl.clear(gl.COLOR_BUFFER_BIT);
        
        // 设置视口
        gl.viewport(0,0,canvas.width,canvas.height);
        
        // 绘制三角形
        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 画布元素

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

示例

以下示例显示如何创建尺寸为 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 绘图上下文对象或简称 WebGL 上下文),请调用当前 HTMLCanvasElementgetContext() 方法。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');

WebGLContextAttributes

参数 WebGLContextAttributes 不是必需的。此参数提供各种接受布尔值的选项,如下所示 −

Sr.No. 属性 &描述
1

Alpha

如果其值为 true,则为画布提供一个 alpha 缓冲区。

默认情况下,其值为 true。

2

depth

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

默认情况下,其值为 true。

3

stencil

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

默认情况下,其值为 false。

4

antialias

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

默认情况下,其值为 true。

5

premultipliedAlpha

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

默认情况下,其值为 true。

6

preserveDrawingBuffer

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

默认情况下,其值为 false。

以下代码片段显示了如何创建带有模板缓冲区的 WebGL 上下文,该上下文不会执行抗锯齿

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

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

WebGLRenderingContext

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

Sr.No. 属性 &描述
1

Canvas

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

2

drawingBufferWidth

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

3

drawingBufferHeight

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

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
]; 
Geometry

类似地,您可以为索引创建一个数组。上述三角形索引的索引将为 [0, 1, 2],可以定义为 −

var indices = [ 0,1,2 ]

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

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

注意

对于绘制图元,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() 方法。

//顶点缓冲区
var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_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() 方法。假设顶点和索引分别是保存顶点和索引数据的数组。

//顶点缓冲区
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

//索引缓冲区
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 数组一样。

解除缓冲区的绑定

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

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

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

Sr.No. 方法和说明
1

void bindBuffer (enum target, Object buffer)

target − ARRAY_BUFFER,ELEMENT_ARRAY_BUFFER

2

void bufferData(enum target, long size, enum usage)

target − ARRAY_BUFFER,ELEMENT_ARRAY_BUFFER

usage − STATIC_DRAW、STREAM_DRAW、DYNAMIC_DRAW

3

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

targetusage − 与上面的 bufferData 相同

4

void bufferSubData(enum target, long offset, Object data)

target − ARRAY_BUFFER,ELEMENT_ARRAY_BUFFER

5 Object createBuffer()
6 void deleteBuffer(Object buffer)
7

any getBufferParameter(enum target, enum pname)

target − ARRAY_BUFFER,ELEMENT_ ARRAY_BUFFER

pname − BUFFER_SIZE,BUFFER_USAGE

8 bool isBuffer(Object buffer)

WebGL - 着色器

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

数据类型

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

Sr.No. 类型 &描述
1

void

表示空值。

2

bool

接受 true 或 false。

3

int

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

4

float

这是一个浮点标量数据类型。

5

vec2、vec3、vec4

n 分量浮点向量

6

bvec2、bvec3、bvec4

布尔向量

7

ivec2、ivec3、ivec4

有符号整数向量

8

mat2、mat3、mat4

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

9

sampler2D

访问 2D 纹理

10

samplerCube

访问立方体映射纹理

限定符

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

Sr.No. 限定符和说明
1

attribute

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

2

uniform

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

示例 − uniform vec4 lightPosition;

3

variing

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

示例 − changing vec3 normal;

顶点着色器

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

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

  • 顶点变换
  • 法线变换和规范化
  • 纹理坐标生成
  • 纹理坐标变换
  • 照明
  • 颜色材料应用

预定义变量

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

Sr.No. 变量 &描述
1

highp vec4 gl_Position;

保存顶点的位置。

2

mediump float gl_PointSize;

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

示例代码

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

attribute vec2 coordinates;

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

如果仔细观察上述代码,我们会发现我们声明了一个名为 coordinates 的属性变量。(此变量将使用方法 getAttribLocation() 与 Vertex Buffer Object 关联。属性 coordinates 将作为参数与着色器程序对象一起传递给此方法。)

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

gl_Position

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

稍后,顶点处理结束后,原始组装、裁剪、剔除和其他对原始进行操作的固定功能操作将使用 gl_position 值。

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

片段着色器

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

  • 对插值的操作
  • 纹理访问
  • 纹理应用
  • 颜色总和

预定义变量

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

Sr.No. 变量 &描述
1

mediump vec4 gl_FragCoord;

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

2

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

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

存储和编译着色器程序

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

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)

此方法接受两个参数 −

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

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

编译程序

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

compileShader(Object 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);

此方法接受两个参数 −

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

  • 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 是着色器程序的对象,coordinates 是顶点着色器程序的属性。

将属性指向 VBO

为了将缓冲区对象分配给属性变量,WebGL 提供了一种名为 vertexAttribPointer() 的方法。以下是此方法的语法 −

void vertexAttribPointer(location, int size, enum type, bool normalized, long stride, long offset)

此方法接受六个参数,下面将进行讨论。

  • Location − 它指定属性变量的存储位置。在此选项下,您必须传递 getAttribLocation() 方法返回的值。

  • Size − 它指定缓冲区对象中每个顶点的组件数。

  • Type −它指定数据的类型。

  • Normalized − 这是一个布尔值。如果为 true,则非浮点数据被标准化为 [0, 1];否则,被标准化为 [-1, 1]。

  • Stride − 它指定不同顶点数据元素之间的字节数,默认步长为零。

  • Offset − 它指定缓冲区对象中的偏移量(以字节为单位),以指示顶点数据从哪个字节存储。如果数据从头开始存储,则 offset 为 0。

以下代码片段显示了如何在程序中使用 vertexAttribPointer()

gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0);

启用属性

激活顶点着色器属性以访问顶点着色器中的缓冲区对象。对于此操作,WebGL 提供了 enableVertexAttribArray() 方法。此方法接受属性的位置作为参数。以下是在程序中使用此方法的方法 −

gl.enableVertexAttribArray(coordinatesVar); 

WebGL - 绘制模型

将缓冲区与着色器关联后,最后一步是绘制所需的图元。WebGL 提供了两种方法来绘制模型,即 drawArrays()drawElements()

drawArrays()

drawArrays() 是用于使用顶点绘制模型的方法。这是它的语法 −

void drawArrays(enum mode, int first, long count)

此方法采用以下三个参数 −

  • mode − 在 WebGL 中,使用图元类型绘制模型。使用模式,程序员必须选择 WebGL 提供的图元类型之一。此选项的可能值为 − gl.POINTS、gl.LINE_STRIP、gl.LINE_LOOP、gl.LINES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN 和 gl.TRIANGLES。

  • first − 此选项指定启用数组中的起始元素。它不能为负值。

  • count −此选项指定要渲染的元素数量。

如果您使用 drawArrays() 方法绘制模型,则 WebGL 在渲染形状时会按照定义顶点坐标的顺序创建几何图形。

示例

如果您想使用 drawArray() 方法绘制单个三角形,则必须传递三个顶点并调用 drawArrays() 方法,如下所示。

var vertices = [-0.5,-0.5, -0.25,0.5, 0.0,-0.5,];
gl.drawArrays(gl.TRIANGLES, 0, 3);

它将产生如下所示的三角形。

Triangle

假设您想要绘制连续的三角形,那么您必须在顶点缓冲区中按顺序传递接下来的三个顶点,并将要渲染的元素数量指定为 6。

var vertices = [-0.5,-0.5, -0.25,0.5, 0.0,-0.5, 0.0,-0.5, 0.25,0.5, 0.5,-0.5,];
gl.drawArrays(gl.TRIANGLES, 0, 6);

它将产生一个连续的三角形,如下所示。

Triangle 1

drawElements()

drawElements() 是使用顶点和索引绘制模型的方法。其语法如下 −

void drawElements(enum mode, long count, enum type, long offset)

此方法采用以下四个参数 −

  • mode − WebGL 模型是使用原始类型绘制的。使用模式,程序员必须选择 WebGL 提供的一种原始类型。此选项的可能值列表为 − gl.POINTS、gl.LINE_STRIP、gl.LINE_LOOP、gl.LINES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN 和 gl.TRIANGLES。

  • count − 此选项指定要渲染的元素数量。

  • type − 此选项指定索引的数据类型,必须是 UNSIGNED_BYTE 或 UNSIGNED_SHORT。

  • offset − 此选项指定渲染的起点。通常是第一个元素 (0)。

如果使用 drawElements() 方法绘制模型,则还应与顶点缓冲区对象一起创建索引缓冲区对象。如果您使用此方法,顶点数据将被处理一次,并使用索引中提到的次数。

示例

如果您想使用索引绘制单个三角形,则需要将索引与顶点一起传递,并调用 drawElements() 方法,如下所示。

var vertices = [ -0.5,-0.5,0.0, -0.25,0.5,0.0, 0.0,-0.5,0.0 ];
var indices = [0,1,2];

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

它将产生以下输出 −

Triangle

如果您想使用 drawElements() 方法绘制传染性三角形,只需添加其他顶点并提及剩余顶点的索引即可。

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

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

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

它将产生以下输出 −

Triangle 1

必需的操作

在绘制图元之前,您需要执行一些操作,下面将对此进行解释。

清除画布

首先,您应该使用 clearColor() 方法清除画布。您可以将所需颜色的 RGBA 值作为参数传递给此方法。然后 WebGL 清除画布并用指定的颜色填充它。因此,您可以使用此方法设置背景颜色。

查看以下示例。这里我们传递的是灰色的 RGBA 值。

gl.clearColor(0.5, 0.5, .5, 1);

启用深度测试

使用 enable() 方法启用深度测试,如下所示。

gl.enable(gl.DEPTH_TEST);

清除颜色缓冲区位

使用 clear() 方法清除颜色和深度缓冲区,如下所示。

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

设置视口

视口表示一个矩形可视区域,其中包含绘图缓冲区的渲染结果。您可以使用 viewport() 方法设置视口的尺寸。在下面的代码中,视口尺寸设置为画布尺寸。

gl.viewport(0,0,canvas.width,canvas.height);

WebGL - 绘制点

我们之前(第 5 章)讨论了如何按照分步过程绘制图元。我们已分五个步骤解释了该过程。每次绘制新形状时,都需要重复这些步骤。本章介绍如何在 WebGL 中绘制具有 3D 坐标的点。在继续之前,让我们重新回顾一下这五个步骤。

必需步骤

创建 WebGL 应用程序来绘制点需要以下步骤。

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

在此步骤中,我们使用方法 getContext() 获取 WebGL 渲染上下文对象。

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

由于我们要绘制三个点,因此我们用 3D 坐标定义三个顶点并将它们存储在缓冲区中。

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

步骤 3 − 创建和编译着色器程序

在此步骤中,您需要编写顶点着色器和片段着色器程序,编译它们,并通过链接这两个程序创建一个组合程序。

  • 顶点着色器 − 在给定示例的顶点着色器中,我们定义一个矢量属性来存储 3D 坐标,并将其分配给 gl_position 变量。

  • gl_pointsize 是用于为点分配大小的变量。我们将点大小指定为 10。

var vertCode = 'attribute vec3 coordinates;' +

   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
      'gl_PointSize = 10.0;'+
   '}';
  • 片段着色器 − 在片段着色器中,我们只需将片段颜色分配给 gl_FragColor 变量即可

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';

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

在此步骤中,我们将缓冲区对象与着色器程序关联。

步骤 5 − 绘制所需对象

我们使用方法 drawArrays() 来绘制点。由于我们要绘制的点数为三个,因此计数值为 3。

gl.drawArrays(gl.POINTS, 0, 3)

示例 – 使用 WebGL 绘制三个点

以下是绘制三个点的完整 WebGL 程序 −

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*================创建画布=================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 

         /*==========定义和存储几何形状=======*/

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

        // 创建一个空的缓冲区对象来存储顶点缓冲区
        var vertex_buffer = gl.createBuffer();
        
        //将适当的数组缓冲区绑定到它
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 将顶点数据传递给缓冲区
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        // 取消绑定缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        /*==========================Shaders===========================*/
        
        // 顶点着色器源代码
        var vertCode =
        'attribute vec3 coordinates;' +
        
        'void main(void) {' +
        ' gl_Position = vec4(coordinates, 1.0);' +
        'gl_PointSize = 10.0;'+
        '}';
        
        // 创建顶点着色器对象
        var vertShader = gl.createShader(gl.VERTEX_SHADER);
        
        // 附加顶点着色器源代码
        gl.shaderSource(vertShader, vertCode);
        
        // 编译顶点着色器
        gl.compileShader(vertShader);
        
        // 片段着色器源代码
        var fragCode =
        'void main(void) {' +
        ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
        '}';
        
        // 创建片段着色器对象
        var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
        
        // 附加片段着色器源代码
        gl.shaderSource(fragShader, fragCode);
        
        // 编译片段着色器
        gl.compileShader(fragShader);
        
        // 创建一个着色器程序对象来存储
        // 组合着色器程序
        var shaderProgram = gl.createProgram();
        
        // 附加一个顶点着色器
        gl.attachShader(shaderProgram, vertShader);
        
        // 附加一个片段着色器
        gl.attachShader(shaderProgram, fragShader);
        
        // 链接两个程序
        gl.linkProgram(shaderProgram);
        
        // 使用组合着色器程序对象
        gl.useProgram(shaderProgram);
        
        /*======== 将着色器与缓冲区对象关联 =========*/
        
        // 绑定顶点缓冲区对象
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 获取属性位置
        var coord = gl.getAttribLocation(shaderProgram, "coordinates");

        
        // 将属性指向当前绑定的 VBO
        gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
        
        // 启用属性
        gl.enableVertexAttribArray(coord);
        
        /*============= 绘制图元 ===============*/
        
        // 清除画布
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        
        // 启用深度测试
        gl.enable(gl.DEPTH_TEST);
        
        // 清除颜色缓冲区位
        gl.clear(gl.COLOR_BUFFER_BIT);
        
        // 设置视口
        gl.viewport(0,0,canvas.width,canvas.height);
        
        // 绘制三角形
        gl.drawArrays(gl.POINTS, 0, 3);
      </script>
   </body>
</html>

它将产生以下结果 −

WebGL - 绘制三角形

在上一章(第 11 章)中,我们讨论了如何使用 WebGL 绘制三个点。在第 5 章中,我们通过示例应用程序演示了如何绘制三角形。在这两个示例中,我们都仅使用顶点绘制了图元。

为了绘制更复杂的形状/网格,我们还将几何图形的索引以及顶点传递给着色器。在本章中,我们将了解如何使用索引绘制三角形。

绘制三角形所需的步骤

创建 WebGL 应用程序来绘制三角形需要执行以下步骤。

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

在此步骤中,我们使用 getContext() 获取 WebGL 渲染上下文对象。

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

由于我们使用索引绘制三角形,因此我们必须传递三角形的三个顶点(包括索引),并将它们存储在缓冲区中。

var vertices = [
   -0.5,0.5,0.0,
   -0.5,-0.5,0.0,
   0.5,-0.5,0.0, 
];
	
indices = [0,1,2]; 

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

在此步骤中,您需要编写顶点着色器和片段着色器程序,编译它们,并通过链接这两个程序创建一个组合程序。

  • 顶点着色器 − 在程序的顶点着色器中,我们定义矢量属性来存储 3D 坐标并将其分配给 gl_position

var vertCode =
   'attribute vec3 coordinates;' +
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
   '}';
  • 片段着色器 − 在片段着色器中,我们只需将片段颜色分配给 gl_FragColor 变量即可。

var fragCode = 'void main(void) {' +
' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +
'}';

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

在此步骤中,我们将缓冲区对象与着色器程序关联起来。

步骤 5 − 绘制所需对象

由于我们使用索引绘制三角形,因此我们将使用 drawElements()。对于此方法,我们必须传递索引数量。 indices.length 的值表示索引的数量。

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

示例 – 绘制三角形

以下程序代码展示了如何使用索引在 WebGL 中绘制三角形 −

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============== 创建画布 ====================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
      
         /*======== 定义和存储几何形状 ===========*/

         var vertices = [
            -0.5,0.5,0.0,
            -0.5,-0.5,0.0,
            0.5,-0.5,0.0, 
         ];
         
         indices = [0,1,2];
         
        // 创建一个空的缓冲区对象来存储顶点缓冲区
        var vertex_buffer = gl.createBuffer();
        
        // 将适当的数组缓冲区绑定到它
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 将顶点数据传递到缓冲区
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        // 取消绑定缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        // 创建一个空的缓冲区对象来存储索引缓冲区
        var Index_Buffer = gl.createBuffer();
        
        // 将适当的数组缓冲区绑定到它
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
        
        // 将顶点数据传递到缓冲区
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
        
        // 解除缓冲区绑定
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        
        /*================ Shaders ======================*/
        
        // 顶点着色器源代码
        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);
        
        //片段着色器源代码
        var fragCode =
        'void main(void) {' +
        ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
        '}';
        
        // 创建片段着色器对象
        var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
        
        // 附加片段着色器源代码
        gl.shaderSource(fragShader, fragCode);
        
        // 编译片段着色器
        gl.compileShader(fragShader);
        
        // 创建着色器程序对象以存储
        // 组合着色器程序
        var shaderProgram = gl.createProgram();
        
        // 附加顶点着色器
        gl.attachShader(shaderProgram, vertShader);
        
        // 附加片段着色器
        gl.attachShader(shaderProgram, fragShader);
        
        // 链接两个程序
        gl.linkProgram(shaderProgram);
        
        // 使用组合着色器程序对象
        gl.useProgram(shaderProgram);
        
        /*======= 将着色器关联到缓冲区对象 =======*/
        
        // 绑定顶点缓冲区对象
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 绑定索引缓冲区对象
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
        
        // 获取属性位置
        var coord = gl.getAttribLocation(shaderProgram, "coordinates");
        
        // 将属性指向当前绑定的 VBO
        gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
        
        // 启用属性
        gl.enableVertexAttribArray(coord);
        
        /*==========绘制三角形===========*/
        
        // 清除画布
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        
        // 启用深度测试
        gl.enable(gl.DEPTH_TEST);
        
        // 清除颜色缓冲区位
        gl.clear(gl.COLOR_BUFFER_BIT);
        
        // 设置视口
        gl.viewport(0,0,canvas.width,canvas.height);
        
        // 绘制三角形
        gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
      </script>
    </body>
</html>

如果运行此示例,它将产生以下输出 −

WebGL - 绘图模式

在上一章(第 12 章)中,我们讨论了如何使用 WebGL 绘制三角形。除了三角形之外,WebGL 还支持各种其他绘图模式。本章介绍 WebGL 支持的绘图模式。

mode 参数

让我们看一下方法 − drawElements() 和 draw Arrays() 的语法。

void drawElements(enum mode, long count, enum type, long offset);

void drawArrays(enum mode, int first, long count);

如果你仔细观察,这两种方法都接受参数 mode。通过此参数,程序员可以选择WebGL中的绘制模式。

WebGL提供的绘制模式如下表所示。

Sr.No. 模式 &描述
1

gl.POINTS

绘制一系列点。

2

gl.LINES

绘制一系列不相连的线段(单独的线)。

3

gl.LINE_STRIP

绘制一系列相连的线段。

4

gl.LINE_LOOP

绘制一系列相连的线段。它还将第一个和最后一个顶点连接起来形成一个循环。

5

gl.TRIANGLES

绘制一系列独立的三角形。

6

gl.TRIANGLE_STRIP

以条带形式绘制一系列连接的三角形。

7

gl.TRIANGLE_FAN

以扇形方式绘制一系列共享第一个顶点的连接三角形。

示例 - 绘制三个平行线

以下示例显示如何使用 gl.LINES 绘制三条平行线。

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>

      <script>
         /*======= 创建画布 =========*/

        var canvas = document.getElementById('my_Canvas');
        var gl = canvas.getContext('experimental-webgl');
        
        /*======= 定义和存储几何图形 ======*/
        
        var vertices = [
        -0.7,-0.1,0,
        -0.3,0.6,0,
        -0.3,-0.3,0,
        0.2,0.6,0,
        0.3,-0.3,0,
        0.7,0.6,0
        ]
        
        // 创建一个空缓冲区对象
        var vertex_buffer = gl.createBuffer();
        
        // 将适当的数组缓冲区绑定到它
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 将顶点数据传递到缓冲区
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        // 解除缓冲区绑定
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        /*=================== Shaders ======================*/
        
        // 顶点着色器源代码
        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);
        
        // 片段着色器源代码
        var fragCode =
        'void main(void) {' +
        'gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
        '}';
        
        // 创建片段着色器对象
        var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
        
        // 附加片段着色器源代码
        gl.shaderSource(fragShader, fragCode);
        
        // 编译片段着色器
        gl.compileShader(fragShader);
        
        // 创建着色器程序对象以存储
        // 组合着色器程序
        var shaderProgram = gl.createProgram();
        
        // 附加一个顶点着色器
        gl.attachShader(shaderProgram, vertShader);
        
        // 附加一个片段着色器
        gl.attachShader(shaderProgram, fragShader);
        
        // 链接两个程序
        gl.linkProgram(shaderProgram);
        
        // 使用组合着色器程序对象
        gl.useProgram(shaderProgram);
        
        /*======= 将着色器关联到缓冲区对象 ======*/
        
        // 绑定顶点缓冲区对象
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 获取属性位置
        var coord = gl.getAttribLocation(shaderProgram, "coordinates");
        
        // 将属性指向当前绑定的 VBO
        gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
        
        // 启用属性
        gl.enableVertexAttribArray(coord);
        
        /*============= 绘制三角形 =============*/
        
        // 清除画布
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        
        // 启用深度测试
        gl.enable(gl.DEPTH_TEST);
        
        // 清除颜色和深度缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        
        // 设置视口
        gl.viewport(0,0,canvas.width,canvas.height);
        
        // 绘制三角形
        gl.drawArrays(gl.LINES, 0, 6);
        
        // 点、线条、线环、线、
        // 三角形条、三角形扇形、三角形
      </script>
   </body>
</html>

如果运行此示例,它将产生以下输出 −

绘图模式

在上面的程序中,如果将 drawArrays() 的模式替换为以下绘图模式之一,则每次都会产生不同的输出。

绘图模式 输出
LINE_STRIP Line Strip
LINE_LOOP Line Loop
TRIANGLE_STRIP Triangle Strip
TRIANGLE_FAN Triangle Fan
TRIANGLES Triangles

WebGL - 绘制四边形

在上一章中,我们讨论了 WebGL 提供的不同绘制模式。我们还可以使用索引来使用其中一种模式绘制图元。要在 WebGL 中绘制模型,我们必须选择其中一个图元并绘制所需的网格(即使用一个或多个图元形成的模型)。

在本章中,我们将举一个例子来演示如何使用 WebGL 绘制四边形。

绘制四边形的步骤

创建 WebGL 应用程序以绘制四边形需要以下步骤。

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

在此步骤中,我们使用 getContext() 获取 WebGL 渲染上下文对象。

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

可以使用两个三角形绘制一个正方形。在此示例中,我们提供了两个三角形(具有一个共同边)的顶点和索引。

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

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

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

在此步骤中,您需要编写顶点着色器和片段着色器程序,编译它们,并通过链接这两个程序创建一个组合程序。

  • 顶点着色器 − 在程序的顶点着色器中,我们定义矢量属性来存储 3D 坐标并将其分配给 gl_position

var vertCode =
   'attribute vec3 coordinates;' +
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
   '}';
  • 片段着色器 − 在片段着色器中,我们只需将片段颜色分配给 gl_FragColor 变量即可。

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(0.5, 0.3, 0.0, 7.5);' +'}';

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

在此步骤中,我们将缓冲区对象与着色器程序关联。

步骤 5 − 绘制所需对象

由于我们使用索引绘制两个三角形以形成四边形,因此我们将使用方法 drawElements()。对于此方法,我们必须传递索引的数量。indices.length 的值给出索引的数量。

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

示例 – 绘制四边形

以下程序显示如何创建 WebGL 应用程序来绘制四边形。

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============ 创建画布 =================*/
      
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
      
         /*========== 定义和存储几何形状 =========*/

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

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

        // 创建一个空的缓冲区对象来存储顶点缓冲区
        var vertex_buffer = gl.createBuffer();
        
        // 将适当的数组缓冲区绑定到它
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 将顶点数据传递到缓冲区
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        // 取消绑定缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        // 创建一个空的缓冲区对象来存储索引缓冲区
        var Index_Buffer = gl.createBuffer();
        
        // 将适当的数组缓冲区绑定到它
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
        
        // 将顶点数据传递到缓冲区
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
        
        // 解除缓冲区绑定
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        
        /*======================== Shaders ==========================*/
        
        // 顶点着色器源代码
        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);
        
        // 片段着色器源代码
        var fragCode =
        'void main(void) {' +
        ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
        '}';
        
        // 创建片段着色器对象
        var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
        
        // 附加片段着色器源代码
        gl.shaderSource(fragShader, fragCode);
        
        // 编译片段着色器
        gl.compileShader(fragShader);
        
        // 创建一个着色器程序对象来
        // 存储组合着色器程序
        var shaderProgram = gl.createProgram();
        
        // 附加一个顶点着色器
        gl.attachShader(shaderProgram, vertShader);
        
        // 附加一个片段着色器
        gl.attachShader(shaderProgram, fragShader);
        
        // 链接两个程序
        gl.linkProgram(shaderProgram);
        
        // 使用组合着色器程序对象
        gl.useProgram(shaderProgram);
        
        /* ======= 将着色器关联到缓冲区对象 =======*/
        
        // 绑定顶点缓冲区对象
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 绑定索引缓冲区对象
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
        
        // 获取属性位置
        var coord = gl.getAttribLocation(shaderProgram, "coordinates");
        
        // 将属性指向当前绑定的 VBO
        gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
        
        // 启用属性
        gl.enableVertexAttribArray(coord);
        
        /*============= 绘制四边形 =================*/
        
        // 清除画布
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        
        // 启用深度测试
        gl.enable(gl.DEPTH_TEST);
        
        // 清除颜色缓冲区位
        gl.clear(gl.COLOR_BUFFER_BIT);
        
        // 设置视口
        gl.viewport(0,0,canvas.width,canvas.height);
        
        // 绘制三角形
        gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
      </script>
   </body>
</html>

如果运行此示例,它将产生以下输出 −

WebGL - 颜色

在我们之前的所有示例中,我们通过为 gl_FragColor 变量分配所需的颜色值来将颜色应用于对象。除此之外,我们可以为每个顶点 − 定义颜色,就像顶点坐标和索引一样。本章通过一个示例来演示如何使用 WebGL 将颜色应用于四边形。

应用颜色

要应用颜色,您必须使用 JavaScript 数组中的 RGB 值为每个顶点定义颜色。您可以为所有顶点分配相同的值,以使对象具有唯一的颜色。定义颜色后,您必须创建一个颜色缓冲区并在其中存储这些值,并将其与顶点着色器属性关联。

在顶点着色器中,除了坐标属性(保存顶点的位置)外,我们还定义了一个attribute和一个variing来处理颜色。

color属性保存每个顶点的颜色值,variing是作为输入传递给片段着色器的变量。因此,我们必须将 color 值分配给 varying

在片段着色器中,保存颜色值的 varying 被分配给 gl_FragColor,后者保存对象的最终颜色。

应用颜色的步骤

创建 WebGL 应用程序以绘制四边形并为其应用颜色需要以下步骤。

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

在此步骤中,我们使用 getContext() 获取 WebGL 渲染上下文对象。

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

可以使用两个三角形绘制正方形。因此,在此示例中,我们提供了两个三角形(具有一个共同边)的顶点和索引。由于我们想为其应用颜色,因此还定义了一个保存颜色值的变量,并将每个颜色值(红色、蓝色、绿色和粉色)分配给它。

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

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

步骤 3 − 创建和编译着色器程序

在此步骤中,您需要编写顶点着色器和片段着色器程序,编译它们,并通过链接这两个程序创建一个组合程序。

  • 顶点着色器 − 在程序的顶点着色器中,我们定义矢量属性来存储 3D 坐标(位置)和每个顶点的颜色。声明一个 varing 变量以将颜色值从顶点着色器传递到片段着色器。最后,将存储在颜色属性中的值分配给 varying

var vertCode = 'attribute vec3 coordinates;'+
   'attribute vec3 color;'+
   'varying vec3 vColor;'+
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
      'vColor = color;'+
   '}';
  • 片段着色器 − 在片段着色器中,我们将 variing 分配给 gl_FragColor 变量。

var fragCode = 'precision mediump float;'+
   'varying vec3 vColor;'+
   'void main(void) {'+
      'gl_FragColor = vec4(vColor, 1.);'+
   '}';

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

在此步骤中,我们将缓冲区对象与着色器程序关联起来。

步骤 5 − 绘制所需对象

由于我们使用索引绘制两个三角形,它们将形成一个四边形,因此我们将使用方法 drawElements()。对于此方法,我们必须传递索引的数量。indices.length 的值表示索引的数量。

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

示例 – 应用颜色

以下程序演示了如何使用 WebGL 应用程序绘制四边形并为其应用颜色。

<!doctype html>
<html>
   <body>
    <canvas width = "300" height = "300" id = "my_Canvas"></canvas>

      <script>
        /*============== 创建画布 ==================*/
        var canvas = document.getElementById('my_Canvas');
        gl = canvas.getContext('experimental-webgl');
        
        /*=========== 定义和存储几何图形 ===========*/
        
        var vertices = [
        -0.5,0.5,0.0,
        -0.5,-0.5,0.0,
        0.5,-0.5,0.0,
        0.5,0.5,0.0
        ];
        
        var colors = [0,0,1, 1,0,0, 0,1,0, 1,0,1,];
        
        indices = [3,2,1,3,1,0];
         
        // 创建一个空的缓冲区对象并存储顶点数据
        var vertex_buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        // 创建一个空的缓冲区对象并存储索引数据
        var Index_Buffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        
        // 创建一个空的缓冲区对象并存储颜色数据
        var color_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

        /*========================== 着色器 ==========================*/
        
        // 顶点着色器源代码
        var vertCode = 'attribute vec3 coordinates;'+
        'attribute vec3 color;'+
        'varying vec3 vColor;'+
        'void main(void) {' +
        ' gl_Position = vec4(coordinates, 1.0);' +
        'vColor = color;'+
        '}';
        
        // 创建顶点着色器对象
        var vertShader = gl.createShader(gl.VERTEX_SHADER);
        
        // 附加顶点着色器源代码
        gl.shaderSource(vertShader, vertCode);
        
        // 编译顶点着色器
        gl.compileShader(vertShader);
        
        // 片段着色器源代码
        var fragCode = 'precision mediump float;'+
        'varying vec3 vColor;'+
        'void main(void) {'+
        'gl_FragColor = vec4(vColor, 1.);'+
        '}';
        
        // 创建片段着色器对象
        var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
        
        // 附加片段着色器源代码
        gl.shaderSource(fragShader, fragCode);
        
        // 编译片段着色器
        gl.compileShader(fragShader);
        
        // 创建着色器程序对象以
        // 存储组合着色器程序
        var shaderProgram = gl.createProgram();
        
        // 附加顶点着色器
        gl.attachShader(shaderProgram, vertShader);
        
        // 附加片段着色器
        gl.attachShader(shaderProgram, fragShader);
        
        // 链接两个程序
        gl.linkProgram(shaderProgram);
        
        // 使用组合着色器程序对象
        gl.useProgram(shaderProgram);
        
        /* ======== 将着色器关联到缓冲区对象 =======*/
        
        // 绑定顶点缓冲区对象
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
        // 绑定索引缓冲区对象
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
        
        // 获取属性位置
        var coord = gl.getAttribLocation(shaderProgram, "coordinates");
        
        // 将属性指向当前绑定的 VBO
        gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
        
        // 启用属性
        gl.enableVertexAttribArray(coord);
        
        // 绑定颜色缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
        
        // 获取属性位置
        var color = gl.getAttribLocation(shaderProgram, "color");
        
        // 将属性指向 volor 缓冲区对象
        gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ;
        
        // 启用颜色属性
        gl.enableVertexAttribArray(color);
        
        /*=============绘制四边形=====================*/
        
        // 清除画布
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        
        // 启用深度测试
        gl.enable(gl.DEPTH_TEST);
        
        // 清除颜色缓冲区位
        gl.clear(gl.COLOR_BUFFER_BIT);
        
        // 设置视口
        gl.viewport(0,0,canvas.width,canvas.height);
        
        //绘制三角形
        gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
      </script>
   </body>
</html>

如果运行此示例,它将产生以下输出 −

WebGL - 平移

到目前为止,我们讨论了如何使用 WebGL 绘制各种形状并在其中应用颜色。在本章中,我们将举一个例子来展示如何平移三角形。

平移

平移是 WebGL 提供的仿射变换之一。使用平移,我们可以在 xyz 平面上移动三角形(任何对象)。假设我们有一个三角形 [a, b, c],我们想将三角形​​移动到向正 X 轴移动 5 个单位、向正 Y 轴移动 3 个单位的位置。那么新的顶点将是 [a+5, b+3, c+0]。这意味着,要平移三角形,我们需要向每个顶点添加平移距离,例如 tx、ty、tz。

由于它是每个顶点操作,我们可以在顶点着色器程序中执行它。

在顶点着色器中,除了属性坐标(保存顶点位置)外,我们还定义了一个保存平移距离(x、y、z)的统一变量。稍后,我们将这个统一变量添加到坐标变量,并将结果分配给gl_Position变量。

注意 −由于顶点着色器将在每个顶点上运行,因此三角形的所有顶点都将被平移。

平移三角形的步骤

创建 WebGL 应用程序以绘制三角形然后将其平移到新位置需要以下步骤。

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

在此步骤中,我们使用 getContext() 获取 WebGL 渲染上下文对象。

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

由于我们绘制的是三角形,因此我们必须传递三角形的三个顶点,并将它们存储在缓冲区中。

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

步骤 3 − 创建和编译着色器程序

在此步骤中,您需要编写顶点着色器和片段着色器程序,编译它们,并通过链接这两个程序来创建组合程序。

  • 顶点着色器 − 在程序的顶点着色器中,我们定义一个矢量属性来存储 3D 坐标。与此同时,我们定义一个统一变量来存储平移距离,最后,我们将这两个值相加并将其分配给保存顶点最终位置的gl_position

var vertCode =
   'attribute vec4 coordinates;' +
   'uniform vec4 translation;'+
   'void main(void) {' +
      ' gl_Position = coordinates + translation;' +
   '}';
  • 片段着色器 − 在片段着色器中,我们只需将片段颜色分配给变量 gl_FragColor。

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';

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

在此步骤中,我们将缓冲区对象与着色器程序关联。

步骤 5 − 绘制所需对象

由于我们使用索引绘制三角形,因此我们将使用方法 drawArrays()。对于此方法,我们必须传递要考虑的顶点/元素的数量。由于我们绘制的是三角形,因此我们将传递 3 作为参数。

gl.drawArrays(gl.TRIANGLES, 0, 3);

示例 – 平移三角形

以下示例展示了如何在 xyz 平面上平移三角形。

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>
         
      <script>
         /*=================创建画布=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 
 
         /*===========定义和存储几何形状==============*/
         var vertices = [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];
            
         //创建一个空的缓冲区对象并存储顶点数据            
         var vertex_buffer = gl.createBuffer(); 
			
         //创建一个新的缓冲区
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);   
			
         //将其绑定到当前缓冲区			
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 
			
         // 传递缓冲区数据
         gl.bindBuffer(gl.ARRAY_BUFFER, null);  
            
         /*========================Shaders============================*/
            
         //顶点着色器源代码 
         var vertCode =
            'attribute vec4 coordinates;' + 
            'uniform vec4 translation;'+
            'void main(void) {' +
               '  gl_Position = coordinates + translation;' +
            '}';
            
         //创建顶点着色器程序对象并编译              
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);
            
   
         //片段着色器源代码
         var fragCode =
            'void main(void) {' +
               '   gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         //创建片段着色器程序对象并编译     
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
         gl.shaderSource(fragShader, fragCode);
         gl.compileShader(fragShader);
            
         //创建并使用组合着色器程序
         var shaderProgram = gl.createProgram();
         gl.attachShader(shaderProgram, vertShader);
         gl.attachShader(shaderProgram, fragShader);
         gl.linkProgram(shaderProgram);
   
         gl.useProgram(shaderProgram);
   
         /* ===========Associating shaders to buffer objects============*/
      
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);    
         var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates");
         gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0);   
         gl.enableVertexAttribArray(coordinatesVar); 
   
         /* ==========translation======================================*/
         var Tx = 0.5, Ty = 0.5, Tz = 0.0;
         var translation = gl.getUniformLocation(shaderProgram, 'translation');
         gl.uniform4f(translation, Tx, Ty, Tz, 0.0);
 
         /*=================Drawing the riangle and transforming it========================*/ 

         gl.clearColor(0.5, 0.5, 0.5, 0.9);
         gl.enable(gl.DEPTH_TEST);
   
         gl.clear(gl.COLOR_BUFFER_BIT);
         gl.viewport(0,0,canvas.width,canvas.height);
         gl.drawArrays(gl.TRIANGLES, 0, 3);
      </script>
    </body>
 </html>

如果运行此示例,它将产生以下输出 −

WebGL - 缩放

在本章中,我们将通过一个示例来演示如何使用 WebGL 修改三角形的比例。

缩放

缩放就是增加或减少对象的大小。例如,如果一个三角形的顶点大小为 [a,b,c],那么顶点为 [2a, 2b, 2c] 的三角形将为其大小的两倍。因此,要缩放三角形,您必须将每个顶点乘以缩放因子。您也可以缩放特定的顶点。

要缩放三角形,在程序的顶点着色器中,我们创建一个统一矩阵,并将坐标值与该矩阵相乘。稍后,我们传递一个 4×4 对角矩阵,该矩阵在对角线位置(最后一个对角线位置为 1)具有 x、y、z 坐标的缩放因子。

所需步骤

创建 WebGL 应用程序来缩放三角形需要以下步骤。

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

在此步骤中,我们使用 getContext() 获取 WebGL 渲染上下文对象。

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

由于我们绘制的是三角形,因此我们必须传递三角形的三个顶点,并将它们存储在缓冲区中。

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

步骤 3 − 创建和编译着色器程序

在此步骤中,您需要编写顶点着色器和片段着色器程序,编译它们,并通过链接这两个程序来创建组合程序。

  • 顶点着色器 − 在程序的顶点着色器中,我们定义一个矢量属性来存储 3D 坐标。与此同时,我们定义一个统一矩阵来存储缩放因子,最后,我们将这两个值相乘并将其分配给保存顶点最终位置的 gl_position

var vertCode =
   'attribute vec4 coordinates;' +
   'uniform mat4 u_xformMatrix;' +
   'void main(void) {' +
      ' gl_Position = u_xformMatrix * coordinates;' +
   '}';
  • 片段着色器 − 在片段着色器中,我们只需将片段颜色分配给 gl_FragColor 变量即可。

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';

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

在此步骤中,我们将缓冲区对象与着色器程序关联。

步骤 5 − 绘制所需对象

由于我们使用索引绘制三角形,因此我们使用 drawArrays() 方法。对于此方法,我们必须传递要考虑的顶点/元素的数量。由于我们正在绘制三角形,因此我们将传递 3 作为参数。

gl.drawArrays(gl.TRIANGLES, 0, 3);

示例 – 缩放三角形

以下示例显示如何缩放三角形 −

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>

      <script>
         /*=================创建画布=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 

         /*===========定义和存储几何形状==============*/
         var vertices =  [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];

         //创建一个空的缓冲区对象并存储顶点数据

         var vertex_buffer = gl.createBuffer();                                                     
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);                                                
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);           
         gl.bindBuffer(gl.ARRAY_BUFFER, null);  

         /*========================Shaders============================*/

         //顶点着色器源代码
         var vertCode =
            'attribute vec4 coordinates;' + 
            'uniform mat4 u_xformMatrix;' +
            'void main(void) {' +
               '  gl_Position = u_xformMatrix * coordinates;' +
            '}';

         //创建顶点着色器程序对象并编译                
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

         //片段着色器源代码
         var fragCode =
            'void main(void) {' +
               '   gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         //Create a fragment shader program object and compile it 
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
         gl.shaderSource(fragShader, fragCode);
         gl.compileShader(fragShader);

         //创建并使用组合着色器程序
         var shaderProgram = gl.createProgram();
         gl.attachShader(shaderProgram, vertShader);
         gl.attachShader(shaderProgram, fragShader);
         gl.linkProgram(shaderProgram);

         gl.useProgram(shaderProgram); 

         /*===================scaling==========================*/

         var Sx = 1.0, Sy = 1.5, Sz = 1.0;
         var xformMatrix = new Float32Array([
            Sx,   0.0,  0.0,  0.0,
            0.0,  Sy,   0.0,  0.0,
            0.0,  0.0,  Sz,   0.0,
            0.0,  0.0,  0.0,  1.0  
         ]);

         var u_xformMatrix = gl.getUniformLocation(shaderProgram, 'u_xformMatrix');
         gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);

         /* ===========Associating shaders to buffer objects============*/
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);   

         var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates"); 
         gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0);  
         gl.enableVertexAttribArray(coordinatesVar);

         /*=================Drawing the Quad========================*/ 
         gl.clearColor(0.5, 0.5, 0.5, 0.9);
         gl.enable(gl.DEPTH_TEST);

         gl.clear(gl.COLOR_BUFFER_BIT);
         gl.viewport(0,0,canvas.width,canvas.height);
         gl.drawArrays(gl.TRIANGLES, 0, 3);
      </script>
   </body>
</html>      

如果运行此示例,它将产生以下输出 −

WebGL - 旋转

在本章中,我们将通过一个示例来演示如何使用 WebGL 旋转三角形。

示例 - 旋转三角形

以下程序展示了如何使用 WebGL 旋转三角形。

<!doctype html>
<html>
   <body>
      <canvas width = "400" height = "400" id = "my_Canvas"></canvas>

      <script>
        /*====================创建画布==========================*/
        var canvas = document.getElementById('my_Canvas');
        gl = canvas.getContext('experimental-webgl');
        
        /*============定义和存储几何图形===============*/
        
        var vertices = [ -1,-1,-1, 1,-1,-1, 1, 1,-1 ];
        var colors = [ 1,1,1, 1,1,1, 1,1,1 ];
        var indices = [ 0,1,2 ];
        
        //创建并将数据存储到顶点缓冲区中
        var vertex_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        //创建并将数据存储到颜色缓冲区中
        var color_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
        
        //创建并将数据存储到索引缓冲区中
        var index_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
        
        /*===========================着色器===========================*/

         var vertCode = 'attribute vec3 position;'+
            'uniform mat4 Pmatrix;'+
            'uniform mat4 Vmatrix;'+
            'uniform mat4 Mmatrix;'+
            'attribute vec3 color;'+//the color of the point
            'varying vec3 vColor;'+

            'void main(void) { '+//pre-built function
               'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+
               'vColor = color;'+
            '}';

         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';

         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

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

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

         /*===========associating attributes to vertex shader ============*/

         var Pmatrix = gl.getUniformLocation(shaderProgram, "Pmatrix");
         var Vmatrix = gl.getUniformLocation(shaderProgram, "Vmatrix");
         var Mmatrix = gl.getUniformLocation(shaderProgram, "Mmatrix");
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         var position = gl.getAttribLocation(shaderProgram, "position");
         gl.vertexAttribPointer(position, 3, gl.FLOAT, false,0,0) ; //position
         gl.enableVertexAttribArray(position);
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);

         var color = gl.getAttribLocation(shaderProgram, "color");
         gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ; //color
         gl.enableVertexAttribArray(color);
         gl.useProgram(shaderProgram);

         /*========================= MATRIX ========================= */

         function get_projection(angle, a, zMin, zMax) {
            var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
            return [
               0.5/ang, 0 , 0, 0,
               0, 0.5*a/ang, 0, 0,
               0, 0, -(zMax+zMin)/(zMax-zMin), -1,
               0, 0, (-2*zMax*zMin)/(zMax-zMin), 0
            ];
         }

         var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100);
         var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
         var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];

         //translating z
         view_matrix[14] = view_matrix[14]-6; //zoom

         /*=======================rotation========================*/
         function rotateZ(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8]; 

            m[0] = c*m[0]-s*m[1];
            m[4] = c*m[4]-s*m[5];
            m[8] = c*m[8]-s*m[9];
            m[1] = c*m[1]+s*mv0;
            m[5] = c*m[5]+s*mv4;
            m[9] = c*m[9]+s*mv8;
         }

         /*=================Drawing===========================*/

         var time_old = 0;
         var animate = function(time) {
            var dt = time-time_old;
            rotateZ(mov_matrix, dt*0.002);
            time_old = time;

            gl.enable(gl.DEPTH_TEST);
            gl.depthFunc(gl.LEQUAL);
            gl.clearColor(0.5, 0.5, 0.5, 0.9);
            gl.clearDepth(1.0);
            gl.viewport(0.0, 0.0, canvas.width, canvas.height);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            gl.uniformMatrix4fv(Pmatrix, false, proj_matrix);
            gl.uniformMatrix4fv(Vmatrix, false, view_matrix);
            gl.uniformMatrix4fv(Mmatrix, false, mov_matrix);

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
            gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
            window.requestAnimationFrame(animate);
         }
         animate(0);
      </script>
   </body>
</html>

如果运行此示例,它将产生以下输出 −

WebGL - 立方体旋转

在本章中,我们将通过一个示例来演示如何使用 WebGL 绘制旋转的 3D 立方体。

示例 - 绘制旋转的 3D 立方体

以下程序展示了如何绘制旋转的 3D 立方体 −

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============= 创建画布 =================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*============ 定义和存储几何形状 =========*/

         var vertices = [
            -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
            -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
            -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1,
            1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1,
            -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1,
            -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1, 
         ];

         var colors = [
            5,3,7, 5,3,7, 5,3,7, 5,3,7,
            1,1,3, 1,1,3, 1,1,3, 1,1,3,
            0,0,1, 0,0,1, 0,0,1, 0,0,1,
            1,0,0, 1,0,0, 1,0,0, 1,0,0,
            1,1,0, 1,1,0, 1,1,0, 1,1,0,
            0,1,0, 0,1,0, 0,1,0, 0,1,0
         ];

         var indices = [
            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 
         ];

        // 创建并将数据存储到顶点缓冲区
        var vertex_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        // 创建并将数据存储到颜色缓冲区
        var color_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
        
        // 创建并将数据存储到索引缓冲区
        var index_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,新的Uint16Array(indices),gl.STATIC_DRAW);

         /*=================== Shaders =========================*/

         var vertCode = 'attribute vec3 position;'+
            'uniform mat4 Pmatrix;'+
            'uniform mat4 Vmatrix;'+
            'uniform mat4 Mmatrix;'+
            'attribute vec3 color;'+//the color of the point
            'varying vec3 vColor;'+

            'void main(void) { '+//pre-built function
               'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+
               'vColor = color;'+
            '}';

         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';

         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

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

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

         /* ====== Associating attributes to vertex shader =====*/
         var Pmatrix = gl.getUniformLocation(shaderProgram, "Pmatrix");
         var Vmatrix = gl.getUniformLocation(shaderProgram, "Vmatrix");
         var Mmatrix = gl.getUniformLocation(shaderProgram, "Mmatrix");

         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         var position = gl.getAttribLocation(shaderProgram, "position");
         gl.vertexAttribPointer(position, 3, gl.FLOAT, false,0,0) ;

         // Position
         gl.enableVertexAttribArray(position);
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         var color = gl.getAttribLocation(shaderProgram, "color");
         gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ;

         // Color
         gl.enableVertexAttribArray(color);
         gl.useProgram(shaderProgram);

         /*==================== MATRIX =====================*/

         function get_projection(angle, a, zMin, zMax) {
            var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
            return [
               0.5/ang, 0 , 0, 0,
               0, 0.5*a/ang, 0, 0,
               0, 0, -(zMax+zMin)/(zMax-zMin), -1,
               0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 
            ];
         }

         var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100);

         var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
         var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];

         // translating z
         view_matrix[14] = view_matrix[14]-6;//zoom

         /*==================== Rotation ====================*/

         function rotateZ(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8];

            m[0] = c*m[0]-s*m[1];
            m[4] = c*m[4]-s*m[5];
            m[8] = c*m[8]-s*m[9];

            m[1]=c*m[1]+s*mv0;
            m[5]=c*m[5]+s*mv4;
            m[9]=c*m[9]+s*mv8;
         }

         function rotateX(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv1 = m[1], mv5 = m[5], mv9 = m[9];

            m[1] = m[1]*c-m[2]*s;
            m[5] = m[5]*c-m[6]*s;
            m[9] = m[9]*c-m[10]*s;

            m[2] = m[2]*c+mv1*s;
            m[6] = m[6]*c+mv5*s;
            m[10] = m[10]*c+mv9*s;
         }

         function rotateY(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8];

            m[0] = c*m[0]+s*m[2];
            m[4] = c*m[4]+s*m[6];
            m[8] = c*m[8]+s*m[10];

            m[2] = c*m[2]-s*mv0;
            m[6] = c*m[6]-s*mv4;
            m[10] = c*m[10]-s*mv8;
         }

         /*================= Drawing ===========================*/
         var time_old = 0;

         var animate = function(time) {

            var dt = time-time_old;
            rotateZ(mov_matrix, dt*0.005);//time
            rotateY(mov_matrix, dt*0.002);
            rotateX(mov_matrix, dt*0.003);
            time_old = time;

            gl.enable(gl.DEPTH_TEST);
            gl.depthFunc(gl.LEQUAL);
            gl.clearColor(0.5, 0.5, 0.5, 0.9);
            gl.clearDepth(1.0);

            gl.viewport(0.0, 0.0, canvas.width, canvas.height);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            gl.uniformMatrix4fv(Pmatrix, false, proj_matrix);
            gl.uniformMatrix4fv(Vmatrix, false, view_matrix);
            gl.uniformMatrix4fv(Mmatrix, false, mov_matrix);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
            gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

            window.requestAnimationFrame(animate);
         }
         animate(0);
      </script>
   </body>
</html>

如果运行此示例,它将产生以下输出 −

WebGL - 交互式立方体

在本章中,我们将通过一个示例来演示如何绘制可以使用鼠标控件旋转的 3D 立方体。

示例 - 绘制交互式立方体

以下程序显示如何使用鼠标控件旋转立方体 −

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============= 创建画布 ======================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*========== 定义和存储几何形状 ==========*/

         var vertices = [
            -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
            -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
            -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1,
            1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1,
            -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1,
            -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1, 
         ];

         var colors = [
            5,3,7, 5,3,7, 5,3,7, 5,3,7,
            1,1,3, 1,1,3, 1,1,3, 1,1,3,
            0,0,1, 0,0,1, 0,0,1, 0,0,1,
            1,0,0, 1,0,0, 1,0,0, 1,0,0,
            1,1,0, 1,1,0, 1,1,0, 1,1,0,
            0,1,0, 0,1,0, 0,1,0, 0,1,0 
         ];

         var indices = [
            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 
         ];

        // 创建并将数据存储到顶点缓冲区
        var vertex_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        
        // 创建并将数据存储到颜色缓冲区
        var color_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
        
        // 创建并将数据存储到索引缓冲区
        var index_buffer = gl.createBuffer ();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,新的Uint16Array(indices),gl.STATIC_DRAW);

         /*=================== SHADERS =================== */

         var vertCode = 'attribute vec3 position;'+
            'uniform mat4 Pmatrix;'+
            'uniform mat4 Vmatrix;'+
            'uniform mat4 Mmatrix;'+
            'attribute vec3 color;'+//the color of the point
            'varying vec3 vColor;'+
            'void main(void) { '+//pre-built function
               'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+
               'vColor = color;'+
            '}';

         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';

         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

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

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

         /*======== Associating attributes to vertex shader =====*/
         var _Pmatrix = gl.getUniformLocation(shaderprogram, "Pmatrix");
         var _Vmatrix = gl.getUniformLocation(shaderprogram, "Vmatrix");
         var _Mmatrix = gl.getUniformLocation(shaderprogram, "Mmatrix");

         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         var _position = gl.getAttribLocation(shaderprogram, "position");
         gl.vertexAttribPointer(_position, 3, gl.FLOAT, false,0,0);
         gl.enableVertexAttribArray(_position);

         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         var _color = gl.getAttribLocation(shaderprogram, "color");
         gl.vertexAttribPointer(_color, 3, gl.FLOAT, false,0,0) ;
         gl.enableVertexAttribArray(_color);
         gl.useProgram(shaderprogram);

         /*==================== MATRIX ====================== */

         function get_projection(angle, a, zMin, zMax) {
            var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
            return [
               0.5/ang, 0 , 0, 0,
               0, 0.5*a/ang, 0, 0,
               0, 0, -(zMax+zMin)/(zMax-zMin), -1,
               0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 
			   ];
         }

         var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100);
         var mo_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];
         var view_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];

         view_matrix[14] = view_matrix[14]-6;

         /*================= Mouse events ======================*/

         var AMORTIZATION = 0.95;
         var drag = false;
         var old_x, old_y;
         var dX = 0, dY = 0;

         var mouseDown = function(e) {
            drag = true;
            old_x = e.pageX, old_y = e.pageY;
            e.preventDefault();
            return false;
         };

         var mouseUp = function(e){
            drag = false;
         };

         var mouseMove = function(e) {
            if (!drag) return false;
            dX = (e.pageX-old_x)*2*Math.PI/canvas.width,
            dY = (e.pageY-old_y)*2*Math.PI/canvas.height;
            THETA+= dX;
            PHI+=dY;
            old_x = e.pageX, old_y = e.pageY;
            e.preventDefault();
         };

         canvas.addEventListener("mousedown", mouseDown, false);
         canvas.addEventListener("mouseup", mouseUp, false);
         canvas.addEventListener("mouseout", mouseUp, false);
         canvas.addEventListener("mousemove", mouseMove, false);

         /*=========================rotation================*/

         function rotateX(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv1 = m[1], mv5 = m[5], mv9 = m[9];

            m[1] = m[1]*c-m[2]*s;
            m[5] = m[5]*c-m[6]*s;
            m[9] = m[9]*c-m[10]*s;

            m[2] = m[2]*c+mv1*s;
            m[6] = m[6]*c+mv5*s;
            m[10] = m[10]*c+mv9*s;
         }

         function rotateY(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8];

            m[0] = c*m[0]+s*m[2];
            m[4] = c*m[4]+s*m[6];
            m[8] = c*m[8]+s*m[10];

            m[2] = c*m[2]-s*mv0;
            m[6] = c*m[6]-s*mv4;
            m[10] = c*m[10]-s*mv8;
         }

         /*=================== Drawing =================== */

         var THETA = 0,
         PHI = 0;
         var time_old = 0;

         var animate = function(time) {
            var dt = time-time_old;

            if (!drag) {
               dX *= AMORTIZATION, dY*=AMORTIZATION;
               THETA+=dX, PHI+=dY;
            }

            //set model matrix to I4

            mo_matrix[0] = 1, mo_matrix[1] = 0, mo_matrix[2] = 0,
            mo_matrix[3] = 0,

            mo_matrix[4] = 0, mo_matrix[5] = 1, mo_matrix[6] = 0,
            mo_matrix[7] = 0,

            mo_matrix[8] = 0, mo_matrix[9] = 0, mo_matrix[10] = 1,
            mo_matrix[11] = 0,

            mo_matrix[12] = 0, mo_matrix[13] = 0, mo_matrix[14] = 0,
            mo_matrix[15] = 1;

            rotateY(mo_matrix, THETA);
            rotateX(mo_matrix, PHI);

            time_old = time; 
            gl.enable(gl.DEPTH_TEST);

            // gl.depthFunc(gl.LEQUAL);

            gl.clearColor(0.5, 0.5, 0.5, 0.9);
            gl.clearDepth(1.0);
            gl.viewport(0.0, 0.0, canvas.width, canvas.height);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            gl.uniformMatrix4fv(_Pmatrix, false, proj_matrix);
            gl.uniformMatrix4fv(_Vmatrix, false, view_matrix);
            gl.uniformMatrix4fv(_Mmatrix, false, mo_matrix);

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
            gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

            window.requestAnimationFrame(animate);
         }
         animate(0);
      </script>
   </body>
</html> 

如果运行此示例,它将产生以下输出 −