WebGL - 快速指南



WebGL - 简介

几年前,Java 应用程序(作为 applet 和 JOGL 的组合)通过访问 GPU(图形处理单元)用于在 Web 上处理 3D 图形。由于 applet 需要 JVM 才能运行,因此难以依赖 Java applet。几年后,人们停止使用 Java applet。

Adobe(Flash、AIR)提供的 Stage3D API 提供了 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 group 维护。
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 图形硬件上表现良好。

它是一个 JavaScript API,可以与 HTML5 一起使用。WebGL 代码写在 HTML5 的 <canvas> 标签内。它是一个规范,允许 Internet 浏览器访问那些计算机上的图形处理单元 (GPU),这些计算机上使用了 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 and GPU

GPU 加速计算

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

GPU Accelerated Computing

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

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

支持的浏览器

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

Web 浏览器

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

移动浏览器

浏览器名称 版本 支持
Chrome for Android 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 浏览器和 Chrome for Android 等移动浏览器。

  • 开源 - 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 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)中的标识符。

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

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

这些属性决定了画布的大小。如果程序员没有在 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 元素上显示内容,我们必须使用脚本语言。此脚本语言应访问渲染上下文并在其上绘制。

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 Coordinate System

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 中保存数据的内存区域。有各种缓冲区,例如绘制缓冲区、帧缓冲区、顶点缓冲区和索引缓冲区。**顶点缓冲区**和**索引缓冲区**用于描述和处理模型的几何体。

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

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

网格

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

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

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

示例

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

Mesh Example

着色器程序

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

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

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

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

顶点着色器

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

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

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

  • 顶点变换
  • 法线变换和归一化
  • 纹理坐标生成
  • 纹理坐标变换
  • 光照
  • 颜色材质应用

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

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

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

  • 插值值的运算
  • 纹理访问
  • 纹理应用
  • 颜色相加
Fragment Shader

OpenGL ES SL 变量

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

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

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

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

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

WebGL - 图形流水线

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

Graphics Pipeline

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

JavaScript

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

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

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

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

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

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

  • **统一变量** - 我们也可以使用 JavaScript 关联统一变量。

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

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

顶点着色器

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

图元装配

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

光栅化

在光栅化步骤中,确定基本图形中最终图像中的像素。它有两个步骤:

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

  • **裁剪** - 如果三角形部分位于视角区域之外,则将视角区域之外的部分移除。此过程称为裁剪。

片段着色器

片段着色器获取

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

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

片段操作

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

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

一旦所有片段都处理完毕,就会形成一个 2D 图像并在屏幕上显示。**帧缓冲区**是渲染管道的最终目标。

Fragment Operations

帧缓冲区

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

WebGL - 示例应用程序

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

WebGL 应用程序结构

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

  • JavaScript 用于与 CPU 通信
  • OpenGL 着色器语言用于与 GPU 通信。
WebGL Application Structure

示例应用程序

现在让我们来看一个简单的示例,学习如何使用 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 画布元素

在第 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 不是必需的。此参数提供各种接受布尔值作为选项,如下所示:

序号 属性和描述
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 时,会创建一个绘图缓冲区。上下文对象管理 OpenGL 状态并渲染到绘图缓冲区。

WebGLRenderingContext

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

序号 属性和描述
1

Canvas

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

2

drawingBufferWidth

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

3

drawingBufferHeight

此属性表示绘图缓冲区的实际高度。它可能与 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
]; 

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() 的方法。如果创建成功,此方法将返回一个新创建的缓冲区对象;否则,如果失败,则返回 null 值。

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

var vertex_buffer = gl.createBuffer();

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

绑定缓冲区

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

语法

bindBuffer() 方法的语法如下:

void bindBuffer (enum target, Object buffer)

此方法接受两个参数,如下所述。

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

  • ARRAY_BUFFER 表示顶点数据。

  • ELEMENT_ARRAY_BUFFER 表示索引数据。

Object 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 表示索引数据

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

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

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

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

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

示例

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

//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

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

解除绑定缓冲区

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

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

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

序号 方法和描述
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提供的基本数据类型。

序号 类型 & 描述
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

访问二维纹理

10

samplerCube

访问立方体贴图纹理

限定符

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

序号 限定符 & 描述
1

attribute

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

2

uniform

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

示例 − uniform vec4 lightPosition;

3

varying

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

示例 − varying vec3 normal;

顶点着色器

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

在顶点着色器的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);
};

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

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

gl_Position

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

稍后,gl_position值将被图元装配、裁剪、剔除和其他固定功能操作使用,这些操作在顶点处理完成后对图元进行操作。

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

片段着色器

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

  • 插值值的运算
  • 纹理访问
  • 纹理应用
  • 颜色相加

预定义变量

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

序号 变量 & 描述
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就是其中之一。此变量保存模型像素的颜色值。

存储和编译着色器程序

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

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)

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

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

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

此方法接受两个参数:

  • 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 - 这是一个布尔值。如果为真,则非浮点数据将归一化到 [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 中,模型使用图元类型绘制。使用 mode,程序员必须选择 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 模型使用图元类型绘制。使用 mode,程序员必须选择 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() 方法绘制点。由于我们要绘制的点数为三个,因此 count 值为 3。

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

示例 – 使用 WebGL 绘制三个点

以下是使用 WebGL 绘制三个点的完整 WebGL 程序:

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

      <script>
         /*================Creating a canvas=================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 

         /*==========Defining and storing the geometry=======*/

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

         // Create an empty buffer object to store the vertex buffer
         var vertex_buffer = gl.createBuffer();

         //Bind appropriate array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

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

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

         // vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +

            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'gl_PointSize = 10.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 fragmentt shader
         gl.compileShader(fragShader);
         
         // Create a shader program object to store
         // the 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);

         /*======== Associating shaders 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, 3, gl.FLOAT, false, 0, 0);

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

         /*============= Drawing the primitive ===============*/

         // 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.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>
         /*============== Creating a canvas ====================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
      
         /*======== Defining and storing the geometry ===========*/

         var vertices = [
            -0.5,0.5,0.0,
            -0.5,-0.5,0.0,
            0.5,-0.5,0.0, 
         ];
         
         indices = [0,1,2];
         
         // Create an empty buffer object to store vertex buffer
         var vertex_buffer = gl.createBuffer();

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

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

         // Create an empty buffer object to store Index buffer
         var Index_Buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
         
         // Unbind the buffer
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

         /*================ Shaders ====================*/
         
         // Vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +
				
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 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 fragmentt shader
         gl.compileShader(fragShader);

         // Create a shader program object to store
         // the 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 the programs
         gl.linkProgram(shaderProgram);

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

         /*======= Associating shaders to buffer objects =======*/

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

         // Bind index buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
         
         // Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); 
         
         // Enable the attribute
         gl.enableVertexAttribArray(coord);

         /*=========Drawing the 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.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 提供的绘制模式列在下表中。

序号 模式和说明
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>
         /*======= Creating a canvas =========*/

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

         /*======= Defining and storing the geometry ======*/

         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 
         ]

         // Create an empty buffer object
         var vertex_buffer = gl.createBuffer();

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

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

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

         // Vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 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 fragmentt shader
         gl.compileShader(fragShader);

         // Create a shader program object to store
         // the 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 the programs
         gl.linkProgram(shaderProgram);

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

         /*======= Associating shaders 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, 3, gl.FLOAT, false, 0, 0);

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

         /*============ Drawing the 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 and depth buffer
         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

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

         // Draw the triangle
         gl.drawArrays(gl.LINES, 0, 6);

         // POINTS, LINE_STRIP, LINE_LOOP, LINES,
         // TRIANGLE_STRIP,TRIANGLE_FAN, TRIANGLES
      </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>
         /*============ Creating a canvas =================*/
      
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
      
         /*========== Defining and storing the geometry =========*/

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

         // Create an empty buffer object to store vertex buffer
         var vertex_buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

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

         // Create an empty buffer object to store Index buffer
         var Index_Buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

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

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

         // Vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 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 fragmentt shader
         gl.compileShader(fragShader);

         // Create a shader program object to
         // store the 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 the programs
         gl.linkProgram(shaderProgram);

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

         /* ======= Associating shaders to buffer objects =======*/

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

         // Bind index buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); 

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

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

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

         /*============= Drawing the Quad ================*/

         // 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.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
      </script>
   </body>
</html>

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

WebGL - 颜色

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

应用颜色

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

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

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

在片段着色器中,保存颜色值的varying被分配给gl_FragColorgl_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;'+
   '}';
  • 片段着色器 - 在片段着色器中,我们将varying分配给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>
         /*============= Creating a canvas ==================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
         
         /*========== Defining and storing the geometry ==========*/

         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];
         
         // Create an empty buffer object and store vertex data
         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);

         // Create an empty buffer object and store Index data
         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);

         // Create an empty buffer object and store color data
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         /*======================= Shaders =======================*/
         
         // vertex shader source code
         var vertCode = 'attribute vec3 coordinates;'+
            'attribute vec3 color;'+
            'varying vec3 vColor;'+
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'vColor = color;'+
            '}';
            
         // 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 = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';
            
         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

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

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

         // Create a shader program object to
         // store the 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 the programs
         gl.linkProgram(shaderProgram);

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

         /* ======== Associating shaders to buffer objects =======*/

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

         // Bind index buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

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

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

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

         // bind the color buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         
         // get the attribute location
         var color = gl.getAttribLocation(shaderProgram, "color");
 
         // point attribute to the volor buffer object
         gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ;
 
         // enable the color attribute
         gl.enableVertexAttribArray(color);

         /*============Drawing the Quad====================*/

         // 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.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)添加到每个顶点。

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

在顶点着色器中,除了保存顶点位置的属性coordinates外,我们还定义了一个uniform变量来保存平移距离(x、y、z)。稍后,我们将此uniform变量添加到coordinates变量中,并将结果分配给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坐标。同时,我们定义了一个uniform变量来存储平移距离,最后,我们将这两个值相加并将其分配给gl_positiongl_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>
         /*=================Creating a canvas=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 
 
         /*===========Defining and storing the geometry==============*/
         var vertices = [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];
            
         //Create an empty buffer object and store vertex data            
         var vertex_buffer = gl.createBuffer(); 
			
         //Create a new buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);   
			
         //bind it to the current buffer			
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 
			
         // Pass the buffer data
         gl.bindBuffer(gl.ARRAY_BUFFER, null);  
            
         /*========================Shaders============================*/
            
         //vertex shader source code 
         var vertCode =
            'attribute vec4 coordinates;' + 
            'uniform vec4 translation;'+
            'void main(void) {' +
               '  gl_Position = coordinates + translation;' +
            '}';
            
         //Create a vertex shader program object and compile it              
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);
            
   
         //fragment shader source code
         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);
            
         //Create and use combiened shader program
         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]的三角形将是其大小的两倍。因此,要缩放三角形,您必须将每个顶点乘以缩放因子。您也可以缩放特定顶点。

要缩放三角形,在程序的顶点着色器中,我们创建一个uniform矩阵并将坐标值与该矩阵相乘。稍后,我们传递一个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坐标。同时,我们定义了一个uniform矩阵来存储缩放因子,最后,我们将这两个值相乘并将其分配给gl_positiongl_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>
         /*=================Creating a canvas=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 

         /*===========Defining and storing the geometry==============*/
         var vertices =  [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];

         //Create an empty buffer object and store vertex data

         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============================*/

         //Vertex shader source code
         var vertCode =
            'attribute vec4 coordinates;' + 
            'uniform mat4 u_xformMatrix;' +
            'void main(void) {' +
               '  gl_Position = u_xformMatrix * coordinates;' +
            '}';

         //Create a vertex shader program object and compile it                
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

         //fragment shader source code
         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);

         //Create and use combiened shader program
         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>
         /*=================Creating a canvas=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*===========Defining and storing the geometry==============*/

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

         //Create and store data into vertex buffer
         var vertex_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         //Create and store data into color buffer
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         //Create and store data into index buffer
         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);

         /*==========================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;
         }

         /*=================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>
         /*============= Creating a canvas =================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*============ Defining and storing the geometry =========*/

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

         // Create and store data into vertex buffer
         var vertex_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Create and store data into color buffer
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         // Create and store data into index buffer
         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);

         /*=================== 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>
         /*============= Creating a canvas ======================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*========== Defining and storing the geometry ==========*/

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

         // Create and store data into vertex buffer
         var vertex_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Create and store data into color buffer
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         // Create and store data into index buffer
         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);

         /*=================== 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> 

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

广告