WebGL 同一缓冲区多种数据传入顶点着色器 gl.vertexAttribPointer()的步进和偏移参数

目录

目录

为了将顶点坐标传入着色器,需要遵循一下五步:

但是!!!

示例代码:

gl.vertexAttribPointer()的函数规范

stride参数

顶点坐标数据

offset参数 

顶点尺寸数据


首先,分析如何实现下方示例为三个不同大小位置的点

WebGL 同一缓冲区多种数据传入顶点着色器 gl.vertexAttribPointer()的步进和偏移参数_第1张图片

为了将顶点坐标传入着色器,需要遵循一下五步:

1. 创建缓冲区对象

2. 将缓冲区对象绑定到target上

3. 将顶点坐标数据写入缓冲区对象

4. 将缓冲区对象分配给对应的attribute对象

5. 激活开启attribute变量 

当然你可以创建两个缓冲区对象分别存储坐标数据和非坐标数据,随之传给各自对应的着色器变量 a_Position 和 a_pointSize

WebGL 同一缓冲区多种数据传入顶点着色器 gl.vertexAttribPointer()的步进和偏移参数_第2张图片

但是!!!

 使用多个缓冲区对象向着色器传递多种数据,比较适合数据量不大的情况。当程序中的复杂三维图形具有成千上万个顶点时,维护所有的顶点数据是很困难的。想象一下,如果程序中的三维模型有几千几万个顶点会怎样。所以,WebGL允许我们把顶点的坐标和尺寸数据打包到同一个缓冲区对象中,并通过某种机制分别访问缓冲区对象中不同种类的数据。比如,可以将顶点的坐标和尺寸数据按照如下方式交错组织(interleaving),如下图所示:

WebGL 同一缓冲区多种数据传入顶点着色器 gl.vertexAttribPointer()的步进和偏移参数_第3张图片
可见,一旦我们将几种“逐顶点”的数据(坐标和尺寸)交叉存储在一个数组中,并将数组写入一个缓冲区对象。WebGL就需要有差别地从缓冲区中获取某种特定数据(坐标或尺寸),即使用gl.vertexAttribPointer()函数的第5个参数stride和第6个参数offset。下面让我们来看看代码。

示例代码:

// 顶点着色器程序
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute float a_PointSize;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  gl_PointSize = a_PointSize;\n' +
  '}\n';

// 片元着色器程序
var FSHADER_SOURCE =
  'void main() {\n' +
  '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
  '}\n';

function main() {
  // Retrieve  element
  var canvas = document.getElementById('webgl');
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }
  // 设置顶点坐标和点的尺寸
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.POINTS, 0, n);
}

function initVertexBuffers(gl) {
  var verticesSizes = new Float32Array([
    // 顶点坐标和点的尺寸
     0.0,  0.5,  10.0,  // 第一个点
    -0.5, -0.5,  20.0,  // 第二个点
     0.5, -0.5,  30.0   // 第三个点
  ]);
  var n = 3; // The number of vertices

  // 创建缓冲区对象
  var vertexSizeBuffer = gl.createBuffer();  
  if (!vertexSizeBuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }
  // 将顶点坐标和尺寸写入缓冲区并开启
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexSizeBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesSizes, gl.STATIC_DRAW);
  var FSIZE = verticesSizes.BYTES_PER_ELEMENT;
  // 获取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 -1;
  }
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0);
  gl.enableVertexAttribArray(a_Position);  // 开启分配
  // 获取a_PointSize的存储位置,分配缓冲区并开启
  var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
  if(a_PointSize < 0) {
    console.log('Failed to get the storage location of a_PointSize');
    return -1;
  }
  gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);
  gl.enableVertexAttribArray(a_PointSize);  // 开启缓冲区分配
  return n;
}

首先,我们定义了一个类型化数组verticesSizes,接下来:创建缓冲区对象,绑定之,把数据写入缓冲区对象。然后,我们将verticeSize数组中每个元素的大小(字节数)存储到FSIZE中,稍后将会用到它。类型化数组具有BYTES_PER_ELEMENT属性,可以从中获知数组中每个元素所占的字节数。 

注意,这里的verticesSizes参数设置就与单纯的传入顶点坐标有所不同了,因为在缓冲区对象中存储了两种类型的数据:顶点坐标和顶点尺寸。

gl.vertexAttribPointer()的函数规范

WebGL 同一缓冲区多种数据传入顶点着色器 gl.vertexAttribPointer()的步进和偏移参数_第4张图片

stride参数

参数stride表示,在缓冲区对象中,单个顶点的所有数据(这里,就是顶点的坐标和大小)的字节数,也就是相邻两个顶点间的距离,即步进参数。 

顶点坐标数据

在缓冲区只含有一种数据时,例顶点的坐标,我们将其设置为0即可。然而,在这里中,当缓冲区中有了多种数据(比如这里的顶点坐标和顶点尺寸)时,我们就需要考虑参数stride的值,如下图所示:

WebGL 同一缓冲区多种数据传入顶点着色器 gl.vertexAttribPointer()的步进和偏移参数_第5张图片

 如上图所示,每一个顶点有3个数据值(两个坐标数据和一个尺寸数据),因此应该设置为每项数据大小的三倍,即3×FSIZE(Float32Array中每个元素所占的字节数)。

offset参数 

参数offset表示当前考虑的数据项距离首个元素的距离,即偏移参数。在verticesSizes数组中,顶点的坐标数据是放在最前面的,所以应当为0。因此,我们调用gl.vertexAttribArray()函数时,如下所示传入stride参数和offset参数

这样一来,我们就把缓冲区中的那部分顶点坐标数据分配给了着色器中的attribute变量a_Position,并开启了该变量。 

顶点尺寸数据

接下来对顶点尺寸数据采取相同的操作:将缓冲区对象中的顶点尺寸数据分配给a_PointSize。然而在这个例子中,缓冲区对象还是原来那个,只不过这次关注的数据不同,我们需要将参数设置为顶点尺寸数据在缓冲区对象中的初始位置。在关于某个顶点的三个值中,前两个是顶点坐标,后一个是顶点尺寸,因此应当设置为FSIZE*2(参见上面stride解释图)。我们如下调用gl.vertexAttribArray()函数,并正确设置stride参数和offset参数

 在开启已被分配的缓冲区对象的a_PointSize变量之后,剩下的任务就只有调用gl.drawArrays()进行绘制操作了。

再次执行顶点着色器时,WebGL系统会根据stride和offset参数从缓冲区中正确地抽取出数据,依次赋值给着色器中的各个attribute变量,并进行绘制(如下图所示)。

WebGL 同一缓冲区多种数据传入顶点着色器 gl.vertexAttribPointer()的步进和偏移参数_第6张图片

你可能感兴趣的:(WebGL,webgl,着色器)