эквивалент gl_FragCoord в вершинном шейдере glsl

Я пытаюсь получить положение вершины на экране в пикселях внутри вершинного шейдера, я видел здесь несколько других сообщений, но не могу найти ответ, который подходит для меня. вот что у меня есть в вершинном шейдере:

#version 400
layout (location = 0) in vec3 inPosition;
uniform mat4 MVP; // modelViewProjection
uniform vec2 window;

void main()
{
    // vertex in screen space
    vec2 fake_frag_coord = (MVP * vec4(inPosition,1.0)).xy;
    float X = (fake_frag_coord.x*window.x/2.0) + window.x;
    float Y = (fake_frag_coord.y*window.y/2.0) + window.y;
}

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

Благодарность! Луис


person user1822451    schedule 28.05.2014    source источник
comment
Ваша трансформация - перспектива или орфография? Если перспектива, вам нужно выполнить перспективное деление. Взгляните на stackoverflow.com/questions/23603231/, вопросы довольно близки.   -  person keltar    schedule 28.05.2014
comment
Я не понимаю, зачем вам значение в пикселях. fake_frag_coord будет в диапазоне [-1,1]. Вам просто нужно умножить на 0,5 и добавить 0,5, чтобы он находился в диапазоне [0, 1], затем вызовите texture вместо texelFetch. Поскольку у вас есть X и Y как float, в любом случае texelFetch теряет свою цель. И +1 @keltar. Вы должны выполнить перспективное деление (деление на w я думаю).   -  person agrum    schedule 28.05.2014
comment
Если вы все же хотите сохранить координату пикселя, добавьте window.x/2 и window.y/2, а не window.x и window.y. Не забывайте еще о перспективном делении.   -  person agrum    schedule 28.05.2014
comment
@keltar: Вы должны выполнять деление на w независимо от того, перспективная это проекция или нет. Ортографическая проекция создает постоянную w координату (в отличие от перспективы, w координата которой изменяется с расстоянием по оси z), но нет гарантии, что значение постоянно 1.0 . Вот почему я предпочитаю избегать термина «перспективное разделение» и вместо этого предпочитаю «однородное разделение» (хотя многие люди не понимают, о чем вы говорите = p)   -  person Andon M. Coleman    schedule 05.06.2014
comment
@ AndonM.Coleman ну термин не мой .. Но интересно. Единственный случай, когда (как я понимаю) это могло быть не 1.0, это когда сама вершина изначально имеет специальный w. Есть ли у него варианты использования? Спасибо за исправление, конечно.   -  person keltar    schedule 05.06.2014
comment
@keltar: Да, это одна из возможностей. Но более практично, рассмотрим ситуацию, когда m44 в матрице ортогональной проекции изменяется с 1.0 на какое-то другое значение. Использование 0,5 для m44 уменьшит объем просмотра вдвое, поэтому объекты будут казаться увеличенными в размере вдвое. Использование 2.0 для m44 увеличит объем просмотра в 2.0, поэтому объекты будут казаться уменьшенными. Использование значения, отличного от 1.0 для m44, можно использовать для применения равномерного масштабирования к сцене с минимальными усилиями. То же самое с масштабной матрицей потребует гораздо больше работы.   -  person Andon M. Coleman    schedule 05.06.2014


Ответы (3)


Я исправил ваш вершинный шейдер надлежащими терминами и показал вам точную последовательность преобразований, которые на самом деле происходят, когда GL вычисляет gl_FragCoord (оконное пространство).

#version 400
layout (location = 0) in vec4 inPosition; // Always use vec4, it makes life easier!
uniform mat4 MVP; // modelViewProjection
uniform vec2 window;

void main()
{
    // Vertex in clip-space
    vec4 fake_frag_coord  = (MVP * inPosition);     // Range:   [-w,w]^4

    // Vertex in NDC-space
    fake_frag_coord.xyz /= fake_frag_coord.w;       // Rescale: [-1,1]^3
    fake_frag_coord.w    = 1.0 / fake_frag_coord.w; // Invert W

    // Vertex in window-space
    fake_frag_coord.xyz *= vec3 (0.5) + vec3 (0.5); // Rescale: [0,1]^3
    fake_frag_coord.xy  *= window;                  // Scale and Bias for Viewport

    // Assume depth range: [0,1] --> No need to adjust fake_frag_coord.z

    [...]
}

Однако координаты текстуры и координаты оконного пространства - это очень разные вещи. Обычно вам нужны нормализованные координаты для традиционных выборок текстур, это означает, что вам нужны координаты в диапазоне [0,1].

К счастью, оконное пространство и пространство текстуры используют одно и то же соглашение о происхождении (0,0) = нижний левый, поэтому вы можете вырезать строку ниже, чтобы получить соответствующие координаты текстуры:

    fake_frag_coord.xy  *= window;                  // Scale and Bias for Viewport
person Andon M. Coleman    schedule 05.06.2014
comment
комментируя ответ Андона М. Коулмана: строка fake_frag_coord.xyz *= vec3 (0.5) + vec3 (0.5); действительно должна быть fake_frag_coord.xyz *= vec3(0.5); fake_frag_coord.xyz += vec3(0.5); (PS: мне не разрешено комментировать напрямую) - person vaifrax; 23.02.2017

Я думаю, что ответ Андона М. Коулмана хорош. Однако мне нравится указывать на более общую проблему с подходом, обсуждаемым в вопросе: может вообще не быть значимого положения на экране для вершины.

Вершина может лежать за пределами усеченной пирамиды. Это не будет проблемой, если нарисованные вами вершины гарантированно лежат в усеченной пирамиде или если вы рисуете только точки.

Но не получится, если у вас есть примитивы, пересекающие ближнюю плоскость. Вы можете подумать, что в таком случае вы просто получаете некоторые координаты, которые находятся за пределами [-1,1] в пространстве NDC, и если вы просто используете их для присвоения некоторого выходного значения для вершины, состояние отсечения сделает это правильно. Но это предположение неверно. Вы можете значения, которые идеально находятся в [-1,1] в пространстве NDC, даже для вершин, которые находятся за пределами усеченной пирамиды, и это будет выглядеть так, как если бы вершины лежали спереди камеры для всех вершин. которые на самом деле находятся позади камеры. И никакой последующий этап отсечения не может это исправить.

Единственный способ получить это право - это фактически выполнить операцию отсечения перед делением на w. И это то, чего вы не хотите делать в вершинном шейдере.

person derhass    schedule 07.06.2014

Если вы хотите, чтобы это работало в части js, вот как я адаптировал ответ Андона М. Коулмана:

var winW = window.innerWidth;
var winH = window.innerHeight;

camera.updateProjectionMatrix();
// Not sure about the order of these! I was using orthographic camera so it didn't matter but double check the order if it doesn't work!
var MVP = camera.projectionMatrix.multiply(camera.matrixWorldInverse);

// position to vertex clip-space
var fake_frag_coord = position.applyMatrix4(MVP);                       // Range:   [-w,w]^4

// vertex to NDC-space
fake_frag_coord.x = fake_frag_coord.x / fake_frag_coord.w;  // Rescale: [-1,1]^3
fake_frag_coord.y = fake_frag_coord.y / fake_frag_coord.w;  // Rescale: [-1,1]^3
fake_frag_coord.z = fake_frag_coord.z / fake_frag_coord.w;  // Rescale: [-1,1]^3
fake_frag_coord.w = 1.0 / fake_frag_coord.w;                                // Invert W

// Vertex in window-space
fake_frag_coord.x = fake_frag_coord.x * 0.5;
fake_frag_coord.y = fake_frag_coord.y * 0.5;
fake_frag_coord.z = fake_frag_coord.z * 0.5;

fake_frag_coord.x = fake_frag_coord.x + 0.5;
fake_frag_coord.y = fake_frag_coord.y + 0.5;
fake_frag_coord.z = fake_frag_coord.z + 0.5;

// Scale and Bias for Viewport (We want the window coordinates, so no need for this)
fake_frag_coord.x = fake_frag_coord.x / winW;
fake_frag_coord.y = fake_frag_coord.y / winH;
person Robert Rodriguez    schedule 17.03.2017