我目前正在 OpenGL 中编写一个程序,其中我仅使用片段着色器渲染场景的一部分,而不是使用纹理(这使得它无法使用 mipmap 来解决)。所讨论的部分是网格,它是场景的一部分,因此会经常移动和缩放。然而,问题在于,每当相机处于 45° 的倍数角度时,就会出现一些闪烁伪影,尤其是在平移视图时。
这是一个例子:
质量不是很好,但你仍然可以看到网格线完全不均匀,而它们应该以相同的宽度渲染。
这是生成网格线的代码部分:
if (uDrawGrid) {
// Texture coordinates.
float x = fTexCoordinate.x;
float y = fTexCoordinate.y;
// Rounding the coordinates to 3 decimals,
// so we have the targetX and targetY of the grid, for this fragment.
float targetX = round(x * 1000.0) / 1000.0;
float targetY = round(y * 1000.0) / 1000.0;
// Factor is basically the width of the grid lines,
// inversely proportional to the size of the underlying texture.
vec2 factor = vec2(0.05 / uTexSize.x, 0.05 / uTexSize.y);
// dx and dy are the distances from the current position to the target, plus a small offset.
float dx = abs(x - targetX + factor.x / 20.0);
float dy = abs(y - targetY + factor.y / 20.0);
// Checking if the distances are smaller than the grid line widths.
if (dx <= factor.x || dy <= factor.y) {
// Blending with grid color being (0.5, 0.5, 0.5, 0.5)
color.a = 1.0;
color.r = 0.25 + topColor.r * 0.5;
color.g = 0.25 + topColor.g * 0.5;
color.b = 0.25 + topColor.b * 0.5;
return;
}
}
备注:
uDrawGrid
是一个布尔值,只是为了检查是否应该绘制网格。uTexSize
是一个 vec2 变量,包含绘制网格的底层纹理的大小。precision highp float;
和 precision highp int;
已设置在着色器的顶部。编辑: 在图像中,我的意思是不均匀的网格线是较小的。角落里的大十字是背景纹理的一部分,圆圈是 Android 上的点击。
您已将网格的颜色定义为 uv 坐标的函数。 这很酷。但为了渲染这个函数,从技术上讲,每个像素应该被着色为函数在像素区域上的“平均值”。从数学上讲,这是函数在像素面积上的积分除以像素面积。
您有四种选择:
忽略该问题,并对每个片段对参数函数进行一次点采样。享受你闪闪发光的文物。
此函数必须集成在当前片段正在渲染的像素区域上。在图中,这是从 a 到 b 的范围。值得注意的是,当网格线相对于像素变小时,像素可能完全或部分包围任意数量网格线的任何部分。 要计算像素的强度,您需要精确计算积分两次,一次是在像素覆盖范围的最小值和最大值处,然后减去这些值并除以像素的面积以获得最能代表像素的强度整个像素的平均强度。
在 Desmos 中像这样可视化网格函数的积分,使得编写计算着色器的代码变得相当直观。在我的示例中,我将网格线的宽度表示为线之间间距的一部分。你的做法有点不同,但我认为我的形式更容易整合。
这是一个适合您的实现。
// Integral of the line's cross-section intensity function
vec2 g(vec2 uv, float portion) {
vec2 c = vec2(1.0, 1.0) * portion;
vec2 t = floor(mod(-uv - c / 2.0, 1.0) + c);
vec2 whole = floor(uv + c / 2.0);
vec2 frac = (uv + c / 2.0) - whole;
vec2 rising = (frac + whole * c);
vec2 level = (c / 2.0 + whole * c) + c / 2.0;
return mix(level, rising, t);
}
float linearToSRGB(float linear) {
if (linear <= 0.0031308) {
return 12.92 * linear;
} else {
return 1.055 * pow(linear, 1.0/2.2) - 0.055;
}
return linear;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// You can choose gnarly numbers here for gridSize and lineWidth
float gridSize = 13.13;
float lineWidth = 0.55;
float fraction = lineWidth / gridSize;
vec2 uv = fragCoord / gridSize;
vec2 pixelSize = vec2(1.0, 1.0) / gridSize;
vec2 average = (g(uv + pixelSize, fraction) - g(uv, fraction)) / pixelSize;
float linear = max(average.x, average.y);
vec3 srgb = vec3(1.0, 1.0, 1.0) * linearToSRGB(linear);
fragColor = vec4(srgb.rgb, 1.0);
}