准备
要真正了解一个engine,肯定需要掌握webgl,万丈高楼平地起,无论engine的功能多么丰富,在浏览器中底层接口必定使用的是canvas,而webgl则是这一切的源头。
你可能听说过矩阵,不过从0编写一个webgl程序,我们暂时还用不到这么高深的知识,不要被矩阵所吓倒,让我们先从最基本的开始吧。
第一个webgl程序
我们先看下本节教程的目标效果:在渲染区域的中心绘制了一个红色的点
看起来并不是太复杂,其实实现的代码也就30行:
- html
<canvas id="canvas"></canvas>
- js
const canvas = document.getElementById("canvas"); const gl = canvas.getContext("webgl"); // 背景色 gl.clearColor(0, 1, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); // 顶点着色器 const vs = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vs, `void main() { gl_Position = vec4(0.0,0.0,0.0, 1.0); gl_PointSize = 10.0; }`); gl.compileShader(vs); // 片段着色器 const fs = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fs, `void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }`); gl.compileShader(fs); // 链接使用shader const program = gl.createProgram(); gl.attachShader(program, vs); gl.attachShader(program, fs); gl.linkProgram(program); gl.useProgram(program); // 绘制 gl.drawArrays(gl.POINTS, 0, 1);
为了方便查看效果,我将这段代码放在了CodePen,你可以直接打开这个链接直接运行。
大概的步骤:
- 获取gl上下文
- 创建、编译着色器,主要是顶点着色器和片段着色器
- 创建program,附加着色器,并链接使用program
- 绘制DrawCall
这里面有几个概念:
- shader:GLSL语言编写的一段运行在GPU上的代码,游戏中各种炫酷的效果,都需要编写对应效果的shader
- program一般都是由2个shader组成,program更像是一对shader组合,当后续渲染时,我们只需要切换program,就能在不同的shader组合之间切换,以此来绘制更加复杂的效果。
- draw:webgl提供了非常多的draw函数,教程中用到的只是其中的一个,我们经常听到术语dc,其实就是draw call的意思,说的再直白一点,就是调用一个draw函数。
draw call为100,意思就是调用了draw函数100次
。
为什么我要把这个DC解释的这么清楚呢?因为在我个人的游戏开发生涯中,这种非常基础非常基础的概念,特别是行业术语相关的,如果没有人给你领路,你可能需要花费非常久的时间才能真正体会到其含义。你认为非常基础的知识可能对别人来说就需要花费非常大的力气才能领悟到,这和智力没有关系,就好比最近我在学习音视频开发一样,因为刚入门,我问的一些问题非常基础,可能有些问题网上真的就查不到相关资料,因为每个行业领域都有自己的术语,甚至方言!
短短的28行代码,我并没有加入一些校验容错逻辑的代码,并且已经将代码精简到极致了,目的就是排查学习干扰,一针见血的看到核心逻辑。
webgl状态机
状态机是WebGL里面非常重要的一个概念,它是webgl渲染的基本原理,对于理解webgl十分重要。
我们会发现getContext
就是在获取gl的handler,