Отображение сферы к кубу

Существует специальный способ отобразить куб на сферу, описанную здесь: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html

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

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

Какие-либо математические гении хотят взять трещину в нем?

Вот уравнения в коде C++:

sx = x * sqrtf(1.0f - y * y * 0.5f - z * z * 0.5f + y * y * z * z / 3.0f);

sy = y * sqrtf(1.0f - z * z * 0.5f - x * x * 0.5f + z * z * x * x / 3.0f);

sz = z * sqrtf(1.0f - x * x * 0.5f - y * y * 0.5f + x * x * y * y / 3.0f);

sx, sy, sz являются проводами сферы, и x, y, z являются проводами куба.

22
задан user1118321 15 March 2015 в 05:41
поделиться

3 ответа

Я хочу отдать должное gmatt, потому что он проделал большую работу. Единственная разница в наших ответах - это уравнение для x.

Чтобы выполнить обратное преобразование сферы в куб, сначала определите грань куба, на которую проецируется точка сферы. Этот шаг прост - просто найдите компонент вектора сферы с наибольшей длиной следующим образом:

// map the given unit sphere position to a unit cube position
void cubizePoint(Vector3& position) {
    double x,y,z;
    x = position.x;
    y = position.y;
    z = position.z;

    double fx, fy, fz;
    fx = fabsf(x);
    fy = fabsf(y);
    fz = fabsf(z);

    if (fy >= fx && fy >= fz) {
        if (y > 0) {
            // top face
            position.y = 1.0;
        }
        else {
            // bottom face
            position.y = -1.0;
        }
    }
    else if (fx >= fy && fx >= fz) {
        if (x > 0) {
            // right face
            position.x = 1.0;
        }
        else {
            // left face
            position.x = -1.0;
        }
    }
    else {
        if (z > 0) {
            // front face
            position.z = 1.0;
        }
        else {
            // back face
            position.z = -1.0;
        }
    }
}

Для каждой грани - возьмите оставшиеся компоненты вектора куба, обозначенные как s и t, и решите их, используя эти уравнения, которые основаны на остальные компоненты вектора сферы обозначены как a и b:

s = sqrt(-sqrt((2 a^2-2 b^2-3)^2-24 a^2)+2 a^2-2 b^2+3)/sqrt(2)
t = sqrt(-sqrt((2 a^2-2 b^2-3)^2-24 a^2)-2 a^2+2 b^2+3)/sqrt(2)

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

Вот последняя функция с добавленными уравнениями и проверкой 0,0 и -0,0 и кодом для правильной установки знака компонента куба - он должен быть равен знаку компонента сферы.

void cubizePoint2(Vector3& position)
{
    double x,y,z;
    x = position.x;
    y = position.y;
    z = position.z;

    double fx, fy, fz;
    fx = fabsf(x);
    fy = fabsf(y);
    fz = fabsf(z);

    const double inverseSqrt2 = 0.70710676908493042;

    if (fy >= fx && fy >= fz) {
        double a2 = x * x * 2.0;
        double b2 = z * z * 2.0;
        double inner = -a2 + b2 -3;
        double innersqrt = -sqrtf((inner * inner) - 12.0 * a2);

        if(x == 0.0 || x == -0.0) { 
            position.x = 0.0; 
        }
        else {
            position.x = sqrtf(innersqrt + a2 - b2 + 3.0) * inverseSqrt2;
        }

        if(z == 0.0 || z == -0.0) {
            position.z = 0.0;
        }
        else {
            position.z = sqrtf(innersqrt - a2 + b2 + 3.0) * inverseSqrt2;
        }

        if(position.x > 1.0) position.x = 1.0;
        if(position.z > 1.0) position.z = 1.0;

        if(x < 0) position.x = -position.x;
        if(z < 0) position.z = -position.z;

        if (y > 0) {
            // top face
            position.y = 1.0;
        }
        else {
            // bottom face
            position.y = -1.0;
        }
    }
    else if (fx >= fy && fx >= fz) {
        double a2 = y * y * 2.0;
        double b2 = z * z * 2.0;
        double inner = -a2 + b2 -3;
        double innersqrt = -sqrtf((inner * inner) - 12.0 * a2);

        if(y == 0.0 || y == -0.0) { 
            position.y = 0.0; 
        }
        else {
            position.y = sqrtf(innersqrt + a2 - b2 + 3.0) * inverseSqrt2;
        }

        if(z == 0.0 || z == -0.0) {
            position.z = 0.0;
        }
        else {
            position.z = sqrtf(innersqrt - a2 + b2 + 3.0) * inverseSqrt2;
        }

        if(position.y > 1.0) position.y = 1.0;
        if(position.z > 1.0) position.z = 1.0;

        if(y < 0) position.y = -position.y;
        if(z < 0) position.z = -position.z;

        if (x > 0) {
            // right face
            position.x = 1.0;
        }
        else {
            // left face
            position.x = -1.0;
        }
    }
    else {
        double a2 = x * x * 2.0;
        double b2 = y * y * 2.0;
        double inner = -a2 + b2 -3;
        double innersqrt = -sqrtf((inner * inner) - 12.0 * a2);

        if(x == 0.0 || x == -0.0) { 
            position.x = 0.0; 
        }
        else {
            position.x = sqrtf(innersqrt + a2 - b2 + 3.0) * inverseSqrt2;
        }

        if(y == 0.0 || y == -0.0) {
            position.y = 0.0;
        }
        else {
            position.y = sqrtf(innersqrt - a2 + b2 + 3.0) * inverseSqrt2;
        }

        if(position.x > 1.0) position.x = 1.0;
        if(position.y > 1.0) position.y = 1.0;

        if(x < 0) position.x = -position.x;
        if(y < 0) position.y = -position.y;

        if (z > 0) {
            // front face
            position.z = 1.0;
        }
        else {
            // back face
            position.z = -1.0;
        }
    }

Итак, это решение не так красиво, как отображение куба в сферу, но оно выполняет свою работу!

Приветствуются любые предложения по повышению эффективности или читаемости приведенного выше кода!

--- edit --- Я должен упомянуть, что я тестировал это, и до сих пор в моих тестах код кажется правильным, а результаты точны как минимум до 7-го знака после запятой. И это было с тех пор, как я использовал поплавки, теперь, наверное, точнее с двойниками.

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

const float isqrt2 = 0.70710676908493042;

vec3 cubify(const in vec3 s)
{
float xx2 = s.x * s.x * 2.0;
float yy2 = s.y * s.y * 2.0;

vec2 v = vec2(xx2 – yy2, yy2 – xx2);

float ii = v.y – 3.0;
ii *= ii;

float isqrt = -sqrt(ii – 12.0 * xx2) + 3.0;

v = sqrt(v + isqrt);
v *= isqrt2;

return sign(s) * vec3(v, 1.0);
}

vec3 sphere2cube(const in vec3 sphere)
{
vec3 f = abs(sphere);

bool a = f.y >= f.x && f.y >= f.z;
bool b = f.x >= f.z;

return a ? cubify(sphere.xzy).xzy : b ? cubify(sphere.yzx).zxy : cubify(sphere);
}
13
ответ дан 29 November 2019 в 05:40
поделиться

Вот один способ подумать об этом: для данной точки P на сфере возьмите отрезок, который начинается в начале координат, проходит через P и заканчивается на поверхности куба. Пусть L - длина этого отрезка. Теперь все, что вам нужно сделать, это умножить P на L; это эквивалентно отображению || P || из интервала [0, 1] в интервал [0, L]. Это сопоставление должно быть взаимно однозначным - каждая точка сферы переходит в уникальную точку куба (а точки на поверхности остаются на поверхности). Обратите внимание, что это предполагает единичную сферу и куб; идея должна быть в другом месте, вам нужно будет задействовать лишь несколько масштабных факторов.

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

1
ответ дан 29 November 2019 в 05:40
поделиться

После некоторой перестановки вы можете получить "красивые" формы

(1)   1/2 z^2 = (alpha) / ( y^2 - x^2) + 1
(2)   1/2 y^2 = (beta)  / ( z^2 - x^2) + 1
(3)   1/2 x^2 = (gamma) / ( y^2 - z^2) + 1

, где alpha = sx ^ 2-sy ^ 2 , beta = sx ^ 2 - sz ^ 2 и gamma = sz ^ 2 - sy ^ 2 . Убедитесь в этом сами.

Сейчас у меня нет ни мотивации, ни времени, но с этого момента его довольно просто решить:

  1. Заменить (1) на (2). Переставляйте (2), пока не получите полиномиальное (корневое) уравнение вида

     (4) a (x) * y ^ 4 + b (x) * y ^ 2 + c (x) = 0 {{1} } 

    это можно решить, используя формулу корней квадратного уравнения для y ^ 2 . Обратите внимание, что a (x), b (x), c (x) являются некоторыми функциями x . Квадратичная формула дает 2 корня для (4), которые вам нужно иметь в виду.

  2. Используя (1), (2), (4), найдите выражение для z ^ 2 в терминах только x ^ 2 .

  3. Используя (3), запишите уравнение полиномиального корня вида:

     (5) a * x ^ 4 + b * x ^ 2 + c = 0 
     

    где a, b, c не функции, а константы. Решите это, используя формулу корней квадратного уравнения. Всего у вас будет 2 * 2 = 4 возможных решения для пары x ^ 2, y ^ 2, z ^ 2 , что означает, что у вас будет всего 4 * 2 = 8 решений для возможных пары x, y, z , удовлетворяющие этим уравнениям.Проверьте условия для каждой пары x, y, z и (надеюсь) удалите все, кроме одного (иначе обратного отображения не существует).

Удачи.

PS. Вполне возможно, что обратного отображения не существует, подумайте о геометрии: сфера имеет площадь поверхности 4 * pi * r ^ 2 , а куб имеет площадь поверхности 6 * d ^ 2 = 6 * (2r) ^ 2 = 24r ^ 2 , поэтому интуитивно понятно, что у вас есть гораздо больше точек на кубе, которые отображаются на сферу. Это означает отображение "многие к одному", и любое такое сопоставление не является инъективным и, следовательно, не биективным (т.е. отображение не имеет обратного). Извините, но я думаю, вам не повезло.

----- изменить --------------

если вы следуете совету МО, установка z = 1 означает, что вы смотрите на сплошной квадрат в плоскости z = 1 .

Используйте свои первые два уравнения для определения x, y, вольфрам-альфа дает результат:

x = (sqrt (6) s ^ 2 sqrt (1/2 (sqrt ((2 s ^ 2-2 t ^ 2-3) ^ 2-24 t ^ 2) +2 s ^ 2-2 t ^ 2-3) +3) -sqrt (6) t ^ 2 sqrt (1/2 (sqrt ((2 s ^ 2 -2 t ^ 2-3) ^ 2-24 t ^ 2) +2 s ^ 2-2 t ^ 2-3) +3) -sqrt (3/2) sqrt ((2 s ^ 2-2 t ^ 2-3) ^ 2-24 t ^ 2) sqrt (1/2 (sqrt ((2 s ^ 2-2 t ^ 2-3) ^ 2-24 t ^ 2) +2 s ^ 2-2 t ^ 2-3) +3) +3 sqrt (3/2) sqrt (1/2 (sqrt ((2 s ^ 2-2 t ^ 2-3) ^ 2-24 t ^ 2) +2 s ^ 2- 2 t ^ 2-3) +3)) / (6 с)

и

y = sqrt (-sqrt ((2 s ^ 2-2 t ^ 2-3) ^ 2-24 t ^ 2 ) -2 s ^ 2 + 2 t ^ 2 + 3) / sqrt (2)

где выше я использую s = sx и t = sy , и я буду использовать u = sz . Затем вы можете использовать третье уравнение для u = sz . Допустим, вы хотите сопоставить верхнюю часть сферы с кубом.Тогда для любого 0 <= s, t <= 1 (где s, t находятся в системе координат сферы), то кортеж (s, t, u) сопоставляется с (x, y, 1) (здесь x, y находятся в системе координат куба). Вам остается только выяснить, что u есть. Вы можете выяснить это, используя s, t , чтобы найти x, y , а затем используя x, y , чтобы найти u .

Обратите внимание, что при этом будет отображена только верхняя часть куба только верхняя плоскость куба z = 1 . Вам нужно будет сделать это для всех 6 сторон ( x = 1 , y = 1 , z = 0 ... и т. Д.). Я предлагаю использовать вольфрам-альфа для решения результирующих уравнений, которые вы получите для каждого подслучайного случая, потому что они будут такими же уродливыми или уродливыми, как и приведенные выше.

7
ответ дан 29 November 2019 в 05:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: