我具有从3D坐标位置获取2D像素位置的功能。 x y z是变换前的坐标(1到-1)。这是一个模型视图体系结构,其中相机永久位于-3.5,0,0,而对象/场景处于0,0,0 通过水平xz旋转和垂直y旋转等变换坐标,以生成最终帧。
此功能主要用于将2D文本覆盖在3D场景的顶部。 2D文本相对于3D基础场景的位置。
void My3D::Get2Dfrom3Dx(float x, float y, float z, float* psx, float* psy) {
XMVECTOR xmScreenCoord = XMLoadFloat3( (XMFLOAT3*) &screenCoord);
XMMATRIX xmWorldViewProjection = XMLoadFloat4x4( (XMFLOAT4X4*) &m_WorldViewProjection);
XMVECTOR result = XMVector3TransformCoord( xmScreenCoord, xmWorldViewProjection);
XMStoreFloat3( (XMFLOAT3*) &screenCoord, result);
screenCoord.x = ((screenCoord.x + 1.0f) / 2.0f) * m_nCurrWidth;
screenCoord.y = ((-screenCoord.y + 1.0f) / 2.0f) * m_nCurrHeight;
*psx = screenCoord.x;
*psy = screenCoord.y; }
当场景完全/几乎可见时(在-4和-1.5之间的眼),此功能可以完美地工作。
我遇到了一个令人困扰的问题,即以3D位置镜像显示的文本(不应该以3D位置显示)。 例如,当我从下面(在对象下方向上60+度)查看图像并缩放(将孔眼位置移至靠近-.5,0,0的位置)时,会发生这种情况。在眼睛后面(请注意,眼孔不超过0,0,0,这确实使图像混乱了), 但是上述功能以某种方式导致计算出的屏幕x y坐标在不应显示的情况下显示在视口中。
我似乎认为有一个解决此副作用的简单方法,但找不到它。希望有人以前见过这个二维镜像问题/效果,并且知道简单的调整。
我意识到我可以走更复杂的路径来确定视图矢量是否与目标点相对并以这种方式进行过滤,但我似乎认为应该有一个更简单的解决方案。
同样,当世界围绕着它转换时,摄像机始终位于-3.5,0,0上,即-.5,0,0。
答案 0 :(得分:1)
问题出在投影的工作方式上。基本上,透视投影将x和y坐标除以z坐标。这就是您获得透视效果的方式,即,距离较远(z坐标较大)的事物在屏幕上显得较小。这种透视划分的一个问题是(简化)它不适用于相机后面的东西。相机后面的东西将具有负的z坐标。将x和y除以负值时,您的点将反映在原点周围。正是您所看到的。由于位于相机后方的东西将始终不可见,因此解决此问题的一种方法是简单地裁剪所有几何图形,然后除以z,以将所有具有负z值的东西都切除并删除。
我认为您代码中的除法发生在XMVector3TransformCoord()
内部。如您所知,无论如何,在有问题的情况下,文本都不应该可见。因此,我建议您仅检查文本是否在相机后面,如果是则不要渲染。这样做的一种方法是简单地检查使用xmWorldViewProjection
矩阵变换世界空间位置的结果,并且仅在碰巧在相机前面时才继续。 xmScreenCoord
保存您的点的同质剪贴空间坐标。如果xmScreenCoord
的z坐标大于零,则该点将位于相机的前面。所以我想你想做类似的事情
if (XMVectorGetZ(xmScreenCoord) > 0)
{
…
}
由于以下注释中的讨论而引起的旁注:当要解决涉及屏幕上对象投影的问题时,通常可以通过将问题转换为对偶并直接在投影空间中工作来避免显式计算投影在齐次坐标上但是,由于您的问题是要在屏幕上以2D形式放置文本,因此我认为这里不是一个选择。您可以将用于绘制文本的几何图形直接放置在剪辑空间中。您可以通过计算要附加2D文本的3D点的剪辑空间坐标(通过将它们乘以m_WorldViewProjection
而不是除以w)来重新开始。然后,您可以通过简单地从该点偏移x和y坐标以获得四边形的角或您需要构造的任何点,来生成用于绘制文本的几何图形的均质坐标。如果然后再通过该点的w坐标缩放四边形的大小,则将在该位置得到一个四边形,该四边形在屏幕上始终投影为相同大小(因为与w的预乘会有效地抵消投影)。但是,您接下来要做的就是将投影的应用程序留给GPU处理。如果要渲染大量的四边形,则可以考虑选择此选项,因为它可以完全在GPU上完成,例如使用几何体着色器。但是,如果您只有几个文本元素,则如上所述跳过摄像机后面的文本元素的绘制会更简单,也可能更有效率……
答案 1 :(得分:0)
Michael的回答非常有帮助,使我继续前进,认为解决方案应该是一个简单的比较。就我而言,我只能通过应用World变换而不是整个WorldViewProjection来重新评估屏幕坐标。我称之为TargetTransformed。然后,我的比较值就是“眼睛/相机”的位置(当世界围绕“眼睛”变形时,永远不会进行调整(缩放除外)。)再次注意,在这种情况下,我的“相机”位于-3.5,0,0,看着0, 0,0(模型的中心,实际上是8,0,0,因此是一条穿过中心的线)。所以我不得不比较x分量,而不是z分量。当目标明显位于相机后方时,由于会出现镜像伪像,因此我会添加一些错误的.1F。在这种情况下,我将在外部空间中返回的最终screenCoord位置转换为(-8000),以确保它们在视口中不可见。
if ((Eye.x + 0.1F) > TargetTransformed.x)
{
screenCoord.x += -8000;
screenCoord.y += -8000;
//TRACE("point is behind camera.\n");
*psx = screenCoord.x;
*psy = screenCoord.y;
}
else
{
*psx = screenCoord.x;
*psy = screenCoord.y;
}
为了完整起见,我的项目有2个视图模型:a)沿模型中心的一条线看。可以将其转换为从任何方向看,并通过屏幕x和y偏移。第一个视图模型可以在上面的代码中正常工作。第二个视图模型b)将相机对准模型的焦点,然后允许围绕该随机点(而不是模型的中心)进行完整旋转,这需要计算一个棘手的附加平移矩阵和矢量(我称为TargetViewTranslation)。对于此附加转换,该公式会添加附加变换的z分量。
if ((Eye.x + 0.1F - m_structTargetViewTranslation.Z) > TargetTransformed.x)
{
screenCoord.x += -8000;
screenCoord.y += -8000;
//TRACE("point is behind camera.\n");
*psx = screenCoord.x;
*psy = screenCoord.y;
}
else
{
*psx = screenCoord.x;
*psy = screenCoord.y;
}
成功,我的镜像文本问题已解决。希望这可以帮助其他人解决此镜像文本问题。意识到可能只需要通过World变换来变换测试用例,这应该是一个简单的比较,并且摄像机的位置可能会影响使用哪个x或z分量。而且,如果您以其他任何方式翻译世界,那么如果比较x或z,这种翻译也可能会产生影响。使用TRACE并查看x y z值有助于确定我在特定情况下需要使用哪些组件。