Подтвердить что ты не робот

Сопоставление сферы с кубом

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

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

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

Любые математические гении хотят взломать его?

Здесь уравнения в коде С++:

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 - кубические коорды.

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

(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) на (2). Перегруппируйте (2), пока не получите уравнение многочлена (корня) формы

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

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

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

  • Используя (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, поэтому интуитивно у вас есть еще много точек на кубе, которые отображаются на сфера. Это означает много-одно отображение, и любое такое отображение не является инъективным и, следовательно, не является биективным (т.е. Отображение не имеет обратного.) Извините, но я думаю, вам не повезло.

----- edit --------------

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

Используйте ваши первые два уравнения для решения для x, y, wolfram alpha дает результат:

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 s)

и

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

Ответ 3

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

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

Ответ 4

Похоже, что есть гораздо более чистое решение, если вы не боитесь триггера и пи, не уверен, что это быстрее/сопоставимо.

Просто выделите оставшиеся компоненты после определения лица и выполните:

u = asin ( x ) / half_pi
v = asin ( y ) / half_pi

Это интуитивный скачок, но this, похоже, поддерживает его (хотя и не совсем такую ​​же тему), поэтому, пожалуйста, исправьте меня, если Я ошибаюсь.

Я слишком ленив, чтобы опубликовать иллюстрацию, объясняющую почему.: D