css绘图-WebGL HelloWorld

四种常用的页面绘制工具

关于h5页面的图形轮廓,我们主要讲这四种工具:html+css、svg、canvas2d、webgl。

Html+css是最常见的绘图工具。 使用css来绘制和编写页面布局是一样的。 在绘制图表时,我们可以使用css来定义图表的样式。 其他则基于不同的数据。 元素添加不同的属性。 这种开发对于图表元素简单、数据节点较少的场景非常友好。 除了减少开发工具数量之外,还不需要引入多余的代码库。 而且,随时需要绘制的图形越来越多,css代码也越来越复杂。 另外,css没有逻辑语义css绘图,代码会变得难以阅读和维护。

svg 是一种可缩放矢量图形。 它与html、css紧密结合。 可以使用svg作为img的src,也可以使用css来操作svg的属性。 svg 和 html 都是文本标记语言。 与html相比,svg减少了对非线性图形的支持,包括圆弧、贝塞尔曲线等,同时svg支持可复用的句型如 等,这使得他保持了一定的可读性即使他勾勒出许多图形,也可以编写代码。 然而,svg 也有一些缺点。 由于图是一个元素节点。 当数据很多的时候,页面刷新带来的布局和渲染预估的成本会特别大。 而且完整的svg把结构、样式、复用逻辑都放在了一起,比html+css+js各自独立的模式不太整洁。

Canvas2D是canvas的2D绘图上下文。 它提供了一系列在画布区域中更改和绘制图像的方法。 与前三种的开箱即用相比,canvas2d 有很多图形和颜色需要自己实现。 封装使得开始使用这个工具变得更加困难。 不过,如果你把这个基础的事情做好了,你就会拥有一个完全覆盖上述两个工具并且易于扩展的绘图工具。

webGL也是canvas的绘图上下文,是opengles的web实现。 最大的特点是下层可以直接使用GPU的并行能力。 在大量图形处理、像素级处理、3D物体处理等场景下具有高性能优势。

四种工具的选择

当我们收到一个绘图请求时,我们首先应该看看这个请求中使用的图形是否比较少,但是简单。 如果是的话,可以直接选择css进行快速开发。 如果图形简单但数量较多,或者图形有一些曲线要求,SVG可以快速处理。 如果图形之间结构复杂,数量较多,则选择canvas2d。而当图形数量大到一定数量,或者需要对每个像素进行处理,或者需要大量的3D显示时,我们就不得不使用 webgl

webgl 你好世界

WebGL的helloworld并不像其他工具那样用一两行代码就可以完成,而是有四十多行代码。 其实这串代码在各个3D渲染库中都有对应的封装方法,基本上我们不需要手写,学习这串代码可以让我们对webgl的绘制流程有一个基本的了解。

webgl绘图有五个步骤:

创建 webgl 绘图上下文 创建着色器编程,关联到 gl 上下文(与步骤 3 并行)创建数据,倒入缓冲区并将缓冲区关联到 gpu 加载缓存中的 gl 上下文(与步骤 2 并行) 数据草图图形创建 Webgl 上下文

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

创建着色器程序

const program = gl.createProgram();
gl.attachShader(program, /*某个着色器(下文的vertexShader)*/);
gl.linkProgram(program);
gl.useProgram(program);

着色器是供 GPU 运行的程序。 我们使用glCreateProgram创建一个空的程序对象,然后使用glAttachShader用编译后的着色器代码填充程序对象。 我们来谈谈什么是着色器以及如何编译它们。 这里我们可以将它们视为某个函数的编译代码。 将这些编译好的几个函数加载到程序对象中后,GPU执行程序对象时,就会以像素信息作为输入参数,依次执行程序对象中的函数。

填充着色器代码后,调用glLinkProgram将程序与gl上下文关联起来,并使用glUseProgram启用程序。

拿出来,我们来看看如何把shader代码弄下来。

const vertex = `
      attribute vec2 position;
      void main() {
        gl_Position = vec4(position, 1.0, 1.0);
      }
    `;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);

首先我们定义一个变量vertex,并给他一串其他语言格式的代码串作为形式参数。 这串代码就是glsl代码,这是一种和C语言非常相似的代码。 该代码接收传入的二维向量位置,然后将其执行环境中的全局变量 gl_Position 设置为四维向量。 这个四维向量的前两个维度是传入的二维向量。

接下来,使用 glCreateShader 创建着色器。 VERTEX_SHADER 常量指示该着色器是顶点着色器。 顶点着色器对应于片段着色器。 顶点着色器处理确定点的位置。 片段着色器将顶点组成的图中的所有位置一一处理。 例如,两点画一条直线。 这两个点由顶点着色器确定。 片段着色器在确定两点的位置后绘制直线。 。

当我们创建了一个空的顶点着色器对象vertexShader之后,我们可以使用glShaderSource将下面的字符串代码加载到顶点着色器对象中,然后使用glCompileShader将这段代码编译成可执行文件。 这个过程类似于C语言的编译过程。

gl.attachShader(program, /*某个着色器(下文的vertexShader)*/);
gl.attachShader(program, vertexShader);

完成这一步后,需要回到之前的注释,将shader对象与program对象关联起来。 事实上,您必须编写一个片段着色器,并使用相同的步骤将片段着色器与程序对象关联起来。

将数据存储在缓冲区中

const points = new Float32Array([-1, -1, 0, 1, 1, -1]);
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

经过上述操作,我们已经有了一个加载了着色器代码的程序对象。 该对象在 gl 绘图上下文中启用。 那么,我们要定义的就是这个程序所使用的数据。

在顶点着色器中,代码接受传入的二维向量,这就是我们现在要定义的向量。 首先,定义一个类型字段并在初始化期间输入 6 个数字。 这6个数字将被绘图程序分为三组并放置在三个顶点着色器调用中。 另外,使用类型化字段是为了优化性能,这样在数据量很大的情况下,数据占用的空间更少。

有了数据,调用glCreateBuffer创建一个缓冲区对象,使用glBindBuffer将该对象与gl绘图上下文关联起来,最后调用glBufferData将点数据加载到缓冲区中。

GPU加载缓存中的数据

const vPosition = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);

这一步我们首先调用glGetAttribLocation获取位置变量在程序对象中的位置,调用glVertexAttribPointer设置这个变量的宽度为2,设置类型为glFLOAT,使用glEnableVertexAttribArray启用这个变量

素描图形

gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);

最后一步,只需使用glClear清除颜色缓冲区,然后使用glDrawArrays进行绘制即可。其中,gl.TRIANGLES决定了片段着色器的绘制范围。 当这个值为gl.POINTS时,着色器会将点两两连接起来,gl.TRIANGLES让第三个点形成一个组来绘制三角形。

这样就完成了一个webgl的helloworld,里面的三角形就是这40行代码输出的图像。

总结

本程序在Three.js等3D框架和工具库中有一定的封装。 使用这些库来绘制webgl相对来说是比较方便的,但是如果你不知道这个库的最基本的操作,那么你在遇到问题的时候就很容易绕过去了。 所以希望这篇文章能够提高你对web3d底层的理解css绘图,为你学习这个3d工具库时提供一些帮助。