我试图从笛卡尔坐标系中的一个点找到球面坐标中的角度theta和phi。
我找到答案,这不正确。但我无法弄清楚发生了什么。 请帮帮我。
这是我的代码:
Vector4 g_eye(8.0f, 8.0f, 8.0f);
Vector4 g_lookat(0.0f, 0.0f, 0.0f);
Vector4 g_up(0.0f, -1.0f, 0.0f);
struct spherical_sys
{
spherical_sys(Vector4& p)
{
_dirty = 1;
_pos = p;
_pos.w = 0.0f;
get_spherical(_pos, _theta, _phi, _r);
}
float getTheta()
{
return _theta;
}
float getPhi()
{
return _phi;
}
void setTheta(float t)
{
_theta = t;
_dirty = 1;
}
void setPhi(float t)
{
_phi = t;
_dirty = 1;
}
Vector4 get_pos()
{
if (_dirty)
{
float sin_phi, cos_phi;
float sin_theta, cos_theta;
FastMath::SinCos(_phi, sin_phi, cos_phi);
FastMath::SinCos(_theta, sin_theta, cos_theta);
_pos.w = 0.0f;
_pos[0] = _r* cos_phi * cos_theta;
_pos[1] = _r* sin_phi;
_pos[2] = _r* cos_phi * sin_theta;
_dirty = 0;
}
return _pos;
}
private:
void get_spherical(Vector4& dir, float& theta, float& phi, float& r)
{
r = dir.Length();
dir.Normalize();
phi = FastMath::ACos(Vector3Dotf(dir, Vector4(0.0f, 1.0f, 0.0f, 0.0f)));
Vector4 v = Vector3CrossProduct(Vector4(0.0f, 1.0f, 0.0f, 0.0f), dir);
if (v.x < 0.0f)
{
phi *= -1;
phi = phi + MATH_PI * 0.5f;
}
else
{
phi = phi - MATH_PI * 0.5f;
}
theta = FastMath::ACos(Vector3Dotf(dir, Vector4(1.0f, 0.0f, 0.0f, 0.0f)));
v = Vector3CrossProduct(Vector4(1.0f, 0.0f, 0.0f, 0.0f), dir);
if (v.y < 0.0f)
{
theta *= -1;
}
}
float _phi;
float _theta;
float _r;
Vector4 _pos;
int _dirty;
};
spherical_sys _teye(g_eye);
spherical_sys _tup(g_up);
_teye.get_pos()的答案.x,_teye.get_pos()。y,_ teye.get_pos()。z为6.531521,-7.998896,-9.238880。
显然,出了点问题。
答案 0 :(得分:2)
看起来像几个问题。我不会评论你的代码结构,只是转换....
供参考,以下是Cartesian-Spherical conversions的等式。
您从Spherical转换为笛卡儿的行为不正确。它看起来既倒退也错......以下是正确的公式(请参阅下面关于交换y / z的说明)
_pos[0] = _r* sin_theta * cos_phi;
_pos[1] = _r* sin_theta * sin_phi;
_pos[2] = _r* cos_theta;
等式中的Dot积无效,结果只是向量的y
分量:
phi = FastMath::ACos(dir.y);
另一个问题是你似乎已经交换了Y轴和Z轴。只要你保持一致,这很好。即使使用此交换,您从球形到笛卡尔的转换仍然不正确。让我们保持古典。
phi = FastMath::ACos(dir.z);
接下来,您使用交叉产品来帮助修复acos
功能的范围。这是一个聪明的&#39;诀窍,直到你compute the product by hand并且看到你实际上只是检查Z坐标。
cross( (0,1,0), (x,y,z) ) => (z,0,-x)
接下来,我们将检查theta计算:
theta = FastMath::ACos(Vector3Dotf(dir, Vector4(1.0f, 0.0f, 0.0f, 0.0f)));
再一次,Dot prodcut是无用的,你只是抓住了x
theta = FastMath::ACos(dir.x);
简化后,您可以看到缺少组件并且使用了错误的trig功能。使用ArcTan和不 ArcCos。
theta = ATan2(dir.y, dir.x);
再次,看起来您正在使用交叉产品来修复范围。然而,手动计算产品:
cross ( (1,0,0) , (x,y,z) ) => (0,-z,y)
所以你再次只是检查dir.z
组件的符号,不需要额外的计算。
我的建议是先使用经典方程来回转换。不要担心修复范围,以后可以在代码工作时执行此操作。
一旦有效,请修复范围。范围正确后,担心交换Z轴和Y轴(或不要)。
如果您不确定Dot产品或Cross产品的作用,请手动完成。忽略这些函数的几何重要性,您会发现它们正在浪费计算并且通常会使代码复杂化。
祝你好运!