前端可视化:使用WebGL绘制多个点

748 阅读4分钟

WebGL如何绘制多个点

1.获取webgl的context

相信用到webgl的小伙伴对canvas也是很熟悉了,类似于canvas,我们首先要获取到webgl的context

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

命名习惯看个人爱好,这里习惯把webgl的context命名为gl

2.编写着色器源码

使用着色器语言(GLSL ES)编写着色器源码

        // 顶点着色器源码
const vsSource = `
            attribute vec4 a_Position;
            attribute float a_PointSize;

            void main() {
                gl_Position = a_Position;
                gl_PointSize = a_PointSize;
            }
            `;

        // 片源着色器源码
const fsSource = `
            precision mediump float;
            uniform vec3 u_FragColor;

            void main() {
                gl_FragColor = vec4(u_FragColor, 1.0);
            }
        `;

特别注意:写完记得写分号!否则将会报错

uniform与attribute类似,都是存储限定符,主要的区别如下:

attribute

只能用于顶点着色器,用来表示逐顶点的数据(每个顶点中该值不一样)

uniform

可以用于顶点着色器,也可以用于片元着色器,用来表示一致的、不变的数据(每个顶点中该值都一样)

片源着色器需要声明浮点数精度precision lowp/mediump/highp float

3.创建着色器对象

// 创建顶点着色器
const vsShader = gl.createShader(gl.VERTEX_SHADER);

// 创建片源着色器
const fsShader = gl.createShader(gl.FRAGMENT_SHADER);

4.将源码注入到对应的着色器中

// 把源码注入到对应的着色器中
gl.shaderSource(vsShader, vsSource);
gl.shaderSource(fsShader, fsSource);

5.编译着色器代码并检查

// 编译着色器代码
gl.compileShader(vsShader);
gl.compileShader(fsShader)
// 判断着色器是否创建成功
if (!gl.getShaderParameter(vsShader, gl.COMPILE_STATUS)) {
    console.error(`shader compile failed: ${gl.getShaderInfoLog(vsShader)}`);
    gl.deleteShader(vsShader);
};

6.创建着色程序

js与webgl内部交流主要依靠这个程序,这步可不能忘

// 创建一个空的着色程序
const program = gl.createProgram();

7.将着色器注入程序

// 将着色器注入程序
gl.attachShader(program, vsShader);
gl.attachShader(program, fsShader);

8.连接着色程序并判断是否成功

// 连接程序
gl.linkProgram(program);
// 判断程序是否创建成功
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.error(`program created failed: ${gl.getProgramInfoLog(program)}`);
    gl.deleteProgram(program);
};

9.使用程序

gl.useProgram(program);

10.创建类型化数组存放顶点数据

这里是一次性传入一组坐标数据

// 存放顶点数据
const vertices = new Float32Array([
    0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5
]);

注意:向buffer写入的数据不是一般数组,需要new类型化数组,否则报错,错误信息如下:

GL ERROR :GL_INVALID_OPERATION :glDrawArrays: attempt to access out of range vertices in attribute 0

因为GLSL ES是强类型语言,js是弱类型的,js数组元素没有限制,而GLSL ES需要非常严格的数据,所以才有了类型化数组。在webgl中,类型化数组有以下8种:

Int8Array

UInt8Array

Int16Array

UInt16Array

Int32Array

UInt32Array

Float32Array

Float64Array

11.创建buffer

buffer是webgl系统内部划分出的一块区域,可以存放一组顶点数据,比如顶点坐标,把顶点数据写入buffer后,着色器程序执行时就可以从buffer中读取数据,在数据量较大时尤为方便

// 创建缓冲对象
const buffer = gl.createBuffer();

12.缓冲器与着色器连接

因为无法直接向缓冲区写入数据,只能向target写入,所以要向缓冲区写数据,必须要先绑定target与buffer。target表示缓冲区对象的用途,值为gl.ARRAY_BUFFER或者gl.ELEMENT_ARRAY_BUFFER,前者表示缓冲区对象包含了顶点数据,后者表示包含了顶点的索引值

// 缓冲器与着色器建立连接
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

13.往缓冲器写入数据

// 往缓冲器写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

通过gl.bufferData(target, data, usage)写入数据

data就是我们上面所说的类型化数组

usage表示缓冲区数据用途,WebGL会根据用途进行优化,值为gl.STATIC_DRAW(只向缓冲区对象写入一次数据,但需要绘制很多次)、gl.STREAM_DRAW(只向缓冲区对象中写入一次数据,然后绘制若干次)或gl.DYNAMIC_DRAW(向缓冲区对象多次写入数据,并绘制很多次),区别不很明显,而且只可能会影响性能,不会影响最终结果

14.将缓冲区对象分配给a_Position

// 将缓冲区对象分配给a_Position
const a_Position = gl.getAttribLocation(program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

gl.vertexAttribPointer(location, size, type, normalized, stride, offset)具体参数含义如下:

size        //缓冲区中每个顶点的分量个数(1~4),表示每个顶点数据中要赋值给着色器变量的分量个数
type        //数据格式,值为gl.UNSIGNED_BYTE、gl.SHORT、gl.UNSIGNED_SHORT、gl.INT、gl.UNSIGNED_INT、gl.FLOAT五种
normalized  //true|false,表示是否把非浮点型数据归一化到[0, 1]或者[-1, 1]区间
stride      //指定相邻两个顶点间的字节数,默认为0
offset      //缓冲区对象中的偏移量,从缓冲区的offset位置开始写,从头开始就是0

15.开启顶点数据处理

// 开启顶点数据处理
gl.enableVertexAttribArray(a_Position);

16.设置像素点大小和颜色

像素点大小和颜色也可以在写源码时直接设定死

// 像素点大小
const a_PointSize = gl.getAttribLocation(program, 'a_PointSize');
gl.vertexAttrib1f(a_PointSize, 10.0);
// 颜色
const u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
gl.uniform3f(u_FragColor, 0.5, 0.0, 1.0);

17.设置绘制的窗口

// 设置绘制的窗口
gl.viewport(0, 0, canvas.width, canvas.height);

18.清除画布

// 清除画布颜色
gl.clearColor(0, 0, 0, 0)
// 清除颜色缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);

19.开始绘制

        // 绘制点
        gl.drawArrays(gl.POINTS, 0, 5);