webgl中绘制多个三角形不同颜色

505 阅读2分钟

获取webgl上下文

const canvas = document.getElementById('webgl');
const gl = canvas.getContext('webgl');

创建着色器

// 顶点着色器
const VSHADER_SOURCE = `
  precision mediump float;
  attribute vec2 a_Position;
  attribute vec2 a_Screen_Size;
  attribute vec4 a_color;
  // 新增一个v_color的 varying 变量
  varying vec4 v_color;
  void main() {
    vec2 position = (a_Position / a_Screen_Size) * 2.0 - 1.0;
    position = position * vec2(1.0,-1.0);
    gl_Position = vec4(position, 0, 1);
    v_color = a_color;
  }
`;
// 片元着色器
const FSHADER_SOURCE = `
  precision mediump float;
  varying vec4 v_color;
  void main() {
    gl_FragColor = v_color / vec4(255, 255, 255, 1); // 设置颜色
  }
`;

这里通过设置了 varying 类型的变量 v_color,这样就可以从顶点着色器向片元着色器传递数据

着色器初始化

// 创建着色器对象
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 传入对应的着色器代码(glsl语言)
gl.shaderSource(vertexShader, VSHADER_SOURCE);
gl.shaderSource(fragmentShader, FSHADER_SOURCE);
// 编译着色器代码
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// 创建着色器程序
const program = gl.createProgram();
// 把着色器对象添加到着色器程序中
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 链接着色器程序
gl.linkProgram(program);
// 使用着色器程序
gl.useProgram(program);
gl.program = program;

获取着色器中定义的属性

// 获取属性
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
const a_Screen_Size = gl.getAttribLocation(gl.program, 'a_Screen_Size');
const a_color = gl.getAttribLocation(gl.program, 'a_color');
// 只有一个顶点时,可以直接传过去(一次性传入多个顶点时,需使用缓冲区)
gl.vertexAttrib2f(a_Screen_Size, canvas.clientWidth, canvas.height);

设置缓冲区

const positions = [200, 200, 400, 200, 300, 300, 500, 200, 700, 200, 600, 300]; // 设置2个三角形的顶点坐标
const colors = [255, 0, 0, 1, 255, 0, 0, 1, 255, 0, 0, 1, 0, 255, 0, 1, 0, 255, 0, 1, 0, 255, 0, 1]; // 设置2个颜色 (因为执行一个顶点就会执行一次片元,因此每个顶点对应一个颜色)

const positionbuffer = gl.createBuffer(); // 创建三角形坐标的缓冲区
const colorBuffer = gl.createBuffer(); // 创建颜色的缓冲区

gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // 绑定当前缓冲区为 positionbuffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); // 给当前缓冲区传入浮点数据
gl.enableVertexAttribArray(a_Position); // 启用顶点着色器中对应的属性
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); // 将该属性绑定到当前缓冲区

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); // 绑定当前缓冲区为 colorBuffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); // 给当前缓冲区传入浮点数据
gl.enableVertexAttribArray(a_color); // 启用顶点着色器中对应的属性
gl.vertexAttribPointer(a_color, 4, gl.FLOAT, false, 0, 0); // 将该属性绑定到当前缓冲区

注意:这里进行了2次绑定,我们通过 gl.vertexAttribPointer 将属性绑定到了当前的缓冲区,即使之后我们使用 bindBuffer 绑定到其他缓冲区时,a_Position 也依然会从 positionbuffer 这个缓冲区中获取数据。

还有一种方式是把 坐标信息 positions 与 颜色信息 colors合并在一起,如下形式[顶点1, 颜色1, 顶点1, 颜色1, 顶点1, 颜色1, 顶点2, 颜色2, 顶点2, 颜色2, 顶点2, 颜色2], 然后 再各自的缓冲区中设置vertexAttribPointer 对应的参数即可

绘制

gl.clearColor(0 , 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
const primitiveType = gl.TRIANGLES; // 绘图单元为三角形
const offset = 0; // 从第几个顶点开始绘制
const count = 6; // 绘制顶点的个数
gl.drawArrays(primitiveType, offset, 6);

注意:给不同三角形绘制颜色的关键点就是颜色缓冲区的数量一定要和顶点数量一致