WebGL 基础篇2/2

  在绘制之前我们应该调整画布(canvas)的尺寸以匹配它的显示尺寸。画布就像图片一样有两个尺寸。 一个是它拥有的实际像素个数,另一个是它显示的大小。CSS决定画布显示的大小。 你应该尽可能用CSS设置所需画布大小 ,因为它比其它方式灵活的多。

  为了使画布的像素数和显示大小匹配, 我这里使用了一个辅助方法,你可以在这里获取更多相关信息

  这里的例子中,有独立窗口显示的示例大多使用400×300像素大小的画布。 但是如果像稍后展示的示例那样嵌在页面中,它就会被拉伸以填满可用空间 (你也可以点击示例下方的“点此在新窗口中浏览”在独立窗口中查看示例)。 通过使用CSS调整画布尺寸可以轻松处理这些情况。

  我们需要告诉WebGL怎样把提供的gl_Position裁剪空间坐标对应到画布像素坐标, 通常我们也把画布像素坐标叫做屏幕空间。为了实现这个目的,我们只需要调用gl.viewport 方法并传递画布的当前尺寸。

  这样就告诉WebGL裁剪空间的 -1 -> +1 分别对应到x轴的 0 -> gl.canvas.width 和y轴的 0 -> gl.canvas.height

  我们用0, 0, 0, 0清空画布,分别对应 r, g, b, alpha (红,绿,蓝,阿尔法)值, 所以在这个例子中我们让画布变透明了。

我们需要告诉WebGL运行哪个着色程序 :

  接下来我们需要告诉WebGL怎么从我们之前准备的缓冲中获取数据给着色器中的属性。 首先我们需要启用对应属性 。

然后指定从缓冲中读取数据的方式 :

  一个隐藏信息是gl.vertexAttribPointer是将属性绑定到当前的ARRAY_BUFFER。 换句话说就是属性绑定到了positionBuffer上。这也意味着现在利用绑定点随意将ARRAY_BUFFER绑定到其它数据上后,该属性依然从positionBuffer上读取数据。

从GLSL的顶点着色器中注意到a_position属性的数据类型是vec4:

  vec4是一个有四个浮点数据的数据类型。在JavaScript中你可以把它想象成a_position = {x: 0, y: 0, z: 0, w: 0}。之前我们设置的size = 2, 属性默认值是0, 0, 0, 1,然后属性将会从缓冲中获取前两个值( x 和 y )。 z和w还是默认值 0 和 1 。

我们终于可以让WebGL运行我们的GLSL着色程序了。

  因为count = 3,所以顶点着色器将运行三次。 第一次运行将会从位置缓冲中读取前两个值赋给属性值a_position.xa_position.y。 第二次运行a_position.xy将会被赋予后两个值,最后一次运行将被赋予最后两个值。

  因为我们设置primitiveType(图元类型)为 gl.TRIANGLES(三角形), 顶点着色器每运行三次WebGL将会根据三个gl_Position值绘制一个三角形, 不论我们的画布大小是多少,在裁剪空间中每个方向的坐标范围都是 -1 到 1 。

  由于我们的顶点着色器仅仅是传递位置缓冲中的值给gl_Position, 所以三角形在裁剪空间中的坐标如下:

  WebGL将会把它们从裁剪空间转换到屏幕空间并在屏幕空间绘制一个三角形, 如果画布大小是400×300我们会得到类似以下的转换 :

  现在WebGL将渲染出这个三角形。绘制每个像素时WebGL都将调用我们的片断着色器。 我们的片断着色器只是简单设置gl_FragColor1, 0, 0.5, 1, 由于画布的每个通道宽度为8位,这表示WebGL最终在画布上绘制[255, 0, 127, 255]

这里有一个在线示例:

  在上例中可以发现顶点着色器只是简单的传递了位置信息。 由于位置数据坐标就是裁剪空间中的坐标,所以顶点着色器没有做什么特别的事。 如果你想做三维渲染,你需要提供合适的着色器将三维坐标转换到裁剪空间坐标,因为WebGL只是一个光栅化API

  你可能会好奇为什么这个三角形从中间开始然后朝向右上方。裁剪空间的x坐标范围是 -1 到 +1. 这就意味着0在中间并且正值在它右边。

  至于它为什么在上方,是因为裁剪空间中 -1 是最底端 +1 是最顶端, 这也意味值0在中间,正值在上方。

  对于描述二维空间中的物体,比起裁剪空间坐标你可能更希望使用屏幕像素坐标。 所以我们来改造一下顶点着色器,让我们提供给它像素坐标而不是裁剪空间坐标。 这是我们新的顶点着色器。

  这里有些变化需要注意,我们将a_position改成vec2类型是因为我们只需要用xy值。 vec2vec4有些类似但是仅有xy值。

  接着我们添加了一个uniform(全局变量)叫做u_resolution,为了设置它的值我们需要找到它的位置。

  其余变化的应该能从注释中理解。通过设置u_resolution为画布的分辨率, 着色器将会从positionBuffer中获取像素坐标将之转换为对应的裁剪空间坐标。

  现在我们可以将位置信息转换为像素坐标。这次我们将通过绘制两个三角形来绘制一个矩形, 每个三角形有三个点。

  在我们设置好使用这个着色程序后,可以设置刚才创建的全局变量的值。gl.useProgram就与之前讲到的gl.bindBuffer相似,设置当前使用的着色程序。 之后所有类似gl.uniformXXX格式的方法都是设置当前着色程序的全局变量。

  显然我们需要告诉WebGL要运行六次顶点着色器来画两个三角形。 所以我们将count改成6

这里是结果

  注意: 这个和以后的例子都将使用webgl-utils.js, 它包含了编译和链接着色器的方法。没有必要让一些样板代码干扰示例代码。

  你可能注意到矩形在区域左下角,WebGL认为左下角是 0,0 。 想要像传统二维API那样起点在左上角,我们只需翻转y轴即可。

现在矩形在我们期望的位置了 。

  接下来让我们来定义一个可以生成矩形的方法,这样我们就可以调用它定义形状不一的多个矩形。 同时我们需要矩形的颜色是可设置的。

  首先我们定义一个片断着色器,可以通过全局变量接收自定义颜色。

  这里是一段新代码,可以随机绘制50个随机位置,随机大小,随机颜色的矩形。

这里是50个矩形。

  我希望你能体会到WebGL其实是一个非常简单的API。好吧,“简单”可能是一个不恰当的描述。 它做的是一件简单的事,它仅仅运行用户提供的两个方法,一个顶点着色器和一个片断着色器, 去绘制点,线和三角形。虽然做三维可以变得很复杂,但是这种复杂只是作为程序员的你, 是一种复杂形式的“着色器”。WebGL API只做光栅化处理并且在概念上十分容易理解。