关于webgl具有哪方面的优势,已经是个老生常谈的问题了,主要是利用了其并行化的优势,这对于图形学领域尤为重要,可以利用这个优势大批量绘制复杂图形,其次,webgl相对于别的图形系统,类似canvas、svg等,webgl相对而言更底层一些,可以完整参与一个图形生成到渲染的每一个细节,这也是我在稍微了解了three.js的相关api后选择直接all in webgl的理由。
<body> <canvas id="canvas"></canvas> </body>首先对比一下canvas的绘图流程,首先会有一个上下文,对于webgl也是同理:
const canvas = document.querySelector('#canvas'); const gl = canvas.getContext('webgl');接下来我们就需要了解着色器的概念了,就是大名鼎鼎的shader。着色器分为顶点着色器与片元着色器,两者都是采用GLSL语法编写,关于语法的细节后面会介绍,熟悉简单C语言即可看懂代码,这两者的区别在于:顶点着色器负责处理图形顶点信息,片元着色器负责处理图形的颜色信息!
const vertex = ` attribute vec2 position; void main() { gl_PointSize = 1.0; gl_Position = vec4(position, 1.0, 1.0); } `; // 堆代码 duidaima.com const fragment = ` precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `;初学者看到这GLSL写的东西肯定是一脸懵逼,我们仅针对当前的例子解释一下吧:
// 创建shader对象 const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertex); gl.compileShader(vertexShader); const fragmentShader = gl.createShader(gl.FRAG MENT_SHADER); gl.shaderSource(fragmentShader, fragment); gl.compileShader(fragmentShader);首先是创建shader对象,用gl.createShader分别创建顶点着色器与片元着色器对象,gl.shaderSource将shader源码分配给着色器对象,最后gl.compilerShader用于编译
// 创建webglProgram对象 关联shader const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program);其次是创建webglProgram对象,gl.createProgram用于创建着色器程序,gl.attachShader用于将相应的着色器挂载在着色器程序上,接下来gl.linkProgram用于链接着色器程序。通常我们应用可能会包括多个webglProgram对象,所以在使用前还需要启用之,即gl.useProgram 。
以上是创建webgl应用程序的基本工作流,基本上是调用webgl基础api的过程,虽然有点绕但不算难,后续也可以抽离出来。
gl.vertexAttribPointer(target, size, type, normalize, stride, offset) // targe较好理解,这里是目标属性vPosition // size: 可以理解为一次性取几个值,因为这里position是vec2类型,所以size为2 // type: 这里为浮点型 // normalize: 是否要归一化到[-1, 1]的区间内 // stride: 步长默认为0,每个顶点所包含的字节数,这里一个节点包含2个Float变量,步长为 2* 4 = 8,如果多个属性共用同一个缓存区,需要定义该属性 // offset: 偏移量默认为0,指偏移多少字节开始读取目标属性然后启用对应的属性gl.enableVertexAttribArray(vPosition);到这一步为止,我们可以将javascript中定义的变量,通过buffer方式让shader读取到了,最后一步就是绘制了。
gl.clear(gl.COLOR_BUFFER_BIT); // 先清除屏幕 gl.drawArrays(gl.TRIANGLES, 0, 3); // 绘制三角形图元
关于顶点着色器,还要补充的是,他除了可以设置顶点坐标等属性外,还可以向片元着色器传递数据。
举个例子,我们在顶点着色器与片元着色器中分别定义一个变量varying vec3 color,然后我们将color映射为与position相关,映射关系为x = 0.5 + position * 0.5,由于position落在[-1, 1]区间内,那么x落在[0, 1]区间内,维度与position一致,也是vec2,重新构造的color = vec3(0.5 + position * 0.5, 0.0),这样color与position之间就建立起了联系,经过映射三个顶点不再是同一个颜色,而是红绿黑三种颜色,片元着色器会根据像素的坐标进行线性插值,最后呈现的就是渐变的效果了。