Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

绘制三角形 #4

Open
sundway opened this issue Oct 9, 2016 · 0 comments
Open

绘制三角形 #4

sundway opened this issue Oct 9, 2016 · 0 comments
Assignees

Comments

@sundway
Copy link
Owner

sundway commented Oct 9, 2016

绘制三角形

在上一篇文章WebGL 初探中,我们熟悉了 WebGL 的简单背景以及如何绘制一个简单的点。但是只绘制点我们是无法进入三维世界的,本篇章将讲解如何使用 WebGL 绘制三角形,因为很多 3D 图形都是使用三角形为基础进行渲染的,所以有些对 GPU 性能指标的评价就是渲染三角形的能力。

attribute 变量

前一篇文章中,绘制一个点直接将一个硬编码的矢量赋给了位置属性。但是,显然这样写是不够灵活的。所以在这篇文章的前一部分会使用变量重写前一篇文章的例子。我们的目标是将位置信息从 JavaScript 程序中传递到顶点着色器,而 attribute 变量就传输的是那些和顶点相关的数据。下图展示了如何使用 attribute 变量传输数据:

图片

var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  gl_PointSize = 10.0;\n' +
  '}\n';

如上所示,前一篇文章中,直接使用矢量进行赋值 gl_Position = vec4(0.0, 0.0, 0.0, 1.0)。而这里使用 attribute vec4 a_Position 声明一个 attribute 变量。关键字 attribute 被称为存储限定符,表示声明一个 attribute 变量,而且 attribute 变量必须声明成全局变量,数据将从着色器外传入。gl_Position = a_Position 将 attribute 变量 a_Position 赋值给 gl_Position。

  // 获得 attribute 变量 a_Position 的存储位置
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return;
  }

  // 将顶点位置传递给 attribute 变量
  gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);

上面,我们使用 vertexAttrib3f(location, v0, v1, v2) 方法将数据 (v0, v1, v2) 传递给 location 参数指向的 attribute 变量。而这里的 location 是要修改的 attribute 变量的存储位置。所以在这之前我们需要获取 attribute 变量的存储位置。我们可以通过 getAttribLocation(program, name) 获取 attribute 变量的存储位置。进行完这些更改之后,运行会发现和上一篇文章相同的效果。

缓冲区对象

到现在,我们一次只能绘制一个点,无法一次性绘制多个点,甚至是线和面。想要一次绘制多个点,我们需要借助一个叫做缓冲区对象的东西。缓冲区对象是 WebGL 中的一块存储区域,其中保存了大量的顶点数据,可以一次性的向顶点着色器传入多个顶点到 attribute 变量。下图展示了如何使用缓冲区对象:

图片

  var vertices = new Float32Array([
    0, 0.5,   -0.5, -0.5,   0.5, -0.5
  ]);

  // 创建缓冲区对象
  var vertexBuffer = gl.createBuffer();
  if (!vertexBuffer) {
    console.log('创建缓冲区对象失败。');
    return -1;
  }
  // 绑定缓冲区对象到目标
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

 // 将数据写入缓冲区对象
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('获得 attriute 变量 a_Position 失败');
    return -1;
  }
  // 将缓冲区对象赋值给 attriute 变量 a_Position
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

  // 开启 attribute 变量
  gl.enableVertexAttribArray(a_Position);

首先,是通过 createBuffer() 方法创建一个缓冲区对象,对应的方法还有 deleteBuffer() 用于删除一个缓冲区对象。接着,使用 bindBuffer(target, buffer) 将缓冲区对象绑定到 WebGL 已经存在的目标上,以通知 WebGL 正确的处理其内容。紧接着,使用 bufferData(target, data, useage) 方法将数据写入缓冲区,这里实际是将数据写入到绑定的目标对象上,所以在第二步中进行了缓冲区对象的绑定。而第二个参数是一个定义好的类型化数组。这里的第三个参数,STATIC_DRAW 表示只会向缓冲区写入一次,绘制多次,其它用法参数可查相关 API。然后,使用 vertexAttribPointer(location, size, type, normalized, stride, offset) 将缓冲区对象赋值给 attribute 变量。这里的第二个参数定义缓冲区对象的每个顶点的分量(1到4之间),如果 size 比 attribute 变量需要的分量小,则按照一定规则补全。第三个参数指定了数据格式。第四个参数表示是否将非浮点型数据归一到 [0, 1] 或[-1,1] 之间。最后两个参数一般默认为0,此处不做具体介绍。最后通过 enableVertexAttribArray(a_Position) 方法开启缓冲区对象和 attribute 变量的连接。

绘制三角形

最后,我们可通过 drawArrays(gl.TRIANGLES, 0, n) 绘制三角形。在前一篇文章中提到 drawArrays 函数功能非常强大。现在,我们就看一下它有哪些强大的特性。函数中第一个参数指定了绘制方式,第二个参数指定从哪个顶点开始绘制,第三个参数指定需要绘制多少个顶点。下表展示了第一个参数对应的绘制方式:

参数 基本图形 呈现效果
gl.POINTS 一系列的点绘制在v0,v1,v2等处。
gl.LINES 线段 一系列单独线段。
gl.LINE_STRIP 连线 一个相连的线段。
gl.LINE_LOOP 回路 一个相连的回路。
gl.TRIANGLES 三角形 一系列单独的三角形。
gl.TRIANGLE_STRIP 三角带 一个相邻三角形组成的图形。

下图是 new Float32Array([0, 0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5]) 矢量的一个测试:

结语

这篇文章主要讲了如何利用缓冲区对象在空间中绘制点、线、面。下一篇文章将讲解矩阵变换,有了这两个知识的积累,理论上可以画出我们想要的 3D 图形。WebGL 的 API 看似复杂,但基本都很类似。当我们对这一套熟悉之后,我们会愈发轻车熟路。

系列文章

@sundway sundway self-assigned this Oct 9, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant