我尝试从笛卡尔坐标系中的一个点找到球坐标中的角度 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。
显然,有什么地方不对劲。
看起来有几个问题。我不会评论你的代码结构,只是转换......
作为参考,这里是笛卡尔-球面转换的方程。
您从 Spherical 到笛卡尔的转换不正确。它看起来既向后又......错了。这是正确的方程(请参阅下面关于交换 y/z 的注释)
_pos[0] = _r* sin_theta * cos_phi;
_pos[1] = _r* sin_theta * sin_phi;
_pos[2] = _r* cos_theta;
方程中的点积没有用,结果只是y
向量的分量:
phi = FastMath::ACos(dir.y);
另一个问题是您似乎交换了 Y 轴和 Z 轴。只要您保持一致,这很好。即使进行了这种交换,您从球形到笛卡尔的转换仍然不正确。让我们继续经典。
phi = FastMath::ACos(dir.z);
接下来,您将使用叉积来帮助确定acos
函数的范围。这是一个“聪明”的技巧,直到您手动计算乘积并发现您实际上只是在检查 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 产品没用,你只是抓住了 x
theta = FastMath::ACos(dir.x);
简而言之,您可以看到您缺少组件并且使用了错误的触发函数。使用 ArcTan 而不是ArcCos。
theta = ATan2(dir.y, dir.x);
同样,看起来您正在使用叉积来修复范围。然而,手工计算产品:
cross ( (1,0,0) , (x,y,z) ) => (0,-z,y)
因此,您再次只需检查dir.z
组件的符号,无需额外计算。
我的建议是首先使用经典方程来回转换。不要担心修复范围,您可以稍后在您的代码工作时进行。
一旦工作正常,请修复范围。范围正确后,再担心交换 Z 轴和 Y 轴(或不交换)。
如果您不确定点积或交叉积正在做什么,请手动解决。忽略这些函数的几何意义,您会发现它们在浪费计算并且通常使您的代码复杂化。
祝你好运!
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句