zoukankan      html  css  js  c++  java
  • iOS11 AR场景中关于3D模型的触碰检测

          最新 iOS11中的AR特别火爆,自己也到网上找了几个Demo把玩了下,核心代码无非以下:

    //AR视图:展示3D界面

    @property(nonatomic,strong)ARSCNView *arSCNView;

    添加模型方法一:

      // Create a new scene

        SCNScene *scene = [SCNScene sceneNamed:@"art.scnassets/ship.scn"];

        

        // Set the scene to the view

        self.arSCNView.scene = scene; 

    添加模型方法二:

    //1.使用场景加载scn文件(scn格式文件是一个基于3D建模的文件,使用3DMax软件可以创建,这里系统有一个默认的3D飞机)--------在右侧我添加了许多3D模型,只需要替换文件名即可

        SCNScene *scene = [SCNScene sceneNamed:@"art.scnassets/ship.scn"];//@"Models.scnassets/chair/chair.scn"];

        //2.获取飞机节点(一个场景会有多个节点,此处我们只写,飞机节点则默认是场景子节点的第一个)

        //所有的场景有且只有一个根节点,其他所有节点都是根节点的子节点

        

        SCNNode *shipNode = scene.rootNode.childNodes[0];

        self.planeNode = shipNode;

        

        //飞机比较大,释放缩放一下并且调整位置让其在屏幕中间

        shipNode.scale = SCNVector3Make(0.5, 0.5, 0.5);

        shipNode.position = SCNVector3Make(0, -15,-15);

       ;

        //一个飞机的3D建模不是一气呵成的,可能会有很多个子节点拼接,所以里面的子节点也要一起改,否则上面的修改会无效

        for (SCNNode *node in shipNode.childNodes) {

            node.scale = SCNVector3Make(0.5, 0.5, 0.5);

            node.position = SCNVector3Make(0, -15,-15);

            

        }

         //3.将飞机节点添加到当前屏幕中

        [self.arSCNView.scene.rootNode addChildNode:shipNode];

    //环境搭建代码见文后 结尾分割线 下

    ========================开头分割线====================

      最初想到的当然是通过添加手势看能否凑效,但是如果向ARSCNView添加,有点儿盲目做功,因为ARSCNView上不全是所求的3D模型,不管有没有点击到模型,手势方法都会出发;再观察SCNScene, SCNScene继承自NSObject,观察了API又陷入了死胡同;返回来看ARSCNView有个方法:

    /*!

     @method hitTest:options:

     @abstract Returns an array of SCNHitTestResult for each node that contains a specified point.

     @param point A point in the coordinate system of the receiver.

     @param options Optional parameters (see the "Hit test options" group for the available options).

     */

    - (NSArray<SCNHitTestResult *> *)hitTest:(CGPoint)point options:(nullable NSDictionary<SCNHitTestOption, id> *)options;

          该方法会返回一个SCNHitTestResult数组,这个数组中每个元素的node都包含了指定的点(CGPoint)

    打个比方:ARSCNView就是个多层合成木板,手指的每次点击,都好像一根针穿透模板,该方法会反回由针穿过的所有点(node)组成的一饿数组,每个点其实都包含了你手指点击的位置(CGPoint),这样我们就可以通过便利每个数组中每个SCNHitTestResult的node,看哪个node有父node,并且找到node的name和3D模型的根节点name做对比,可以找到那就是点击到了3D模型;

    代码如下:

               SCNNode *vaseNode = scene.rootNode.childNodes[0];

                vaseNode.name = @"Virtual object root node";//很重要,就靠name做对比

                //4.设置花瓶节点的位置为捕捉到的平地的位置,如果不设置,则默认为原点位置,也就是相机位置

                vaseNode.position = SCNVector3Make(planeAnchor.center.x, 0, planeAnchor.center.z);

                //5.将花瓶节点添加到当前屏幕中

                //!!!此处一定要注意:花瓶节点是添加到代理捕捉到的节点中,而不是AR试图的根节点。因为捕捉到的平地锚点是一个本地坐标系,而不是世界坐标系

                [node addChildNode:vaseNode];

    // 系统方法

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

    {

        if (self.arType == ARTypePlane&&self.rootNode) {

            self.currentTouches = touches;

            UITouch *touch = [self.currentTouches anyObject];

             CGPoint tapPoint  = [touch locationInView:self.arSCNView];//该点就是手指的点击位置

            NSDictionary *hitTestOptions = [NSDictionary dictionaryWithObjectsAndKeys:@(true),SCNHitTestBoundingBoxOnlyKey, nil];

            NSArray<SCNHitTestResult *> * results= [self.arSCNView hitTest:tapPoint options:hitTestOptions];

            for (SCNHitTestResult *res in results) {//遍历所有的返回结果中的node

                if ([self isNodePartOfVirtualObject:res.node]) {

                    [self doSomeThing];

                    break;

                }

            }

        }   

    }

    //上溯找寻指定的node

    -(BOOL) isNodePartOfVirtualObject:(SCNNode*)node {

        if ([@"Virtual object root node" isEqualToString:node.name]) {

            return true;

        }

        if (node.parentNode != nil) {

            return [self isNodePartOfVirtualObject:node.parentNode];

        }    

        return false;

    }

    ===========结尾分割线==============

    #pragma mark -搭建ARKit环境

    //懒加载会话追踪配置

    - (ARSessionConfiguration *)arSessionConfiguration

    {

        if (_arSessionConfiguration != nil) {

            return _arSessionConfiguration;

        }

        

        //1.创建世界追踪会话配置(使用ARWorldTrackingSessionConfiguration效果更加好),需要A9芯片支持

        ARWorldTrackingSessionConfiguration *configuration = [[ARWorldTrackingSessionConfiguration alloc] init];

        //2.设置追踪方向(追踪平面,后面会用到)

        configuration.planeDetection = ARPlaneDetectionHorizontal;

        _arSessionConfiguration = configuration;

        //3.自适应灯光(相机从暗到强光快速过渡效果会平缓一些)

        _arSessionConfiguration.lightEstimationEnabled = YES;

        

        return _arSessionConfiguration;

        

    }

    //懒加载拍摄会话

    - (ARSession *)arSession

    {

        if(_arSession != nil)

        {

            return _arSession;

        }

        //1.创建会话

        _arSession = [[ARSession alloc] init];

        _arSession.delegate = self;

        //2返回会话

        return _arSession;

    }

    //创建AR视图

    - (ARSCNView *)arSCNView

    {

        if (_arSCNView != nil) {

            return _arSCNView;

        }

        //1.创建AR视图

        _arSCNView = [[ARSCNView alloc] initWithFrame:self.view.bounds];

        

        //2.设置代理  捕捉到平地会在代理回调中返回

        _arSCNView.delegate = self;

        

        //2.设置视图会话

        _arSCNView.session = self.arSession;

        //3.自动刷新灯光(3D游戏用到,此处可忽略)

        _arSCNView.automaticallyUpdatesLighting = YES;

        

        return _arSCNView;

    }

    - (void)viewDidAppear:(BOOL)animated

    {

        [super viewDidAppear:animated];

        

        //1.将AR视图添加到当前视图

        [self.view addSubview:self.arSCNView];

        //2.开启AR会话(此时相机开始工作)

        [self.arSession runWithConfiguration:self.arSessionConfiguration];

    }

  • 相关阅读:
    读图,特征提取——形状
    5.2 SW1控制LED1亮灭(中断功能)
    3、寄存器
    5.1、按键SW1控制LED1亮灭
    4.2、LED1、LED2交替闪烁
    2、编程工具IAR、烧写工具SmartRF的使用
    4.1、实现4个LED灯同时闪烁
    1、CC2530单片机介绍
    装windows系统教程
    连接夜神模拟器
  • 原文地址:https://www.cnblogs.com/shuizhuqing/p/7158016.html
Copyright © 2011-2022 走看看