有一种将立方体映射到此处描述的球体的特殊方法: 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是立方体坐标。
答案 0 :(得分: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;
}
}
}
对于每个面 - 取剩余的立方体矢量分量表示为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);
}
答案 1 :(得分: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
其中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)产生2个根,你必须牢记这一点。
使用(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
对,您将有2 * 2 = 4种可能的解决方案
对于满足这些方程的可能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=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
...等)执行此操作。我建议使用wolfram alpha来解决每个子案例得到的公式,因为它们会像上面那些一样丑陋或丑陋。
答案 2 :(得分:1)
这是你可以考虑的一种方式:对于球体中的给定点P,取出从原点开始的段,穿过P,并在立方体的表面结束。设L是该段的长度。现在你需要做的就是将P乘以L;这相当于映射|| P ||从区间[0,1]到区间[0,L]。此映射应该是一对一的 - 球体中的每个点都会到达立方体中的唯一点(并且表面上的点保留在曲面上)。请注意,这是假设一个单位球体和立方体;这个想法应该在其他地方举行,你只会涉及一些比例因素。
我已经掩盖了困难部分(找到片段),但这是一个标准的光线投射问题。有一些链接here解释了如何为任意光线与轴对齐边界框计算此值;你可以简化一些事情,因为你的光线从原点开始并进入单位立方体。如果您需要帮助来简化方程式,请告诉我,我会对它进行一次尝试。
答案 3 :(得分:1)
此答案包含cube2sphere
和sphere2cube
,不受a = 1
的限制。因此,立方体的侧面2a
从-a
到a
,球体的半径为a
。
我知道问这个问题已经十年了。不过,如果有人需要,我会给出答案。该实现使用Python,
我将(x, y, z)
用于立方体坐标,将(p, q, r)
用于球坐标和相关的下划线变量(x_, y_, z_)
,这意味着它们是使用反函数生成的。
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
我还绘制了一些点以直观地检查功能,这些是结果。
答案 4 :(得分:0)
如果你不害怕触发和pi,看起来有一个更清洁的解决方案,不确定它是否更快/可比。
确定面部后,只需取出剩余的组件,然后执行:
u = asin ( x ) / half_pi
v = asin ( y ) / half_pi
这是一个直观的飞跃,但this似乎支持它(虽然不是完全相同的主题),所以如果我错了请纠正我。
我懒得发布解释原因的插图。 :d