今天我们使用 OpenGL ES 来实现一个绘画板,主要介绍在 OpenGL ES 中绘制平滑曲线的实现方案。
首先看一下最终效果:

在 iOS 中,有很多种方式可以实现一个绘画板,比如我的另外一个项目 MFPaintView 就是基于 CoreGraphics 实现的。
然而,使用 OpenGL ES 来实现可以获得更多的灵活性,比如我们可以自定义笔触的形状,这是其他实现方式做不到的。
我们知道,OpenGL ES 中只有 点、直线、三角形 这三种图元。因此, 怎么在 OpenGL ES 中绘制曲线 ,是我们第一个要解决的问题,也是最复杂的问题。
我们会使用比较大的篇幅来讲解这个问题。至于绘画板的其他功能实现,并不是说不重要,只是说其他的绘画板实现方式,也会有类似的逻辑,所以这部分会放在最后再简单介绍一下。
一、怎么绘制曲线
在 OpenGL ES 中绘制曲线的方式,就是 将曲线拆分成点序列来绘制 。
因为要绘制点,所以我们采取的是 点图元 。即我们要把顶点数据当成 点 来绘制,并且每个点都要绘制出笔触的纹理。关键步骤如下:
指定图元类型:
glDrawArrays(GL_POINTS, 0, self.vertexCount);
顶点着色器:
attribute vec4 Position;
uniform float Size;
void main (void) {
gl_Position = Position;
gl_PointSize = Size;
}
片段着色器:
precision highp float;
uniform float R;
uniform float G;
uniform float B;
uniform float A;
uniform sampler2D Texture;
void main (void) {
vec4 mask = texture2D(Texture, vec2(gl_PointCoord.x, 1.0 - gl_PointCoord.y));
gl_FragColor = A * vec4(R, G, B, 1.0) * mask;
}
这里的关键点在于 gl_PointCoord 这个内置变量,当我们使用点图元的时候,可以通过这个变量获取到 当前像素在点图元中的归一化坐标 。
但是这个坐标的原点是在左上角,这和纹理坐标在竖直方向上是相反的。所以从纹理读取颜色的时候,要做一个 y 坐标的转换。
接下来,我们通过 UITouch 来获取触摸点的位置,然后算出归一化的顶点坐标。
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
[self addPointWithTouches:touches];
}
但是由于 iOS 系统触摸事件的派发频率有限,我们最终得到的只能是稀疏的点。如下图所示,每个触摸点之间的间隔会比较大。

二、怎么绘制密集的点
很容易想到,只需要在两个点之间,按照一定的密度进行插值,就可以绘制出连续的轨迹。










