首先,我在场景中投影一个特定点。

SCNVector3 topLeft = SCNVector3Make(
    -50,
    -50,
    -UIScreen.mainScreen.bounds.size.width);
SCNVector3 projectedPoint = [self.sceneView projectPoint:topLeft];


当我打印该值时,在屏幕上会得到一个预期的位置。

(-50 -50 -667) --> (309 212 1)


但是当我取消投影时,基本上是在寻找反向操作。

SCNVector3 point = SCNVector3Make(309, 212, 1);
SCNVector3 unprojectedPoint = [self.sceneView unprojectPoint:point];


我得到了非常不同的东西。

(309, 212, 1) --> (-7.544 -7.544 -100)


我想念/做错了什么?

最佳答案

您的SCNVector3 topLeft使用屏幕宽度作为z坐标,该宽度与世界坐标系无关。

坐标系


  SceneKit使用右手坐标系,其中(默认情况下)视图方向沿负z轴,如下所示。


https://developer.apple.com/documentation/scenekit/organizing_a_scene_with_nodes

从世界坐标到2D视图的项目点

projectPoint(_ :)


  将点从场景的3D世界坐标系投影到
  渲染器的2D像素坐标系。


https://developer.apple.com/documentation/scenekit/scnscenerenderer/1524089-projectpoint

插图

ios - SceneKit取消投影点会产生奇怪的结果-LMLPHP

在这里,我们看到一个尺寸为2的多维数据集。它位于世界坐标系的原点(x:0,y:0,z:0)。相机位于x:0,y:0,z:10。

现在要将多维数据集的位置(0/0/0)投影到2D视图,可以在Objective-C中编写:

SCNVector3 projectedPoint = [self.sceneView projectPoint:SCNVector3Make(0, 0, 0)];


在iPhone 8 Plus上,我们知道屏幕尺寸为414 x736。因此,我们预期结果为x:207 y:368。

如果我们记录输出,则使用以下代码:

NSLog(@"projectedPoint: %f %f %f", projectedPoint.x, projectedPoint.y, projectedPoint.z);


一个会得到这样的东西:

projectedPoint: 207.000000 368.000000 0.909091


因此,现在使用此projectedPoint并将其取消投影,我们应该再次获取原点:

SCNVector3 unprojectedPoint = [self.sceneView unprojectPoint:projectedPoint];
NSLog(@"unprojectedPoint: %f %f %f", unprojectedPoint.x, unprojectedPoint.y, unprojectedPoint.z);


实际上输出是

unprojectedPoint: 0.000000 0.000000 0.000000


完整的例子

这里是上述场景的完整代码:

@interface GameViewController ()

@property (nonatomic, weak) SCNView *scnView;
@property (nonatomic, strong) SCNNode *cameraNode;
@property (nonatomic, strong) SCNScene *scnScene;
@property (nonatomic, strong) SCNNode *boxNode;

@end

@implementation GameViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self _setupView];
    [self _setupScene];
    [self _setupCamera];
    [self _setupTapping];
    [self _addCube];
}

-(void)_setupTapping {
    UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_tapHandler:)];
    self.scnView.gestureRecognizers = @[tapGestureRecognizer];
}

-(void)_tapHandler:(UIGestureRecognizer *)gestureRecognizer {
    NSLog(@"--------------");
    SCNVector3 projectedPoint = [self.scnView projectPoint:SCNVector3Make(0, 0, 0)];
    NSLog(@"projectedPoint: %f %f %f", projectedPoint.x, projectedPoint.y, projectedPoint.z);

    SCNVector3 unprojectedPoint = [self.scnView unprojectPoint:projectedPoint];
    NSLog(@"unprojectedPoint: %f %f %f", unprojectedPoint.x, unprojectedPoint.y, unprojectedPoint.z);

    CGPoint point = [gestureRecognizer locationInView:self.scnView];
    SCNVector3 scenePoint = [self.scnView unprojectPoint:SCNVector3Make(point.x, point.y, projectedPoint.z)];
    NSLog(@"point %f %f -> scenePoint: %f %f %f", point.x, point.y, scenePoint.x, scenePoint.y, scenePoint.z);
}

-(void)_setupView {
    self.scnView = (SCNView *)self.view;
    self.scnView.autoenablesDefaultLighting = YES;
}

-(void)_setupScene {
    self.scnScene = [SCNScene new];
    self.scnView.scene = self.scnScene;
}

- (void)_setupCamera {
    self.cameraNode = [SCNNode new];
    self.cameraNode.camera = [SCNCamera camera];
    self.cameraNode.position = SCNVector3Make(0, 0, 10);
    [self.scnScene.rootNode addChildNode:self.cameraNode];
}

- (void)_addCube {
    SCNBox *box = [SCNBox boxWithWidth:2 height:2 length:2 chamferRadius:0];
    SCNMaterial *mat = [SCNMaterial new];
    mat.diffuse.contents = @"art.scnassets/crosshair.png";
    box.materials = @[mat];
    self.boxNode = [SCNNode new];
    self.boxNode.geometry = box;
    self.boxNode.position = SCNVector3Make(0, 0, 0);
    [self.scnScene.rootNode addChildNode:self.boxNode];
}

- (BOOL)prefersStatusBarHidden {
    return YES;
}

@end


点击多维数据集的左上角

示例中的立方体具有十字准线纹理。在iPhone上,它看起来像这样:

ios - SceneKit取消投影点会产生奇怪的结果-LMLPHP

因此,现在您可以点击视图上的某个位置。

用下面的代码

CGPoint point = [gestureRecognizer locationInView:self.scnView];
SCNVector3 scenePoint = [self.scnView unprojectPoint:SCNVector3Make(point.x, point.y, projectedPoint.z)];
NSLog(@"point %f %f -> scenePoint: %f %f %f", point.x, point.y, scenePoint.x, scenePoint.y, scenePoint.z);


我们在上图中以网格显示的区域上输出未投影的点(世界坐标系)。

由于立方体的尺寸为2,并且位于原点,因此确切的世界坐标点将为x:-1,y:-1,z:0。

例如,当我在模拟器中点击多维数据集的左上角时,我得到类似以下内容:

point 141.000000 299.000000 -> scenePoint: -1.035465 1.082531 0.000000

关于ios - SceneKit取消投影点会产生奇怪的结果,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51464688/

10-16 10:02