WebGL07

多个缓冲区对象

const vertices = new Float32Array([
  -.6, -.6,
  0., .8,
  .6, -.6,
])

const colors = new Float32Array([
  1., 0., 0., 1., // 红色
  0., 1., 0., 1., // 绿色
  0., 0., 1., 1., // 蓝色
])

对应的数据准备好之后,我们就可以继续创建我们的缓冲区了。

  1. 创建缓冲区对象。
  2. 绑定缓冲区对象到target。
  3. 分配缓冲区数据。
  4. 将缓冲区分配到attribute变量。
  5. 开启attribute变量。 这样我们就通过建立多种缓冲区传递不同种类的数据,当使用gl.drawArrays绘制时,各种数据将按照其缓冲区中的顺序一一传递到顶点着色器的attribute变量中。
var canvas = document.getElementById("canvas");

canvas.width = canvas.height = 600;

const gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
const vertexCode = `
  // 顶点坐标数据
  attribute vec4 a_Position;
  // 颜色数据
  attribute vec4 a_Color;
  // varying 变量传递到片元着色器
  varying vec4 v_Color;

  void main () {
    gl_Position = a_Position;
    v_Color= a_Color;
  }
`;

const fragmentCode = `
  precision mediump float;
  // 颜色值变量
  varying vec4 v_Color;

  void main () {
    gl_FragColor = v_Color;
  }
`;

//着色器代码的初始化
// 绘制实现

document.getElementById("redFn").addEventListener("click", () => {
  gl.clear(gl.COLOR_BUFFER_BIT);
});

function createShader({ glslCode, type }) {
  // 创建对应类型着色器
  const shader = gl.createShader(type);
  // 传入对应着色器代码
  gl.shaderSource(shader, glslCode);
  // 编译着色器
  gl.compileShader(shader);
  return shader;
}

// 创建顶点着色器
const vertexShader = createShader({
  glslCode: vertexCode,
  type: gl.VERTEX_SHADER
});
// 创建片元着色器
const fragmentShader = createShader({
  glslCode: fragmentCode,
  type: gl.FRAGMENT_SHADER
});

// 创建着色器程序
const program = gl.createProgram();

// 分别添加两个着色器 gl.attachShader
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

// 链接两个着色器
gl.linkProgram(program);
gl.useProgram(program);

// 动态修改顶点位置
const a_Position = gl.getAttribLocation(program, "a_Position");
const a_Color = gl.getAttribLocation(program, "a_Color");

// 绘制多个点
// const vertices = new Float32Array([0, 0.8, -0.6, -0.6, 0.6, -0.6]);
// 本节的顶点坐标
const vertices = new Float32Array([-0.5, 0.5, 0, -0.9, 0.5, 0.5]);

const colors = new Float32Array([
  1.0,
  0.0,
  0.0,
  1.0,
  0.0,
  1.0,
  0.0,
  1.0,
  0.0,
  0.0,
  1.0,
  1.0
]);
function createBuffer({ gl, target, bufferData, attribute, size }) {
  const buffer = gl.createBuffer();
  gl.bindBuffer(target, buffer);
  gl.bufferData(target, bufferData, gl.STATIC_DRAW);
  gl.vertexAttribPointer(attribute, size, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(attribute);
}

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);
createBuffer({
  gl,
  target: gl.ARRAY_BUFFER,
  bufferData: colors,
  attribute: a_Color,
  size: 4
});
gl.clearColor(0.0, 0.0, 0.0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// gl.drawArrays(gl.LINE_STRIP, 0, 3);
// gl.drawArrays(gl.TRIANGLES, 0, 3);
// gl.drawArrays(gl.POINTS, 0, 3);
// gl.drawArrays(gl.LINES, 0, 6);
// gl.drawArrays(gl.LINE_STRIP, 0, 6);
// gl.drawArrays(gl.LINE_LOOP, 0, 6);

// gl.drawArrays(gl.TRIANGLES, 0, 6);
// gl.drawArrays(gl.TRIANGLE_STRIP, 0, 6);
gl.drawArrays(gl.TRIANGLES, 0, 3);

canvas.addEventListener("click", (e) => {
  const halfWidth = canvas.width / 2;
  const halfHeight = canvas.height / 2;
  const x = (e.offsetX - halfWidth) / halfWidth;
  const y = (halfHeight - e.offsetY) / halfHeight;
  // gl.clear(gl.COLOR_BUFFER_BIT);
  gl.vertexAttrib2f(a_Position, x, y);
  // gl.vertexAttrib4f(a_Color, Math.random(), Math.random(), Math.random(), 0.9);

  //动态设置颜色值
  gl.drawArrays(gl.POINTS, 0, 1);
});

gl.vertexAttribPointer的神奇参数

通过参数配置,我们可以将多种类数据存放在一个缓冲区对象中。

  1. index 表示attribute变量地址。
  2. size 每个顶点分配到缓冲区数据的个数。范围[1-4]
  3. type 数据格式
  4. normalize boolean值 是否将非浮点数转换时归一到[0,1]或[-1,1] 对于float类型无效
  5. stride 指两个顶点之间的字节数 默认是0.
  6. offset 指定缓冲区数据的偏移量(单位是字节)若为开始的位置则是0。

stride参数详解

指两个顶点之间的字节数 默认是0。 这个间距字节数其实就和顶点自身字节数一致。
TypedArray.BYTES_PER_ELEMENT 表示强类型数组中每个元素所占的字节数。

const verticesColors = new Float32Array([
  0.0, 0.0, 1.0, 0.0, 0.0, // 第一个点包括坐标、颜色rgb
  0.1, 0.1, 1.0, 0.0, 0.0, // 第二个点包括坐标、颜色rgb
  0.2, 0.2, 1.0, 0.0, 0.0, // 第三个点包括坐标、颜色rgb
])
// 每个元素占用的字节数
const FSIZE = verticesColors.BYTES_PER_ELEMENT
// 每个顶点数据总共有 5 个浮点数
stride参数 = FSIZE * 5

offset参数详解

表示一组数据距离首个类型数据元素的距离。

// 获取坐标数据
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
// 获取颜色数据
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2)

通过这个参数再配合我们的size就可以告诉WebGl我们的数据从哪里获取。
使用一个缓冲区对象和多个缓冲区对象实现渐变三角的区别。

// 顶点 坐标数据、颜色数据放在一起
const verticesColors = new Float32Array([
  -.6, -.6, 1., 0., 0., 1.,
  0., .8, 0., 1., 0., 1.,
  .6, -.6, 0., 0., 1., 1.,
])

// 分配坐标数据,注意看 size分配个数、 stride步进参数 和 offset偏移参数 的值
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 6, 0)
gl.enableVertexAttribArray(a_Position)

// 分配颜色数据,注意看 size分配个数、 stride步进参数 和 offset偏移参数 的值
gl.vertexAttribPointer(a_Color, 4, gl.FLOAT, false, FSIZE * 6, FSIZE * 2)
gl.enableVertexAttribArray(a_Color)

写在最后

未完待续…
希望大家好好生活,健康快乐。