Несоответствие интерполяции - PullRequest
1 голос
/ 04 мая 2020

Посмотрите на эти два изображения:

enter image description here enter image description here

Красный и синий каналы равны нулю на обоих снимках, в то время как зеленый канал - это координата х этой точки. Координата 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: Первый, вершинный шейдер было приказано нарисовать три вершины (но ни одна из них не была дана, больше информации здесь ):

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);
}

Это, по сути, отправляло координаты baricentri c каждому из фрагментов. Они были интерполированы между тремя значениями. Затем фрагментный шейдер выполнил следующие вычисления:

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). ) диапазон.

Если вы снова посмотрите на изображения, вы заметите, что они не совпадают. Процессор один гораздо более плавный вблизи центра, в то время как тот, что отображается на графическом процессоре, имеет более резкий переход. У вас есть идеи, что может вызвать это? Похоже, что это может быть связано с потерей точности где-то на GPU. Я думал, что интерполированные числа с плавающей точкой не были достаточно точными, поэтому я попробовал другой подход для c:

double screenWidth = 750;
double viewportWidth = 2.0;
double c = (gl_FragCoord.xy - 0.5 * screenWidth )/ screenWidth  * viewportWidth;

При этом используется gl_FragCoord и разрешение экрана вместо того, чтобы полагаться на интерполированное значение. Это дало мне результаты, которые были неотличимы с первой попытки, так что я думаю, что это не интерполяция. Просто чтобы быть уверенным, я вычислил c в обе стороны и сравнил их. Они были точно такими же, вплоть до 7-го знака после запятой.

Кто-нибудь знает, почему два изображения не совпадают?

1 Ответ

2 голосов
/ 04 мая 2020

Образ графического процессора является правильным - похоже, у вас включена обработка sRGB (что хорошо!). Следовательно, ваш код GPU интерполируется линейно, а затем применяет функцию сжатия гаммы sRGB. В OpenGL это поведение контролируется glEnable/Disable(GL_FRAMEBUFFER_SRGB). В Vulkan он настраивается при создании swapchain .

Ваш код CPU интерполируется в «неправильном» цветовом пространстве - то есть путем непосредственного вычисления значений sRGB, как если бы они были линейными, даже если это не так.

Вы можете исправить код вашего процессора следующим образом:

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