在上一节,我们成功使用webgl绘制了一个点,这一节我们就来实现让这个点动起来。
分析:如何让点动起来?
先复习下上节课的shader:
- vertex shader
void main() { gl_Position = vec4(0.0,0.0,0.0, 1.0); gl_PointSize = 10.0; }
- fragment shader
void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }
运动的本质就是坐标的变化,我们想要修改坐标,其实就是要修改gl_Position
,也就是说我们需要在vertex shader定义一个变量,同时程序能够控制变量的值。
修改点的位置
因为shader使用的是glsl语言,这里就需要用到attribute, attribute变量只能定义在vertex shader中,attribute更像是一个修饰符,表示这是一个可以被js代码修改的属性(为了方便理解暂且这么解释,更专业的解释你需要看glsl语言规范)。
- vertex shader修改
attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos.x,a_pos.y,0.0, 1.0); gl_PointSize = 10.0; }
- 在shader中我们定义了a_pos属性,然后将a_pos.x、a_pos.y分别传递给了gl_Position,当我们通过代码修改a_pos后,gl_Postion也会随之改变。
- 在js代码中,我们可以这样传递数据给a_pos
// 放到program之后,从vertex shader里面获取a_pos属性 const a_pos = gl.getAttribLocation(program, "a_pos"); // 设置a_pos属性值 gl.vertexAttrib2f(a_pos, 0.9, 0.9);
重新运行长须观察到点的位置渲染到了右上角,通过这个表现,不难推理出,webgl的坐标是[-1, 1]
实现:让点动起来!
原理也非常简单,不停的改变a_pos属性即可:
// ... let x = -1; setInterval(() => { x += 0.1; if (x > 1) { x = -1; } gl.vertexAttrib2f(a_pos, x, 0); gl.drawArrays(gl.POINTS, 0, 1); }, 100)
这样子,我们就看到了一个红点,从左往右移动,完整的代码我放到了CodePen。
整体思路回顾:
vertex shader
增加attribute vec2 a_pos
,并且将a_pos的值赋给gl_Position
- 通过
getAttribLocation
获取a_pos
- 通过
vertexAttrib2f
设置a_pos
的值,并调用draw进行渲染绘制
- 使用定时器,改变
a_pos
,进而实现点的移动效果
定时器的一个callback,我们通常称为一帧,要绘制出复杂的游戏画面,通常需要进行多次的draw函数调用,在这一帧中我们调用了几次draw函数,那么我们通常就说drawcall是多少,drawcall是性能优化的一个非常重要的指标。
当然以上的代码非常的简陋,在实际项目中我们也不会使用setInterval驱动渲染更新,因为无法保证100ms,但是这个思路是通用的。
游戏引擎其实就是一个死循环,每次循环开始都要擦除上一次的绘制结果,然后计算出当前帧的绘制数据,多次调用draw函数进行渲染,希望到这里,能让你对游戏引擎有更深层次的理解。
拓展
上边的代码中,我们如果要绘制多个不同颜色、不同位置、不同大小的点,就比较麻烦,而且数据量比较多,webgl当然考虑到了这种情况,所以产生了buffer
的概念,并且可以通过gl.vertexAttribPointer
进行数据的绑定,这里就不再展开了,留给你自己探索了。