绘制圆点
之前在《从画一个点入手》我们使用 webgl 绘制了一个点,这个点默认是正方形的,那么要如何绘制一个圆形的点呢?要回答这个问题,我们先来看一下 webgl 是如何绘制一个点的。
原理
比如下图的黄色小方块组成的区域就是默认绘制出来的那个正方形的点,每个小方块就代表一个片元(或者说是像素)。webgl 在绘制图形的时候,是逐片元处理的,并且可以大致认为每绘制一个片元,webgl 就会去执行一次片元着色器源码:
那么当我们想得到一个如图中的黑色虚线所标示的圆形的点时,我们只需要修改片元着色器源码中对于 gl_FragColor
赋值,将位于黑色圆圈外的像素,绘制上与背景色相同的颜色(案例中为红色);对位于圆圈内的像素,赋值上黄色,就可以了:
// 代码片段 1
const fsSource = `
precision lowp float;
void main() {
// 计算距离
float dis = distance(gl_PointCoord, vec2(0.5, 0.5));
// 点的颜色
if (dis > 0.5) {
// 对于圆圈外的片元赋值背景色红色
gl_FragColor = vec4(0.5, 0.0, 0.0, 1.0);
} else {
// 对于圆圈内的片元赋值黄色
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}
}
`
其中( 用到的这些 GLSL 中的属性或方法,可以去维护 webgl 等图形标准的组织 Khronos Group 的官网查看细节):
distance
函数用于计算两点之间的距离;gl_PointCoord
则表示片元在点内的坐标,而且值是经过归一化处理的,也就是说点内的片元的 x,y 轴坐标的值都被映射到了 0 ~ 1 的范围之内。
所以我们传给 distance()
的第 2 个参数 vec2(0.5, 0.5)
就是点的中心坐标,dis
即为点中各个片元距离点的中心的距离。又因为 dis
是 float
类型的数据,所以我们还需要通过 precision lowp float;
来指定着色器的默认浮点数精度。
最后再使用 GLSL 中使用方法同 js 一样的 if...else
分支逻辑做个判断即可画出圆点。效果如下:
使用 discard
对位于圆圈外的片元,除了可以让其颜色与背景色一致,还可以使用 discard
—— 它只能在片元着色器中使用,表示放弃当前片元,直接处理下一个片元。效果如下:
稍稍更该一下 if
的条件,还可以绘制圆环:
添加透明效果
在片元着色器源码中,我们给 gl_FragColor
赋值的 vec4()
,传入的第 4 个参数就代表着透明度,比如传 0.5
,让圆点为 50% 透明度的黄色:
gl_FragColor = vec4(1.0, 1.0, 0.0, 0.5);
其效果如下,只是原本黄色有些偏白,圆点并没有变为半透明从而能看到背景的红色:
要起到半透明效果,还需要开启 alpha 混合:
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
通过 gl.enable(gl.BLEND)
激活片元的颜色融合计算;
通过 gl.blendFunc() 定义一个用于混合像素算法的函数,该函数用于决定绘制像素时,新绘制的像素(源像素,也就是黄色圆点)和已存在的像素(目标像素,即红色背景)如何混合。第 1 个参数为源混合因子指定一个乘数,其决定了源像素的 alpha 值如何影响混合结果;第 2 个参数为源目标合因子指定一个乘数,它决定了目标像素的 alpha 值(减去源像素的 alpha 值)如何影响混合结果。分别传入 gl.SRC_ALPHA
和 gl.ONE_MINUS_SRC_ALPHA
,就可以起到透明或半透明的效果: