Math 将球体映射到立方体

Math 将球体映射到立方体,math,geometry,mapping,Math,Geometry,Mapping,有一种特殊的方法可以将立方体映射到此处描述的球体: 这不是基本的“规格化点,然后完成”方法,而是提供了一个更均匀的映射 我试着做从球坐标到立方体坐标的逆映射,但一直无法得出工作方程。这是一个相当复杂的方程组,有很多平方根 有数学天才想尝试一下吗 下面是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 -

有一种特殊的方法可以将立方体映射到此处描述的球体:

这不是基本的“规格化点,然后完成”方法,而是提供了一个更均匀的映射

我试着做从球坐标到立方体坐标的逆映射,但一直无法得出工作方程。这是一个相当复杂的方程组,有很多平方根

有数学天才想尝试一下吗

下面是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是立方体坐标。

重新排列后,你可以得到“漂亮”的形状

其中
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
    的一些函数。二次公式得出(4)的两个根,你必须记住

  • 使用(1)、(2)、(4)算出
    z^2
    的表达式,只需
    x^2

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

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

  • 祝你好运

    注:很可能逆映射不存在,想想几何体:球体的表面积
    4*pi*r^2
    ,而立方体的表面积
    6*d^2=6*(2r)^2=24r^2
    ,因此直观地说,立方体上有更多的点映射到球体。这意味着一个多对一映射,任何这样的映射都不是内射的,因此也不是双射的(即映射没有逆)。对不起,我认为你运气不好

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

    如果您遵循MO的建议,设置
    z=1
    表示您正在查看平面
    z=1
    中的实心正方形

    使用前两个方程求解x,y,wolfram alpha给出结果:

    
    1/2(2 s^2-2 t^2-3 2-3 2-2 2-2 2-2 2-2 2-3 2-3)2-24 t^2 2-2 t^2-2 2 2-2 t^2-2 2 2-2 t^2-2-2 2-2-2(2 s^2 2-2-2 t^2-2-2-2-2 t 2-2 2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-2-3)2 2 2 2 2-3 2 2 2 2 2 2-3 2 2 2 2 2 2 2 2-3)3 2 2 2 2 2 2 2 2 2 2 2-3+3)3)3)3)3)3(2(3)3)2(3)3)2(3)2(2(3)2)2)2)2(3)2(3)2(3)2)2)2)2 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((2s^2-2t^2-3)^2-24t^2)-2s^2+2t^2+3)/sqrt(2)
    


    其中,我使用
    s=sx
    t=sy
    ,我将使用
    u=sz
    。然后你可以使用你的第三个等式来计算
    u=sz
    。也就是说,假设要将球体的顶部映射到立方体。然后,对于任何
    0,有一种方法可以考虑:对于球体中的给定点p,取从原点开始,经过p,并在立方体表面结束的线段。让我是这段的长度。现在你需要做的就是把P乘以L;这相当于将| | P | |从区间[0,1]映射到区间[0,L]。此映射应该是一对一的-球体中的每个点都指向立方体中的唯一点(曲面上的点保持在曲面上)。注意,这是假设一个单位球体和立方体;这个想法应该适用于其他地方,你只需要考虑一些规模因素


    我已经忽略了困难的部分(找到片段),但这是一个标准的光线投射问题。有一些链接解释了如何为任意光线与轴对齐的边界框计算此值;您可能可以简化事情,因为光线从原点开始,然后进入单位立方体。如果你需要帮助简化方程式,请告诉我,我会试试看

    我想把这归功于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;
            }
        }
    

    如果你不害怕trig和pi,看起来有一个更干净的解决方案,但不确定它是否更快/可比

    确定面后,只需取下其余组件,然后执行以下操作:

    u = asin ( x ) / half_pi
    v = asin ( y ) / half_pi
    
    这是一个直观的飞跃,但似乎支持它(虽然不是完全相同的主题),所以请纠正我,如果我错了


    我懒得贴一张说明原因的插图D

    此答案包含
    cube2sphere
    sphere2cube
    ,不受
    a=1
    的限制。因此立方体的侧面为
    2a
    -a
    a
    ,球体的半径为
    a

    我知道这个问题已经问了10年了。不过,如果有人需要,我会给出答案
    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);
    }
    
    u = asin ( x ) / half_pi
    v = asin ( y ) / half_pi
    
    import math
    from random import randint # for testing
    
    def sign_aux(x):
        return lambda y: math.copysign(x, y)
    
    sign = sign_aux(1) # no built-in sign function in python, I know...
    
    def cube2sphere(x, y, z):
        if (all([x == 0, y == 0, z == 0])):
            return 0, 0, 0
    
        def aux(x, y_2, z_2, a, a_2):
            return x * math.sqrt(a_2 - y_2/2 - z_2/2 + y_2*z_2/(3*a_2))/a
        
        x_2 = x*x
        y_2 = y*y
        z_2 = z*z
        a = max(abs(x), abs(y), abs(z))
        a_2 = a*a
    
        return aux(x, y_2, z_2, a, a_2), aux(y, x_2, z_2, a, a_2), aux(z, x_2, y_2, a, a_2)
    
    def sphere2cube(p, q, r):
        if (all([p == 0, q == 0, r == 0])):
            return 0, 0, 0
        def aux(s, t, radius):
            A = 3*radius*radius
            R = 2*(s*s - t*t)
            S = math.sqrt( max(0, (A+R)*(A+R) - 8*A*s*s) )  # use max 0 for accuraccy error
            iot = math.sqrt(2)/2
            s_ = sign(s) * iot * math.sqrt(max(0, A + R - S)) # use max 0 for accuraccy error
            t_ = sign(t) * iot * math.sqrt(max(0, A - R - S)) # use max 0 for accuraccy error
            return s_, t_
        
        norm_p, norm_q, norm_r = abs(p), abs(q), abs(r)
        norm_max = max(norm_p, norm_q, norm_r)
        radius = math.sqrt(p*p + q*q + r*r)
        if (norm_max == norm_p):
            y, z = aux(q, r, radius)
            x = sign(p) * radius
            return x, y, z
        if (norm_max == norm_q):
            z, x = aux(r, p, radius)
            y = sign(q) * radius
            return x, y, z
        x, y = aux(p, q, radius)
        z = sign(r) * radius
        return x, y, z
    
    # measuring accuracy
    
    max_mse = 0
    for i in range(100000):
        x = randint(-20, 20)
        y = randint(-20, 20)
        z = randint(-20, 20)
        p, q, r = cube2sphere(x, y, z)
        x_, y_, z_ = sphere2cube(p, q, r)
        max_mse = max(max_mse, math.sqrt(((x-x_)**2 + (y-y_)**2 + (z-z_)**2))/3)
    print(max_mse)
    
    # 1.1239159602905078e-07
    
    max_mse = 0
    for i in range(100000):
        p = randint(-20, 20)
        q = randint(-20, 20)
        r = randint(-20, 20)
        x, y, z = sphere2cube(p, q, r)
        p_, q_, r_ = cube2sphere(x, y, z)
        max_mse = max(max_mse, math.sqrt(((p-p_)**2 + (q-q_)**2 + (r-r_)**2))/3)
    print(max_mse)
    
    # 9.832883321715792e-08