立体 ARSCNview 使 VR 和 AR 混合

Stereo ARSCNview to make VR and AR mix

我想混合 virtual realityaugmented reality。 目标是我有一个立体相机(每只眼睛)。

我试图将两个 ARSCNView 放在一个 viewCotnroller 中,但 ARKit 似乎同时只启用一个 ARWorldTrackingSessionConfiguration。我怎样才能做到这一点?

我研究过复制一个视图的图形表示以将其传递到另一个视图,但无法找到。请帮助我找到解决方案。

我找到了这个link,也许它可以启发我们:

这是我的问题示例:

https://www.youtube.com/watch?v=d6LOqNnYm5s

PS:不像我之前的post,评论为什么!

ARSession documentation 表示 ARSession 是一个共享对象。

Every AR experience built with ARKit requires a single ARSession object. If you use an ARSCNView or ARSKView object to easily build the visual part of your AR experience, the view object includes an ARSession instance. If you build your own renderer for AR content, you'll need to instantiate and maintain an ARSession object yourself.

所以最后一句话中有一个线索。而不是两个 ARSCNView 实例,使用 SCNView 并在它们之间共享单个 ARSession

我希望这是一个常见的用例,因此值得提交 Radar 请求立体声支持。

现在怎么办?

(单例)会话只有一个委托。您需要两个不同的委托实例,每个视图一个。您可以使用一个将委托消息发送到每个视图的对象来解决这个问题;可解决,但需要一些额外的工作。

还有一个问题是需要两个稍微不同的相机位置,每只眼睛一个,用于立体视觉。 ARKit 使用一个摄像头,放置在 iOS 设备的位置,因此您必须对其进行模糊测试。

然后你必须处理每只眼睛的不同桶形失真。

这对我来说,就是编写我自己的自定义对象来拦截 ARKit 委托消息,将坐标转换为我从两个不同相机看到的坐标,并管理两个不同的 SCNView(不是 ARSCNView)。或者可能使用一个 ARSCNView(一只眼睛),拦截其帧更新,并将这些帧传递给 SCNView(另一只眼睛)。

提交雷达,post 号码,我会欺骗它。

下面的代码基本上就是哈尔说的。我之前在 github 上写了几行,可能可以帮助您入门。 (简单的代码,没有桶形失真,没有针对狭窄的 FOV 进行调整 - 还没有)。

本质上,我们将同一个场景连接到第二个 ARSCNView(因此两个 ARSCNView 都看到同一个场景)。无需让 ARWorldTrackingSessionConfiguration 使用 2 个 ARSCNView。然后,我们偏移它的 pointOfView,使其定位为第二只眼。

https://github.com/hanleyweng/iOS-Stereoscopic-ARKit-Template

Objective-C Han 的 github 代码版本,以编程方式创建的场景视图,y + z 位置未更新 - 所有归功于 Han:

-(void)setup{

    //left
    leftSceneView = [ARSCNView new];
    leftSceneView.frame = CGRectMake(0, 0, w, h/2);
    leftSceneView.delegate = self;
    leftSceneView.autoenablesDefaultLighting = true;
    [self.view addSubview:leftSceneView];

    //right
    rightSceneView = [ARSCNView new];
    rightSceneView.frame = CGRectMake(0, h/2, w, h/2);
    rightSceneView.playing = true;
    rightSceneView.autoenablesDefaultLighting = true;
    [self.view addSubview:rightSceneView];

    //scene
    SCNScene * scene = [SCNScene new];
    leftSceneView.scene = scene;
    rightSceneView.scene = scene;

    //tracking
    ARWorldTrackingConfiguration * configuration = [ARWorldTrackingConfiguration new];
    configuration.planeDetection = ARPlaneDetectionHorizontal;
    [leftSceneView.session runWithConfiguration:configuration];
}

-(void)renderer:(id<SCNSceneRenderer>)renderer updateAtTime:(NSTimeInterval)time {

    dispatch_async(dispatch_get_main_queue(), ^{

        //update right eye
        SCNNode * pov = self->leftSceneView.pointOfView.clone;

        SCNQuaternion orientation = pov.orientation;
        GLKQuaternion orientationQuaternion = GLKQuaternionMake(orientation.x, orientation.y, orientation.z, orientation.w);
        GLKVector3 eyePosition = GLKVector3Make(1, 0, 0);
        GLKVector3 rotatedEyePosition = GLKQuaternionRotateVector3(orientationQuaternion, eyePosition);
        SCNVector3 rotatedEyePositionSCNV = SCNVector3Make(rotatedEyePosition.x, rotatedEyePosition.y, rotatedEyePosition.z);

        float mag = 0.066f;
        float rotatedX = pov.position.x + rotatedEyePositionSCNV.x * mag;
        float rotatedY = pov.position.y;// + rotatedEyePositionSCNV.y * mag;
        float rotatedZ = pov.position.z;// + rotatedEyePositionSCNV.z * mag;
        [pov setPosition:SCNVector3Make(rotatedX, rotatedY, rotatedZ)];

        self->rightSceneView.pointOfView = pov;
    });

}

为此,请使用以下代码:

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet weak var sceneView: ARSCNView!
    @IBOutlet weak var sceneView2: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()

        sceneView.delegate = self
        sceneView.showsStatistics = true
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        sceneView.scene = scene
        sceneView.isPlaying = true

        // SceneView2 Setup
        sceneView2.scene = scene
        sceneView2.showsStatistics = sceneView.showsStatistics

        // Now sceneView2 starts receiving updates
        sceneView2.isPlaying = true     
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }
}

并且不要忘记为 ARSCNViews.

激活 .isPlaying 实例属性