不匹配的插值

Mismatching interpolations

看这两张图:

两张图片的红色和蓝色通道均为零,而绿色通道是该点的 x 坐标。 x 坐标在 (-1, 1) 范围内。

第一张图片是在CPU上计算的:

double screenHeight = 750;
double screenWidth = 750;
double viewportWidth = 2.0;

for (int i = 0; i < screenHeight; ++i) {
    for (int j = 0; j < screenWidth; ++j) {
        float greenChannel = (double)j / screenWidth * viewportWidth;
    }
}

第二个是在GPU上计算的: 首先,顶点着色器被告知绘制三个顶点(但给出了 none,更多信息 here):

layout (location = 0) out vec2 outUV;

void main() {
    outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
    gl_Position = vec4(vec2(outUV) * 2.0f + -1.0f, 0.0f, 1.0f);
}

这实质上是将重心坐标发送到每个片段。它们被插入到三个值之间。片段着色器然后进行了以下计算:

layout (location = 0) in vec2 outUV;
layout(location = 0) out vec4 outColor;

void main() {
    double viewportWidth = 2.0;
    double c = (outUv.x - 0.5) * viewportWidth;
    outColor = vec4(vec3(0.0, float(c), 0.0), 1.0);
}

这首先移动 x 坐标,使其现在处于 (-0.5, -0.5) 范围内,然后将其缩放至 (-1, 1) 范围内。

如果您再次查看这些图像,您会发现它们并不相同。 CPU 靠近中心的更平滑,而在 GPU 上渲染的过渡更突然。你知道是什么原因造成的吗?看起来可能是由于 GPU 某处的精度损失所致。我认为插值浮点数不够精确,所以我为 c:

尝试了一种不同的方法
double screenWidth = 750;
double viewportWidth = 2.0;
double c = (gl_FragCoord.xy - 0.5 * screenWidth )/ screenWidth  * viewportWidth;

这使用 gl_FragCoord 和屏幕分辨率,而不是依赖于插值。它给了我与第一次尝试无法区分的结果,所以我猜这不是插值。可以肯定的是,我计算了两种方式的 c 并进行了比较。直到小数点后第 7 位,它们完全相同。

有谁知道为什么这两张图片不一样?

GPU 图像是正确的 -- 似乎您启用了 sRGB 处理(这很好!)。因此,您的 GPU 代码以线性方式进行插值,然后应用 sRGB 伽马压缩函数。在 OpenGL 中,此行为由 glEnable/Disable(GL_FRAMEBUFFER_SRGB) 控制。在 Vulkan 中是 configured when creating the swapchain.

您的 CPU 代码在 'wrong' 色彩空间中进行插值——即通过直接在 sRGB 值中计算,就好像它们是线性的,即使它们不是。

您可以按如下方式修复 CPU 代码:

float greenChannel = ...;
greenChannel = greenChannel <= 0.0031308f ? 12.92f*greenChannel : 1.055f*powf(greenChannel, 1/2.4f) - 0.055f;