Нахождение кватерниона, представляющего преобразование из одного вектора в другой - PullRequest
1 голос
/ 01 апреля 2019

Введение.

Ранее я задавал вопрос о преобразовании rgb тройного в кватернион. После этого вопроса мне удалось получить кватернионы, но я сомневаюсь в их внутренней структуре. Не было простого способа управлять ими, а также отделять luma и chroma , поскольку это были кватернионы единичной длины. Согласно моим ощущениям, яркость должна быть закодирована либо в реальной части, либо в целой величине; И информация о цвете " chroma " должна быть закодирована в мнимой части.

Сегодня я решил улучшить ситуацию, приняв другой подход, отличный от первого в ссылке выше. Я думаю, что это может иметь успех, поскольку кватернион может хранить не только вращение (единичный кватернион), но и масштаб. Перво-наперво, поэтому я начну с объяснения моей следующей идеи. Я бы использовал синтаксис шейдера GLSL в следующих пояснениях.


Описание подхода и тело вопроса.

Для некоторого пикселя изображения, давайте создадим трехмерный вектор vec3 u в единичном кубе, где положительные координаты лежат в закрытом диапазоне [0.0, 1.0] и представляют полное rgb цветовое пространство. Так что теперь координаты u, u.x, u.y и u.z будут представлять красные, зеленые и синие значения этого пикселя, соответственно. Тогда давайте возьмем чистый белый вектор const vec3 v = vec3(1.0, 1.0, 1.0);. И давайте определим некоторый кватернион q, чтобы наш вектор u был «v, повернут и масштабирован с кватернионом q». Проще говоря, q должен ответить на вопрос «Как преобразовать v, чтобы получить изначально задуманный цвет u. И давайте введем функцию для этой операции «вращение и масштаб»: vec3 q2c(in vec4 q, in vec3 v) Я назову это конвертером кватерниона в цвет.

Запись q2c(q, v) довольно проста, , как определено : q2c(q, v) == (q*vec4(v, 0.0))*q'. Здесь оператор "*" обозначает умножение кватернионов ; Давайте сделаем это функцией vec4 qmul(in vec4 q1, in vec4 q2). А "q'" обозначает q сопряженное , давайте сделаем это vec4 qconj(in vec4 q). Опуская их простую реализацию (которую вы можете найти в полном исходном коде), мы подошли бы к классическому коду:

vec4 q2c(in vec4 q, in vec3 v) {
    return qmul(qmul(q, vec4(v, 0.0)), qconj(q));
}

Итак, теперь у нас есть функция q2c(q,v), которая преобразует кватернион q в цвет, вращая и масштабируя выбранный трехмерный вектор v.

Вопрос Как найти этот кватернион q?

С точки зрения программиста цель состоит в том, чтобы написать обратную функцию vec4 c2q(in vec3 u, in vec3 v) - соответствующий преобразователь "цвет в кватернион".

Обратите внимание, что вам не следует прикасаться к q2c() без уважительной причины. Например, серьезная ошибка в логике, приводящая к «невозможности решить задачу», и вы можете это доказать.


Как вы можете проверить, правильный ли ваш ответ?

Действительно, метод проверки возник бы из-за того, что вы получите начальное значение, если вам удастся конвертировать вперед и назад. Таким образом, условие проверки равно . Для любой ненулевой длины v, u всегда должно быть равно q2c(c2q(u, v), v). v должна иметь ненулевую длину, потому что нельзя «масштабировать ноль», чтобы получить «что-то».

Чтобы упростить задачу, я подготовил программу тестирования , используя сервис shadertoy.com.

Вам потребуется приличный компьютер с работающим подключением к Интернету и веб-браузер с поддержкой webGL (я использую Chrome). Программа должна работать на любом графическом процессоре, даже встроенном в процессоры Intel. Он даже работал на моем нижнем смартфоне!

Чтобы проверить свой ответ, вы должны поместить предложенную формулу, написанную в синтаксисе GLSL, в функцию c2q(). Затем нажмите кнопку «Применить», и ваши изменения вступят в силу: enter image description here

Изображение слева представляет некоторые неизмененные исходные пиксели.А правая половина будет содержать пиксели, преобразованные вперед и назад на q2c(c2q()).Очевидно, что половинки должны быть визуально равны, вы не должны заметить никакой вертикальной линии.Может возникнуть небольшая математическая (незаметная) ошибка , но только из-за природы с плавающей точкой - ее конечной точности и возможных ошибок округления.

Не стесняйтесь редактировать и экспериментировать, изменения будут внесенытолько локально, на вашем компьютере, и вы ничего не можете разрушить.Если видео не воспроизводится при первом открытии (ошибка шадертой) - попробуйте приостановить / отменить его.Наслаждайтесь!


Зал c2q() Попытки

Если все правильно, правая сторона изображения (обработанная) должна быть равна левой стороне (оригинал).И здесь я бы рассмотрел различные результаты, которые были получены путем помещения чего-то вместо xxxxx в реализацию c2q():

vec4 c2q(vec3 u, vec3 v) {
    return xxxxx;
}

Давайте продолжим!

  • Сначала я думал, что это должно сработать: vec4(cross(u, v), dot(u, v)): enter image description here

  • Один из SE ответов : vec4( cross(u, v), sqrt( dot(u, u) * dot(v, v) ) + dot(u, v) ): enter image description here

  • И с его подсказкой " Не забудьте нормализовать q ": normalize(vec4( cross(u, v), sqrt( dot(u, u) * dot(v, v) ) + dot(u, v) )): enter image description here

  • Комментарий @ minorlogic, похоже, на шаг ближе: масштабирует все компоненты q на sqrt( length(v)/length(u) ), vec4(cross(u, v), dot(u, v)) * sqrt( length(u)/length(v) ): enter image description here

  • С измененным коэффициентом: vec4(cross(u, v), dot(u, v)) * sqrt( length(v)/length(u) ): enter image description here

1 Ответ

1 голос
/ 05 апреля 2019

Моя попытка:

vec4 c2q(vec3 u, vec3 v) {
    float norm_q = sqrt(length(u) / length(v));
    vec4 u4 = vec4(normalize(u), 0.0);
    vec4 v4 = vec4(normalize(v), 0.0);
    return norm_q * (qmul(u4, v4 + u4) / length(v4 + u4));
}
...