如何将一个SCNVector3位置转换为CGPoint的SCNView坐标?

4

我有一个应用程序,仅支持横屏显示。

在scenekit中,我有一个物体。该物体位于特定位置,由以下方式指定:

 SCNNode * buttonRef = [scene.rootNode childNodeWithName:@"buttonRef" recursively:YES];
 SCNVector3 buttonRefPosition = [buttonRef position];

现在我需要将 SCNVector3 转换为 mainView 的2D坐标,其中
 mainView = (SCNView *)self.view;

场景使用正交相机,因此没有透视。相机是默认相机。没有旋转,没有平移。相机的正交比例尺为5.4。
我该如何进行转换?
我在SO上找到了其他关于反向问题的答案,即从CGPoint转换为SCNVector3,但对我来说不太明显如何进行反向操作,从SCNVector3到CGPoint。
如果SCNView是UIView的子类,则(0,0)是左上点,在iPhone 6横向情况下,(667,375)是右下点。
我的这个按钮几乎在右侧和高度中间。在UIKit方面,我会说这个按钮位于(600,187),但当我执行以下操作时:
SCNNode * buttonRef = [scene.rootNode childNodeWithName:@"buttonRef" recursively:YES];
SCNVector3 buttonRefPosition = [buttonRef position];

SCNVector3 projectedPoint = [mainView projectPoint: buttonRefPosition];

我得到的projectPoint等于(7.8, 375, 0) (?)
当我使用UIKit将一个10x10的视图添加到坐标(7.8, 375)时,我得到的是左下角的视图!!
正如我所说,这个应用只在横向模式下运行,但这些坐标系统都混乱了。

buttonRef节点的几何形状是一个SceneKit对象(SCNSphereSCNBox等),还是在3D建模工具中创建并作为dae文件加载的东西?所有的SceneKit对象都有一个原点在0,0,0(我的意思是球体的中心位于0,0,0),你使用的模型可能具有不围绕0居中的几何形状。一种快速检查的方法是将一个小的SCNSphere添加为您的buttonRef节点的子节点,并确认它出现在您认为几何形状的原点处。对于我的应用程序,projectPoint返回预期的2D坐标。 - lock
你的评论很有道理。为了测试,我执行了 [scnView projectPoint:SCNVector3Zero];,换句话说,我正在投影 0,0,0。投影结果是 (0, 375, 0)。在我的情况下,在场景中心可见的是 scenekit 中的 0,0,0,但它怎么会变成 (0,375,0),或者在 UIKit 上实际上是 (0,375) 呢?...这是右下角的一个点吗? - Duck
好的测试,我已将其添加到我的项目中,甚至切换到正交投影,仍然在我的视图中心得到了一个2D点。你的SCNView比屏幕尺寸大很多可能吗?在didLayoutView中打印出你的SCNView的frame/bounds会给出什么结果?如果你的视图是屏幕宽度的两倍,左半部分不在视图内,那么这就可以解释你得到的数字。 - lock
返回的结果是 (0,0,667,375),与预期相符。但无论如何,就像我之前所说的,SceneKit 坐标系中应该是 (333,187) 的点被报告为 (0,375),这完全是胡说八道。 - Duck
同意,有些不对劲,我已经没有更多的想法了。 - lock
显示剩余2条评论
2个回答

4
将模型空间中的点转换为屏幕空间中的点称为投影(因为它应用相机的投影变换将3D空间映射到2D空间)。渲染器(通过GPU)每帧执行多次,以将场景中的点显示在屏幕上。
当您需要自行执行此操作时,也有一个方便的方法:projectPoint:

我已经添加了更多信息。 - Duck
这个的反义词是什么?从屏幕进入基于场景的(我猜)相机项目。我应该为此开始另一个问题吗? - Confused
1
或者只需查看链接。具有“project”的相同协议可能还具有听起来相反的东西... - rickster

3

虽然这是一个老问题,但不幸的是@ricksters的回答对我没有真正帮助,因为在我的情况下projectPoint:会向我提供混乱的视图坐标(与@SpaceDog描述的同样的问题)!

然而,我发现,在我的情况下,问题是我已经对根节点应用了一个变换(将世界颠倒过来 ;-))。这显然导致了projectPoint:的错误结果...

由于我无法删除变换,因此我必须相应地调整projectPoint:的输入。

SCNVector3  viewStartPosition = ...; // Some place in the scene
SCNVector3  viewStartPositionRel = SCNVector3FromSCNVector4(SCNMatrix4MultV(self.scene.rootNode.worldTransform, SCNVector4FromSCNVector3(viewStartPosition)));
// Vector from UISceneView 0/0 top/left to start point
SCNVector3  viewStartPositionInViewCoords = [self.sceneView projectPoint:viewStartPositionRel];

很遗憾,苹果没有为SCNMatrix4矩阵提供任何矩阵数学运算,因此使用的(非常简单)矩阵处理方法也是“自制”的:

/*
 SCNVector4FromSCNVector3

 */
SCNVector4 SCNVector4FromSCNVector3(SCNVector3 pV) {
    return SCNVector4Make(pV.x, pV.y, pV.z, 0.0);
}

/*
 SCNVector3FromSCNVector4

 */
SCNVector3 SCNVector3FromSCNVector4(SCNVector4 pV) {
    return SCNVector3Make(pV.x, pV.y, pV.z);
}

/*
 SCNMatrix4MultV

 */
SCNVector4 SCNMatrix4MultV(SCNMatrix4 pM, SCNVector4 pV) {
    SCNVector4  r = {
        pM.m11 * pV.x + pM.m12 * pV.y + pM.m13 * pV.z + pM.m14 * pV.w,
        pM.m21 * pV.x + pM.m22 * pV.y + pM.m23 * pV.z + pM.m24 * pV.w,
        pM.m31 * pV.x + pM.m32 * pV.y + pM.m33 * pV.z + pM.m34 * pV.w,
        pM.m41 * pV.x + pM.m42 * pV.y + pM.m43 * pV.z + pM.m44 * pV.w
    };
    return r;
}

我不确定这是否是SceneKit中的一个错误,或者可能是禁止对根节点应用变换...无论如何:问题已解决 :-)


我遇到了这个问题:"从 '[Float]' 转换为不相关的类型 'SCNVector4' 总是失败"。 - Roshan Bade
func SCNMatrix4MultV(_ pM: SCNMatrix4, _ pV: SCNVector4) -> SCNVector4 { let r = [pM.m11 * pV.x + pM.m12 * pV.y + pM.m13 * pV.z + pM.m14 * pV.w, pM.m21 * pV.x + pM.m22 * pV.y + pM.m23 * pV.z + pM.m24 * pV.w, pM.m31 * pV.x + pM.m32 * pV.y + pM.m33 * pV.z + pM.m34 * pV.w, pM.m41 * pV.x + pM.m42 * pV.y + pM.m43 * pV.z + pM.m44 * pV.w] as? SCNVector4 return r! }对于这个函数,我得到了“从 '[Float]' 到不相关类型 'SCNVector4' 的转换总是失败”的错误,并且在返回时应用程序崩溃为 nil。 - Roshan Bade
我对Swift并不是很熟悉,但我猜想[]术语并不会给出一个SCNVector4,而是一个Float,然后被强制转换为SCNVector4...尝试使用SCNVector4 init(x,y,z,w)方法。 - LaborEtArs
在Swift中,您可以这样编写代码: func SCNMatrix4MultV(_ pM: SCNMatrix4, _ pV: SCNVector4) -> SCNVector4 { let r = SCNVector4(x: pM.m11 * pV.x + pM.m12 * pV.y + pM.m13 * pV.z + pM.m14 * pV.w, y: pM.m21 * pV.x + pM.m22 * pV.y + pM.m23 * pV.z + pM.m24 * pV.w, z: pM.m31 * pV.x + pM.m32 * pV.y + pM.m33 * pV.z + pM.m34 * pV.w, w: pM.m41 * pV.x + pM.m42 * pV.y + pM.m43 * pV.z + pM.m44 * pV.w) return r } - Raja Kishan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接