Применение матрицы преобразования перспективы из GIMP в шейдер GLSL - PullRequest
0 голосов
/ 25 мая 2018

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

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

private final Matrix3 perspectiveTransform = new Matrix3(new float[] {
    0.58302f, -0.29001f, 103.0f,
    -0.00753f, 0.01827f, 203.0f,
    -0.00002f, -0.00115f, 1.0f
});

Эта матрица перспективы выполняла результатЯ хочу в GIMP, используя изображение 500x500.Затем я пытаюсь применить эту же матрицу к координатам текстуры.Вот почему я умножаю на 500 до и делю на 500 после.

attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;

uniform mat4 u_projTrans;
uniform mat3 u_rotation;
uniform mat3 u_perspective;

varying vec4 v_color;
varying vec2 v_texCoords;

void main() {
    v_color = a_color;

    vec3 vec = vec3(a_texCoord0 * 500.0, 1.0);

    vec = vec * u_perspective;

    vec = vec3((vec.xy / vec.z) / 500.0, 0.0);

    vec -= vec3(0.5, 0.5, 0.0);
    vec = vec * u_rotation;
    v_texCoords = vec.xy + vec2(0.5);

    gl_Position = u_projTrans * a_position;
}

Для поворота я смещаю начало координат так, чтобы оно вращалось вокруг центра вместо верхнего левого угла.

Практически все, что я знаю об инструменте перспективы GIMP, взято из http://www.math.ubc.ca/~cass/graphics/manual/pdf/ch10.ps Это предполагало, что я смогу воспроизвести то, что делает GIMP после прочтения, но, оказывается, я не могу.Результат ничего не показывает (без пикселей), в то время как удаление части перспективы показывает правильное вращение изображения.

Как упоминалось в ссылке, я делю на vec.z, чтобы преобразовать мои однородные координаты обратно в 2D точку.Я не использую смещение начала координат для преобразования перспективы, поскольку в ссылке упоминалось, что верхний левый угол использовался как источник.стр.11:

Есть одна вещь, о которой следует быть осторожным - начало координат GIMP находится слева вверху, причем y увеличивается вниз.

РЕДАКТИРОВАТЬ :

Благодаря ответу @ Rabbid76, теперь он что-то показывает!Однако, это не трансформирует мою текстуру, как матрица трансформировала мое изображение в GIMP.

Моя матрица преобразования в GIMP должна была делать что-то вроде этого:

Expected

Но вместо этого это выглядит примерно так:

Actual

Это то, что я думаю из того, что вижуот фактического результата:

https://imgur.com/X56rp8K ( Изображение используется)

(Как указывалось, это параметр текстуры - зажим к краю, а не зажим к границе, но это не относится к делу)

Похоже, что он делает прямо противоположное тому, что я ищу.Я попытался сместить начало координат в центр изображения и внизу слева, прежде чем применять матрицу безуспешно.Это новый результат, но это все та же проблема: как применить матрицу перспективы GIMP в шейдер GLSL?

EDIT2 :

С помощью дополнительного тестирования я могуПодтвердите, что он делает «обратное».Используя эту простую матрицу преобразования с уменьшением масштаба:

private final Matrix3 perspectiveTransform = new Matrix3(new float[] {
        0.75f, 0f, 50f,
        0f, 0.75f, 50f,
        0f, 0f, 1.0f
});

В результате получается увеличенная версия изображения:

Opposite result

Если инвертироватьматрица программно, она работает для простой матрицы масштабирования!Но для матрицы перспективы это показывает, что:

https://imgur.com/v3TLe2d

EDIT3 :

Благодаря @ Rabbid76 снова получилось применить вращениепосле того, как матрица перспективы выполняет вращение раньше, и я получаю такой результат: https://imgur.com/n1vWq0M

Это почти все!Единственная проблема в том, что изображение ОЧЕНЬ сдавлено.Это как перспективная матрица применялась несколько раз.Но если вы посмотрите внимательно, вы можете увидеть, как он вращается в перспективе, как я хочу.Теперь проблема в том, как отговорить его, чтобы получить результат, как у меня в GIMP.(Основная проблема остается той же, как взять матрицу GIMP и применить ее в шейдере)

1 Ответ

0 голосов
/ 25 мая 2018

Эта перспективная матрица достигла желаемого результата в GIMP с использованием изображения 500x500.Затем я пытаюсь применить эту же матрицу к координатам текстуры.Вот почему я умножаю на 500 до и делю на 500 после.

Матрица

 0.58302 -0.29001 103.0
-0.00753  0.01827 203.0
-0.00002 -0.00115 1.0f

- это матрица преобразования 2D-перспективы.Работает с 2D Однородная координата .См. 2D аффинно-перспективные матрицы преобразования

Поскольку матрица, отображаемая в GIMP , является преобразованием из перспективы в ортогональное представление, обратная матрица должна бытьиспользуется для преобразования.Обратную матрицу можно вычислить, вызвав inv().

. Матрица настроена на выполнение операции декартовой координаты в диапазоне [0, 500] до Однородные координаты в диапазоне [0, 500].

Ваше предположение верно, вы должны масштабировать входной сигнал от диапазона [0, 1] до [0, 500] и выходной сигнал от [0, 500] до [0, 1].Но вы должны масштабировать 2D декартовы координатыДалее вам нужно выполнить вращение после проекции перспективы и Перспективное деление .Может потребоваться (в зависимости от растрового изображения и атрибутов координат текстуры), что вам нужно перевернуть координату V координат текстуры.

И самое главное, преобразование должно быть выполнено для каждого фрагментаво фрагментном шейдере .Обратите внимание, поскольку это преобразование не является линейным (это перспективное преобразование), его недостаточно для вычисления координат текстуры в угловых точках.

vec2 Project2D( in vec2 uv_coord )
{
    vec2 v_texCoords;

    const float scale = 500.0;

    // flip Y
    //vec2 uv = vec2(uv_coord.x, 1.0 - uv_coord.y);
    vec2 uv = uv_coord.xy;

    // uv_h: 3D homougenus in range [0, 500] 
    vec3 uv_h = vec3(uv * scale, 1.0) * u_perspective;

    // uv_h: perspective devide and downscale [0, 500] -> [0, 1]
    vec3 uv_p = vec3(uv_h.xy / uv_h.z / scale, 1.0);

    // rotate
    uv_p = vec3(uv_p.xy - vec2(0.5), 0.0) * u_rotation + vec3(0.5, 0.5, 0.0);

    return uv_p.xy; 
}

Конечно, вы можете выполнить преобразование в вершинном шейдере.тоже.Но затем вы должны передать 2d однородную координату от вершинного шейдера до фрагментного шейдера. Это похоже на установку координат пространства клипа на gl_Position.Разница в том, что у вас есть двумерная однородная координата, а не 3d.и вам нужно сделать Перспективное деление вручную в фрагментном шейдере:

Вершинный шейдер:

attribute vec2 a_texCoord0;
varying   vec3 v_texCoords_h;

uniform mat3 u_perspective

vec3 Project2D( in vec2 uv_coord )
{
    vec2 v_texCoords;

    const float scale = 500.0;

    // flip Y
    //vec2 uv = vec2(uv_coord.x, 1.0 - uv_coord.y);
    vec2 uv = uv_coord.xy;

    // uv_h: 3D homougenus in range [0, 500] 
    vec3 uv_h = vec3(uv * scale, 1.0) * u_perspective;

    // downscale
    return vec3(uv_h.xy / scale, uv_h.z);
}

void main()
{
    v_texCoords_h = Project2D( a_texCoord0 );

    .....
}

Фрагментный шейдер:

varying vec3 v_texCoords_h;

uniform mat3 u_rotation;

void main()
{
    // perspective divide
    vec2 uv = vertTex.xy / vertTex.z;

    // rotation
    uv = (vec3(uv.xy - vec2(0.5), 0.0) * u_rotation + vec3(0.5, 0.5, 0.0)).xy;

    .....
}

Смотрите предварительный просмотр, где я использовал следующую матрицу 2D проекции, которая является обратной матрицей от той, которая отображается в GIMP :

2.452f,     2.6675f,    -388.0f,
0.0f,       7.7721f,    -138.0f,
0.00001f,   0.00968f,    1.0f

bommerang


Примечание: по сравнению с u_projTrans, u_perspective инициализируется в мажорном порядке строки.

Из-за этого вам нужно умножить вектор слева на u_perspective:

vec_h = vec3(vec.xy * 500.0, 1.0) * u_perspective;

Но вы должны умножить вектор справа на u_projTrans:

gl_Position = u_projTrans * a_position;

См. Программирование GLSL / Векторные и матричные операции и Тип данных (GLSL)

Конечно, это может измениться, если вы транспонируете матрицу при установке ее на glUniformMatrix*

...