将球体映射到立方体

pet*_*ket 22 mapping math geometry

有一种将立方体映射到此处描述的球体的特殊方法: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);
Run Code Online (Sandbox Code Playgroud)

sx,sy,sz是球体坐标,x,y,z是立方体坐标.

pet*_*ket 13

我想为此付出代价,因为他完成了很多工作.我们答案的唯一区别是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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于每个面 - 取剩余的立方体矢量分量表示为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)
Run Code Online (Sandbox Code Playgroud)

您应该看到内部平方根用于两个方程式中,因此只执行该部分一次.

这是抛出方程式的最终函数,检查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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

因此,这个解决方案并不像立方体到球体映射那么漂亮,但它完成了工作!

任何提高上述代码的效率或阅读能力的建议都值得赞赏!

---编辑---我应该提到我已经测试了这个,到目前为止在我的测试中代码看起来是正确的,结果至少精确到小数点后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);
}
Run Code Online (Sandbox Code Playgroud)


ldo*_*dog 7

经过一些重新安排后,您可以获得"漂亮"的表格

(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
Run Code Online (Sandbox Code Playgroud)

在哪里alpha = sx^2-sy^2,beta = sx^2 - sz^2gamma = sz^2 - sy^2.自己验证一下.

现在我既没有动力,也没有时间,但从这一点来看,它非常简单易于解决:

  1. 将(1)代入(2).重新排列(2)直到得到表格的多项式(根)方程

    (4)    a(x) * y^4  + b(x) * y^2 + c(x) = 0
    
    Run Code Online (Sandbox Code Playgroud)

    这可以使用二次公式来解决y^2.注意,这a(x),b(x),c(x)是一些功能x.二次公式为(4)产生2个根,你必须牢记这一点.

  2. 使用(1),(2),(4)z^2仅表示表达式x^2.

  3. 使用(3)写出形式的多项式根方程:

    (5)    a * x^4  + b * x^2 + c = 0
    
    Run Code Online (Sandbox Code Playgroud)

    哪里a,b,c不是函数而是常量.使用二次公式解决这个问题.对于对,总共有2*2 = 4个可能的解决方案x^2,y^2,z^2意味着对于x,y,z满足这些方程的可能对,您将有4*2 = 8个总解.检查每x,y,z对上的条件并(希望)消除除一个之外的所有条件(否则不存在逆映射.)

祝好运.

PS.很可能是逆映射不存在,考虑几何:球体具有表面积,4*pi*r^2而立方体具有表面积,6*d^2=6*(2r)^2=24r^2因此直观地说,您在立方体上有更多的点被映射到球体.这意味着多对一的映射,任何这样的映射都不是单射的,因此不是双射的(即映射没有逆.)抱歉,但我认为你运气不好.

-----编辑--------------

如果您遵循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=sxt=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...等).我建议使用wolfram alpha来解决每个子案例得到的公式,因为它们会像上面那样丑陋或丑陋.

  • 我不知道逆映射是否存在,但是球体表面上的点比立方体表面上的点多的想法是非常错误的。 (2认同)
  • @gmatt:更多或更少点的概念不适用于infinites.考虑整数和理性之间存在双射.由于无数无限,如表面区域的点,情况甚至更棘手.例如,f(x)= 2x为您提供[0,1]和[0,2]之间的双射,即使间隔具有不同的长度. (2认同)