多边形填充渐变色。渐变色由 2 个锚点确认。如下图,给定 2 个锚点 p0, p1 的坐标及各自的色彩,直线 p0-p1 上每一点的色彩由锚点的色彩通过线性插值取得;平面上恣意一点 p2 在直线 p0-p1 上的投影为 p,p2 的色彩与 p 相同:
依据直线 p0-p1 方程有
(x – x0)(y1 – y0) – (x1 – x0)(y – y0) = 0
又矢量 p0-p1 与 p2-p 正交,故二者点积为 0,有
(x – x2)(x1 – x0) (y – y2)(y1 – y0) = 0
将以上 2 式变形,并组成线性方程组
(y1 – y0)⋅x – (x1 – x0)⋅y = (y1 – y0)⋅x0 – (x1 – x0)⋅y0
(x1 – x0)⋅x (y1 – y0)⋅y = (x1 – x0)⋅x2 (y1 – y0)⋅y2
若记
A = x1 – x0
B = y1 – y0
C = B⋅x0 – A⋅y0
D = A⋅x2 B⋅y2
则有
B⋅x – A⋅y = C
A⋅x B⋅y = D
求解得
x = (AD BC)/(A2 B2)
y = (BD – AC)/(A2 B2)
注意到,A, B, C 的值固定,而 D 的值与 p2 相关。
p 点色彩重量 c 由 p0, p1 点对应的色彩重量 c0, c1 线形插值得到
t = (x – x0 y – y0)/(x1 – x0 y1 – y0)
c = c0 t⋅(c1 – c0)
OpenGL 完成
为对比程序运转成果,预先制造一张填充了渐变色的图片,如下。将从这张图片上取多边形极点坐标,以及 2 个锚点的坐标和色彩:
取得的极点及锚点数据如下:
GLfloat vertices[] = {
194, 350, //
407, 297, //
444, 166, //
331, 108, //
246, 147, //
199, 77, //
90, 75, //
44, 248, //
};
GLfloat anchors[] = {
91, 295, .976, .082, .067, //
401, 170, .875, .922, .008, //
};
其中,在锚点数据数组 anchors
中,每个点占 5 个数组元素,依次为方位坐标 (x, y) 和色彩 (r, g, b)。色彩重量已转换到 [0, 1] 区间。
Vertex shader 首要代码如下:
struct anchor
{
vec2 pos;
vec3 color;
};
attribute vec4 a_pos;
uniform mat4 m_trans;
uniform anchor u_anchors[2];
varying mediump vec4 v_color;
vec3 gradient(vec2 pos);
void main()
{
gl_Position = m_trans * a_pos;
v_color = vec4(gradient(vec2(a_pos)), 1.0);
}
空间改换部分这儿只做简要介绍。a_pos
为极点坐标,m_trans
是改换矩阵。将改换矩阵应用到极点坐标,得到投影后的 NDC 坐标,赋值给内建变量 gl_Position
。
这儿将锚点数据界说为一个 struct
(代码第 1 行),并以 uniform
类型从应用程序绑定到 OpenGL,以使得 shader 可以拜访(第 10 行)。
gradient()
是一个自界说函数,依据前文推导的算法,完成渐变色的计算。函数参数是某一点的 2D 坐标 (x, y),返回值为该点的色彩 (r, g, b)。Vertex shader 调用 gradient()
取得极点的色彩后,将其赋值给 varying
变量(第 19 行)。该 varying
变量值将进入 OpenGL 烘托流水线的后续流程,并终究到达 fragment shader,由 fragment shader 设置为终究像素的色彩。
gradient()
完好界说如下。可以看到,它是对前面所推导出来的算法的忠实完成:
vec3 gradient(vec2 pos)
{
float x0 = u_anchors[0].pos.x;
float y0 = u_anchors[0].pos.y;
float x1 = u_anchors[1].pos.x;
float y1 = u_anchors[1].pos.y;
float A = x1 - x0;
float B = y1 - y0;
float C = B * x0 - A * y0;
float D = A * pos.x B * pos.y;
float d = A * A B * B;
float x = (A * D B * C) / d;
float y = (B * D - A * C) / d;
float t = (x - x0 y - y0) / (x1 - x0 y1 - y0);
vec3 c = u_anchors[0].color (u_anchors[1].color - u_anchors[0].color) * t;
return clamp(c, 0.0, 1.0);
}
程序运转成果如下图。目测基本可认定,实践填充效果与我预先制造的参照图片共同:
完好代码见 gitlab.com/sihokk/lear…