1-抗锯齿的概念
1-1-锯齿出现的原因
仔细去看,我们之前所绘制的线条上面有许多锯齿:
这是因为canvas画布是点阵图,着色时,又是非黑即白,所以导致其边缘有凌厉的锯齿(aliasing)。
以三角形为例,更直观的给大家演示一下锯齿出现的原因。
下面这个三角形要画在4*4的canvas 画布上。
然而,因为像素已经是着色的最小单位,按照我们之前非黑即白的方式来绘制,它可能会是这样的:
1-2-消除锯齿的思路
消除锯齿的思路有2种:
- 提高图像分辨率,让图像边缘的锯齿小到肉眼难以察觉,如下图
- 羽化图像边缘,让图像边缘与周围的环境有一个柔和的过度,如下图:
在实际工作中,我们所说的抗锯齿,就是羽化图像边缘。
2-抗锯齿的方法
抗锯齿的方法可以分成两类:在绘图之前抗锯齿和在绘图之后抗锯齿。
2-1-在绘图之前抗锯齿
在绘图之前抗锯齿的方法有很多,我简单说两种常见的:
- 距离场抗锯齿,基于着色点到图形的距离做过度,这种方法有一些局限性,实际用得并不多。
- 多重采样抗锯齿,将像素点细分后取均值,这种方法是当前用得最多的。
2-2-在绘图之后抗锯齿
在绘图之后抗锯齿会对渲染图像进行后处理,比如查找图像边缘,然后对其进行重绘。
这种抗锯齿的方法属于计算机图像处理,我在此不做讲解。
接下来,我们重点来说在绘图之前的抗锯齿方法。
3-距离场抗锯齿
先以直线为例说一下其实现原理。
3-1-直线的抗锯齿
我们可以基于着色点到直线的距离,为其添加一个过渡色,如下图所示:
代码如下:
1.基于当先片元到直线的距离和线宽,羽化直线的边缘。
vec4 Line(in vec2 C, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 向量AB的单位向量
vec2 ABn = normalize(B - A);
// 向量AC
vec2 AC = C - A;
// 点C到直线AB的距离 = 向量AC与单位向量ABn的叉乘
float distance = abs(AC.x * ABn.y - AC.y * ABn.x);
// 偏移导数
float dx = dFdx(C.x);
// 线宽
float width = dx * lineWidth;
// 收缩线宽
float shrinkWidth = max(width - dx * 2., dx);
// 用于抗锯齿的透明度
float a = 1. - smoothstep(shrinkWidth, width, distance);
// 直线
return distance < width ? vec4(vec3(lineColor), a * lineColor.a) : vec4(0);
}
smoothstep(edge0,edge1,x) 是抗锯齿的重要方法:
- 当x小于edge0时,返回0
- 当x大于edge1时,返回1
- 当x在edge0和edge1之间时,返回x在其中的位置比,即(x-edge0)/(edge1-edge0)
在上面的代码中,我让宽度线往里收了2个像素,并且使其不小于一个像素:
float shrinkWidth = max(width - dx * 2., dx);
smoothstep(width - dx * 2., width, distance)的取值范围是[0,1],我想让其与线条的透明度正相关,所以我在前面又用1减去了此值。
线条的透明度会受其自身透明度和边界透明度的影响:
// 用于抗锯齿的透明度
float a = 1. - smoothstep(shrinkWidth, width, distance);
// 直线
return distance < width ? vec4(vec3(lineColor), a * lineColor.a) : vec4(0);
2.绘制直线。
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord, 3.);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
// 直线
vec4 line = Line(coord, vec2(-1, -1), vec2(1, 1), 5., vec4(1));
// 最终的颜色
fragColor = mix(backgroundColor + projectionHelper, line, line.a);
}
重点看最后一行。
mix(x,y,a) :基于a 值对x和y进行融合:
- 当a=0时,返回x
- 当a=1时,返回y
- 当a在(0,1) 之间时,对x和y进行融合,融合算法为:
x*(1-a)+y*a
最终的颜色便是基于直线的透明度,将直线和栅格背景进行了融合。
fragColor = mix(backgroundColor + projectionHelper, line, line.a);
最终效果如下:
整体代码如下:
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord, in float scale) {
return scale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}
// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * axisWidth;
float dy = dFdy(coord.y) * axisWidth;
if(abs(coord.x) < dx) {
color = yAxisColor;
} else if(abs(coord.y) < dy) {
color = xAxisColor;
}
return color;
}
// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * gridWidth;
float dy = dFdy(coord.y) * gridWidth;
vec2 fraction = fract(coord);
if(fraction.x < dx || fraction.y < dy) {
color = gridColor;
}
return color;
}
// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
// 坐标轴
vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
// 栅格
vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
// =投影坐标系
return bool(axisHelper.a) ? axisHelper : gridHelper;
}
// 直线
vec4 Line(in vec2 C, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 向量AB的单位向量
vec2 ABn = normalize(B - A);
// 向量AC
vec2 AC = C - A;
// 点C到直线AB的距离 = 向量AC与单位向量ABn的叉乘
float distance = abs(AC.x * ABn.y - AC.y * ABn.x);
// 基于偏移导数的线宽
float width = dFdx(C.x) * lineWidth;
// 直线
return distance < width ? lineColor : vec4(0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord, 3.);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
// 直线
vec4 line = Line(coord, vec2(-1, -1), vec2(1, 1), 1., vec4(1));
// 最终的颜色
fragColor = backgroundColor + projectionHelper + line;
}
以此原理,我们还可以对其它的线条进行抗锯齿。
3-2-线段的抗锯齿
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord, in float scale) {
return scale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}
// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * axisWidth;
float dy = dFdy(coord.y) * axisWidth;
if(abs(coord.x) < dx) {
color = yAxisColor;
} else if(abs(coord.y) < dy) {
color = xAxisColor;
}
return color;
}
// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * gridWidth;
float dy = dFdy(coord.y) * gridWidth;
vec2 fraction = fract(coord);
if(fraction.x < dx || fraction.y < dy) {
color = gridColor;
}
return color;
}
// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
// 坐标轴
vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
// 栅格
vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
// =投影坐标系
return bool(axisHelper.a) ? axisHelper : gridHelper;
}
// 线段
vec4 Segment(in vec2 C, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 向量AB
vec2 AB = B - A;
// 向量AB的单位向量
vec2 ABn = normalize(AB);
// 向量AC
vec2 AC = C - A;
// 点C到直线AB的距离 = 向量AC与单位向量ABn的叉乘
float distance = abs(AC.x * ABn.y - AC.y * ABn.x);
// 偏移导数
float dx = dFdx(C.x);
// 基于偏移导数的线宽
float width = dx * lineWidth;
// 收缩线宽
float shrinkWidth = max(width - dx * 2., dx);
// 用于抗锯齿的透明度
float a = 1. - smoothstep(shrinkWidth, width, distance);
// 将有向距离AC和有向距离AB的比值收束在[0,1] 之间
float ratio = clamp(dot(AC, AB) / dot(AB, AB), 0., 1.);
// 线段
return length(ratio * AB - AC) < width ? vec4(vec3(lineColor), a * lineColor.a) : vec4(0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord, 3.);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
// 线段
vec4 segment = Segment(coord, vec2(-1, -1), vec2(1, 2), 5., vec4(1));
// 最终的颜色
fragColor = mix(backgroundColor + projectionHelper, segment, segment.a);
}
效果如下:
3-3-正弦型曲线的抗锯齿
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord, in float scale) {
return scale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}
// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * axisWidth;
float dy = dFdy(coord.y) * axisWidth;
if(abs(coord.x) < dx) {
color = yAxisColor;
} else if(abs(coord.y) < dy) {
color = xAxisColor;
}
return color;
}
// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * gridWidth;
float dy = dFdy(coord.y) * gridWidth;
vec2 fraction = fract(coord);
if(fraction.x < dx || fraction.y < dy) {
color = gridColor;
}
return color;
}
// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
// 坐标轴
vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
// 栅格
vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
// =投影坐标系
return bool(axisHelper.a) ? axisHelper : gridHelper;
}
// 线段
vec4 Segment(in vec2 C, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 向量AB
vec2 AB = B - A;
// 向量AB的单位向量
vec2 ABn = normalize(AB);
// 向量AC
vec2 AC = C - A;
// 点C到直线AB的距离 = 向量AC与单位向量ABn的叉乘
float distance = abs(AC.x * ABn.y - AC.y * ABn.x);
// 偏移导数
float dx = dFdx(C.x);
// 基于偏移导数的线宽
float width = dx * lineWidth;
// 收缩线宽
float shrinkWidth = max(width - dx * 2., dx);
// 用于抗锯齿的透明度
float a = 1. - smoothstep(shrinkWidth, width, distance);
// 将有向距离AC和有向距离AB的比值收束在[0,1] 之间
float ratio = clamp(dot(AC, AB) / dot(AB, AB), 0., 1.);
// 线段
return length(ratio * AB - AC) < width ? vec4(vec3(lineColor), a * lineColor.a) : vec4(0);
}
// 正弦函数(自变量x,振幅a,频率omega,偏移alpha)
float SinFn(float x, float a, float omega, float alpha) {
return a * sin(omega * x + alpha);
}
// 正弦路径(当前点coord,起点star,结束点end,段数segs,线宽lineWidth,颜色sinColor,振幅a,频率omega,偏移alpha)
vec4 SinPath(vec2 coord, float start, float end, int segs, float lineWidth, vec4 sinColor, float a, float omega, float alpha) {
vec4 color = vec4(0);
float step = (end - start) / float(segs);
for(int n = 0; n < segs; n++) {
float x = start + float(n) * step;
float nextX = x + step;
vec2 A = vec2(x, SinFn(x, a, omega, alpha));
vec2 B = vec2(nextX, SinFn(nextX, a, omega, alpha));
vec4 segment = Segment(coord, A, B, lineWidth, sinColor);
if(segment.a != 0.) {
color = segment;
}
}
return color;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord, 3.);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
//正弦曲线
vec4 path = SinPath(coord, -2., 2., 24, 2., vec4(1), 1., 1., 0.);
// 最终的颜色
fragColor = mix(backgroundColor + projectionHelper, path, path.a);
}
效果如下:
3-4-圆形的抗锯齿
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord, in float scale) {
return scale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}
// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * axisWidth;
float dy = dFdy(coord.y) * axisWidth;
if(abs(coord.x) < dx) {
color = yAxisColor;
} else if(abs(coord.y) < dy) {
color = xAxisColor;
}
return color;
}
// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * gridWidth;
float dy = dFdy(coord.y) * gridWidth;
vec2 fraction = fract(coord);
if(fraction.x < dx || fraction.y < dy) {
color = gridColor;
}
return color;
}
// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
// 坐标轴
vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
// 栅格
vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
// =投影坐标系
return bool(axisHelper.a) ? axisHelper : gridHelper;
}
// 圆形
vec4 Circle(vec2 C, vec2 O, float r, vec4 circleColor) {
// 当前点到圆心的距离
float distance = length(C - O);
// 偏移导数
float dx = dFdx(C.x);
// 收缩半径
float shrinkR = max(r - dx * 2., dx);
// 用于抗锯齿的透明度
float a = 1. - smoothstep(shrinkR, r, distance);
// 圆
return distance < r ? vec4(vec3(circleColor), a * circleColor.a) : vec4(0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord, 3.);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
//正弦曲线
vec4 path = Circle(coord, vec2(0), 1., vec4(1));
// 最终的颜色
fragColor = mix(backgroundColor + projectionHelper, path, path.a);
}
效果如下:
4-多重采样抗锯齿
多重采样时,对于采样点的定位方式有很多,我在这就说两种比较简单的。
4-1-九点采样抗锯齿
比如蓝点是一个采样位置,我们可以基于此点位向上下左右偏移0.5个像素,然后取蓝点和周围8个红点所对应的颜色的平均值,将其作为当前片元的颜色。
以直线为例,其代码如下:
// 坐标系缩放
#define ProjectionScale 3.
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord) {
return ProjectionScale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}
// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * axisWidth;
float dy = dFdy(coord.y) * axisWidth;
if(abs(coord.x) < dx) {
color = yAxisColor;
} else if(abs(coord.y) < dy) {
color = xAxisColor;
}
return color;
}
// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * gridWidth;
float dy = dFdy(coord.y) * gridWidth;
vec2 fraction = fract(coord);
if(fraction.x < dx || fraction.y < dy) {
color = gridColor;
}
return color;
}
// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
// 坐标轴
vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
// 栅格
vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
// 投影坐标系
return bool(axisHelper.a) ? axisHelper : gridHelper;
}
// 直线
vec4 Line(in vec2 C, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 向量AB的单位向量
vec2 ABn = normalize(B - A);
// 向量AC
vec2 AC = C - A;
// 点C到直线AB的距离 = 向量AC与单位向量ABn的叉乘
float distance = abs(AC.x * ABn.y - AC.y * ABn.x);
// 基于偏移导数的线宽
float width = dFdx(C.x) * lineWidth;
// 直线
return distance < width ? lineColor : vec4(0);
}
// 采样点
vec2[9] Samples9() {
return vec2[9](vec2(0), vec2(-0.5, 0.5), vec2(0, 0.5), vec2(0.5, 0.5), vec2(0.5, 0), vec2(0.5, -0.5), vec2(0, -0.5), vec2(-0.5, -0.5), vec2(-0.5, 0));
}
// 抗锯齿图形
vec4 LineAA9(vec2 coord, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
vec2[9] samples = Samples9();
vec4 line = vec4(0);
for(int i = 0; i < 9; i++) {
vec2 pos = samples[i] * ProjectionScale / iResolution.xy;
line += Line(coord + pos, A, B, lineWidth, lineColor) / 9.;
}
return line;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// float scale = 3.;
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
// 直线
vec4 line = LineAA9(coord, vec2(-.1, -1), vec2(.1, 1), 3., vec4(1));
// 最终的颜色
fragColor = mix(backgroundColor + projectionHelper, line, line.a);
}
效果如下:
之前有锯齿的样子是这样的:
由此可见,锯齿已经模糊了很多。
我们重点看一下这部分代码:
// 采样点
vec2[9] Samples9() {
return vec2[9](vec2(0), vec2(-0.5, 0.5), vec2(0, 0.5), vec2(0.5, 0.5), vec2(0.5, 0), vec2(0.5, -0.5), vec2(0, -0.5), vec2(-0.5, -0.5), vec2(-0.5, 0));
}
// 抗锯齿图形
vec4 LineAA9(vec2 coord, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
vec2[9] samples = Samples9();
vec4 line = vec4(0);
for(int i = 0; i < 9; i++) {
vec2 pos = samples[i] * ProjectionScale / iResolution.xy;
line += Line(coord + pos, A, B, lineWidth, lineColor) / 9.;
}
return line;
}
Samples9()方法返回的就是包括当前点在内的9个采样点。
- vec2(0) 对应的就是当前点。
- 0.5 对应的就是半个像素的尺寸。
LineAA9()方法便是对9个采样点所对应的颜色进行了加权平均。
若我们觉得当前抗锯齿的力度还不够,可以增加一下采样数量。
4-2-十七点采样抗锯齿
如下图所示,我们可以在当前采样点的周围再加上8个黄色的采样点。
代码如下:
// 坐标系缩放
#define ProjectionScale 3.
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord) {
return ProjectionScale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}
// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * axisWidth;
float dy = dFdy(coord.y) * axisWidth;
if(abs(coord.x) < dx) {
color = yAxisColor;
} else if(abs(coord.y) < dy) {
color = xAxisColor;
}
return color;
}
// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * gridWidth;
float dy = dFdy(coord.y) * gridWidth;
vec2 fraction = fract(coord);
if(fraction.x < dx || fraction.y < dy) {
color = gridColor;
}
return color;
}
// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
// 坐标轴
vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
// 栅格
vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
// 投影坐标系
return bool(axisHelper.a) ? axisHelper : gridHelper;
}
// 直线
vec4 Line(in vec2 C, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 向量AB的单位向量
vec2 ABn = normalize(B - A);
// 向量AC
vec2 AC = C - A;
// 点C到直线AB的距离 = 向量AC与单位向量ABn的叉乘
float distance = abs(AC.x * ABn.y - AC.y * ABn.x);
// 基于偏移导数的线宽
float width = dFdx(C.x) * lineWidth;
// 直线
return distance < width ? lineColor : vec4(0);
}
// 采样点
vec2[17] Samples17() {
float d = 0.5;
vec2 p9[9] = vec2[9](vec2(0), vec2(-d, d), vec2(0, d), vec2(d, d), vec2(d, 0), vec2(d, -d), vec2(0, -d), vec2(-d, -d), vec2(-d, 0));
return vec2[17](p9[0], p9[1], p9[2], p9[3], p9[4], p9[5], p9[6], p9[7], p9[8], p9[1] * 2., p9[2] * 2., p9[3] * 2., p9[4] * 2., p9[5] * 2., p9[6] * 2., p9[7] * 2., p9[8] * 2.);
}
// 抗锯齿图形
vec4 LineAA17(vec2 coord, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
vec2[17] samples = Samples17();
vec4 line = vec4(0);
for(int i = 0; i < 17; i++) {
vec2 pos = samples[i] * ProjectionScale / iResolution.xy;
line += Line(coord + pos, A, B, lineWidth, lineColor) / 17.;
}
return line;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// float scale = 3.;
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
// 直线
vec4 line = LineAA17(coord, vec2(-1, -.1), vec2(1, .1), 3., vec4(1));
// 最终的颜色
fragColor = mix(backgroundColor + projectionHelper, line, line.a);
}
效果如下:
对比一下之前的9点采样,可以发现17点采样的效果更加平滑了:
4-3-相邻点抗锯齿
抗锯齿的思路和具体写法是有很多种的,这个需要根据我们的项目需求来确定。
比如,我们取一个像素和其周围的八个像素的平均值也可以实现抗锯齿。
代码如下:
// 坐标系缩放
#define ProjectionScale 3.
// 抗锯齿的行数和列数
#define AA 3
// 投影坐标系
vec2 ProjectionCoord(in vec2 coord) {
return ProjectionScale * 2. * (coord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
}
// 坐标轴
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * axisWidth;
float dy = dFdy(coord.y) * axisWidth;
if(abs(coord.x) < dx) {
color = yAxisColor;
} else if(abs(coord.y) < dy) {
color = xAxisColor;
}
return color;
}
// 栅格
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
vec4 color = vec4(0, 0, 0, 0);
float dx = dFdx(coord.x) * gridWidth;
float dy = dFdy(coord.y) * gridWidth;
vec2 fraction = fract(coord);
if(fraction.x < dx || fraction.y < dy) {
color = gridColor;
}
return color;
}
// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
// 坐标轴
vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
// 栅格
vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
// 投影坐标系
return bool(axisHelper.a) ? axisHelper : gridHelper;
}
// 直线
vec4 Line(in vec2 C, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 向量AB的单位向量
vec2 ABn = normalize(B - A);
// 向量AC
vec2 AC = C - A;
// 点C到直线AB的距离 = 向量AC与单位向量ABn的叉乘
float distance = abs(AC.x * ABn.y - AC.y * ABn.x);
// 基于偏移导数的线宽
float width = dFdx(C.x) * lineWidth;
// 直线
return distance < width ? lineColor : vec4(0);
}
// 抗锯齿图形 Anti-Aliasing
vec4 LineAA(vec2 fragCoord, in vec2 A, in vec2 B, in float lineWidth, in vec4 lineColor) {
// 初始颜色
vec4 color = vec4(0);
// 行列的一半
float aa2 = float(AA / 2);
// 逐行列变了
for(int y = 0; y < AA; y++) {
for(int x = 0; x < AA; x++) {
// 基于像素的偏移距离
vec2 offset = vec2(float(x), float(y)) / float(AA) - aa2;
// 投影坐标位
vec2 coord = ProjectionCoord(fragCoord + offset);
// 累加周围片元的颜色
color += Line(coord, A, B, lineWidth, lineColor);
}
}
// 返回周围颜色的均值
return color / float(AA * AA);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// 投影坐标
vec2 coord = ProjectionCoord(fragCoord);
// 背景色
vec4 backgroundColor = vec4(0, 0, 0, 1);
// 投影坐标系辅助对象
vec4 projectionHelper = ProjectionHelper(coord, 2., vec4(0, .4, 0, 1), vec4(.4, 0, 0, 1), 2., vec4(vec3(.3), 1));
// 直线
vec4 line = LineAA(fragCoord, vec2(-1, -.1), vec2(1, .1), 3., vec4(1));
// 最终的颜色
fragColor = mix(backgroundColor + projectionHelper, line, line.a);
}
效果如下:
其效果不如之前的19点抗锯齿明显,但其采样点少,渲染更快一些。
扩展
我当前所说的抗锯齿,更多的还是给大家做一个科普,让大家知道锯齿产生的原因,以及抗锯齿的思路。
现在比较主流的多重采样抗锯齿技术有MSAA,TAA;后处理抗锯齿的技术有FXAA,SMAA。
若大家想进一步研究抗锯齿,可以去研究一下上面所说的4种技术。
参考链接:space.bilibili.com/10707223/ch…
\