philm-iOS-wiki
  • 介绍
  • 网络层
    • 说明
  • UI
    • 说明
    • 在ios7以前使用ColorSpace的坑
    • UITableView偏移异常问题
    • drawRect时单独设置文字阴影无效
    • Xcode9下相册访问权限问题
    • 避免同时点击多个Button
    • scroll上的button延迟响应问题
    • uibutton触发边界事件
    • ios 11 上tableview 改动
    • YYImage 显示指定区域的图片
  • 数据持久化
    • 说明
  • 其它
    • 取消延迟执行之坑
    • NSString 转换 float 的精度问题
  • 每周阅读
    • 目录
    • 深入思考NSNotification
    • gitBook使用小助手
    • iOS App签名的原理
    • 响应链
    • iOS10跳转系统到设置页
    • SDWebImage下载高清图内存问题
    • iOS圆角避免离屏渲染
    • 常用的延时调用
    • iOS 神经网络
    • SDWebImage缓存策略
    • 3Dtouch
    • 为什么 Objective-C 对象存储在堆上而不是栈上
    • 深入浅出理解视频编码H264结构
    • CATextLayer学习
    • cocoaPods
    • 任意网站支持RSS
    • Metal简介
    • 动态更改icon
    • CAReplicatorLayer
    • 增加点击间隔
    • 勒索病毒当道的时代
    • iOS常用宏定义
    • Metal实现YUV转RGB渲染视频
    • 获取当前下载的app及下载进度
    • OpenGL ES 三种类型修饰 uniform attribute varying
    • 技术部门引入OKR
    • 基于runloop的线程保活、销毁与通信
    • 深入理解哈希表
    • TOLL-FREE BRIDGING 和 UNMANAGED
    • 开发者能拿到的标识符
    • Swift自定义LOG
    • 系统通知整理
    • iOS 中的 imageIO 与 image 解码
    • CGImageRef基本介绍及方法说明
    • Swift 3.0 语法
    • webview加载部分网页
    • 在CAAnimation中暂停动画
    • 把代码迁移到协调器上
    • ios11API更新整理
    • 非越狱iOS设备的远程控制实现原理
    • 关于本地化
    • swift命名空间
    • CoreML与coremltools体验
    • 力学动画
    • Swift 4官方文档中文版: The Basic(上)
    • swift 中的KVO用法
    • GPUImage的图像形变设计(简单形变部分)
    • iOS响应式架构
    • 移动端图片上传旋转、压缩的解决方案
    • AVFoundation使用指南AVAssert使用
    • 过渡动画
    • 谈谈 MVX 中的 Model
    • AVFoundation编程-AVPlayer使用
    • GPUImage的图像形变设计(复杂形变部分)
    • What's New in LLVM 9
    • ios的事件机制
    • GPUImage源码解读(一)
    • GPUImage源码解读(二)
    • iOS 启动优化
    • 模块化 Swift 中的状态
    • swift中的let和var背后的编程模式
    • Swift Runtime动态性分析
    • RAC下的响应式编程
    • GPUImage源码解读(三)
    • 如何准确判断webView是否加载完成
    • NSObject的+load和+initialize详解
    • ios8以后设置启动图
    • GPUImage源码解读(四)
    • Swift自动闭包
    • IOS11新特性
    • GPUImage源码解读(五)
    • 理解 OC 内部的消息调用、消息转发、类和对象
    • 修饰符
    • IOS 切面统计事件解耦
    • GPUImage源码解读(六)
    • CoreImage介绍
    • 影响Core Animation性能的原因
    • Instruments中的动画工具选项介绍
    • GPUImage源码解读(七)
    • Xcode 7新的特性Lightweight Generics 轻量级泛型与__kindof修饰符
    • GPUImage源码解读(八)
    • Core Image之自定 Filter
    • iOS通用链接
    • 谈nonatomic非线程安全问题
    • 深拷贝与浅拷贝
    • CIKernel 介绍
    • iOS11适配
    • GPUImage源码解读(九)
    • CVPixelBufferCreate使用的坑
    • ios一窥并发底层
    • ARKit进阶:物理世界
    • ARKit的工作原理及流程介绍
    • UI线程卡顿监控
    • FBKVOController使用
    • GPUImage源码解读(十)
    • WKWebView在ios11崩溃问题解决方法
    • 微信iOS SQLite源码优化实践
    • HEIF 和 HEVC 研究
    • 谈谈 iOS 中图片的解压缩
    • 提升 iOS 开发效率! Xcode 9 内置模拟器的9个技巧
    • ObjC和JavaScript的交互,在恰当的时机注入对象
    • iOS数据保护
    • iOS11中网络层的一些变化(Session707&709脱水版)
    • GPUImage源码解读(十一)
    • 一种避免 iOS 内存碎片的方法
    • pods的原理
    • GPUImage源码解读(十二)
    • GPUImage源码解读(十三)
    • iOS 11 Layout的新特性
    • iOS应用瘦身方法思路整理
    • GPUImage源码解读(十四)
    • CAEmitterLayer属性介绍
    • 浅析移动蜂窝网络的特点及其省电方案
    • 如何在 table view 中添加 3D Touch Peek & Pop 功能
    • iOS中锁的介绍与使用
    • NSLog效率低下的原因及尝试lldb断点打印Log
    • GPUImage源码解读(十五)
    • GPUImage源码解读(十六)
    • CADisplayLink
    • GPUImage源码解读(十七)
    • CADisplayLink
    • 老生常谈category增加属性的几种操作
    • 30行代码演示dispatch_once死锁
    • GPUImage源码解读(十八)
    • YYImage设计思路
    • GPUImage源码解读(十九)
    • 深入理解Tagged Pointer
    • iOS 11:WKWebView内容过滤规则详解
    • Swift语法对编译速度的影响
    • GPUImage源码解读(二十)
    • GPUImage源码解读(二十一)
    • iOS App间常用的五种通信方式
    • YYCache深入学习
    • 冲顶大会插件
    • iOS高性能图片架构与设计
    • YUV颜色编码解析
    • iOS传感器:App前后台切换后,获取敏感信息使用touch ID进行校验
    • GPUImage源码解读(二十二)
    • GPUImage源码解读(二十三)
    • 从零开始的机器学习 - Machine Learning(一)
    • 从零开始的机器学习 - Machine Learning(二)
    • GPUImage源码解读(二十四)
    • Objective-C消息转发机制
    • iOS 程序 main 函数之前发生了什么
    • MMKV--基于 mmap 的 iOS 高性能通用 key-value 组件
    • Objective-C 消息发送与转发机制原理
    • 谈Objective-C block的实现
    • GPUImage源码解读(二十五)
    • podfile语法
    • 轻量级低风险 iOS 热更新方案
    • 使用objection来模块化开发iOS项目
    • swift 中delegate的使用注意
    • 使用appledoc自动生成api文档
    • UITextChecker的使用
    • ARKit 如何给SCNNode贴Gif图片
    • Unity与iOS平台交互和原生插件开发
    • SceneKit编程珠玑
Powered by GitBook
On this page
  • 写在前面
  • Demo
  • 关于物理模拟
  • SCNPhysicsWorld
  • SCNPhysicsBody
  • physicsShape:
  • body category
  • SCNPhysicsShape
  • 物理模拟与其他动画的冲突
  • process of collision
  • collision detection
  • collision determination
  • collision respond
  • contact test
  • scene test
  • AR scene test
  • physics body test
  • contact test
  • 最后
  1. 每周阅读

ARKit进阶:物理世界

Previousios一窥并发底层NextARKit的工作原理及流程介绍

Last updated 7 years ago

写在前面

ARKit的渲染能力是由其他框架实现的,除了苹果的SceneKit, Unity3D、UE, 或者其他自定义的OpenGL、Metal渲染引擎都可以与ARKit相结合。本文所介绍的技术都是基于SceneKit。

Demo

这个Demo使用苹果的demo改的,。

关于物理模拟

虽然物理引擎都具有真实的物理变量,如质量、重力、摩擦力等,但当我们说道物理模拟,不是要真的去用真实世界的数值去模拟物理行为,事实上那样反而会失真。我们要做的是维护好各种变量的相对关系,制造一种真实的物理感官即可。

SCNPhysicsWorld

游戏中的物理引擎用来模拟3D世界中的物理特效,使物体具备的真实的动态行为。SceneKit使用SCNPhysicsWorld来管理这种物理模拟,让物体的碰撞、连接、掉落等具有真实感。 ARSCNScene具有继承自SCNScene的默认SCNPhysicsWorld。任何添加到ARSCNScene的物理对象,都会注册到SCNPhysicsWorld中,维护其中的物理关系是重点。 利用SCNPhysicsWorld,我们主要做以下工作:

  • 管理全局的物理变量。

  • 利用其代理方法观察物理行为。

  • 使用contact/ray/convex test方法,检测物理之间的物理关系。

SCNPhysicsBody

想要一个SCNNode参与到物理模拟中,只需要给node.physicsBody赋值一个合适的值。 所有拥有physics body的node,会在render loop的physics simulation阶段,计算该node的物理行为,在接下来的渲染阶段对node做相应的变换。

一个合适的SCNPhysicsBody需要合理设置其type与physicsShape。 type:

  • dynamic: 可以被碰撞、力影响。适合场景中物理引擎可以完全接管的类型,如掉落的石块。

  • static: 不受碰撞、力影响,且不能移动。适合场景中地面、墙体等。

  • kinematic: 不受碰撞、力影响,但移动的时候会影响其他body。适合场景中的角色,毕竟我们不想角色的移动不想被太多力影响。

physicsShape:

当physics body参与到物理模拟时,一个更贴合的形状能得到一个更令人满意的结果。但是对于一个比较复杂的几何体,简单的convex会显得过大,concave又会太复杂影响性能。这种情况可以使用若干个简单的形状拼装一个相似的形状,或者由设计给出一个合理的形状,总之形状的选择要平衡性能与真实感。

body category

一个场景中会有许多node,需要给他们设置category,让我们只关注感兴趣的碰撞、接触。尤其要注意的是它们各自的默认值,不然很容易出现bug。

categoryBitMask:

指定body的类型, dynamic/kinematic body默认为1,static body默认为2。

collisionBitMask:

指定能与该body产生碰撞的physics body类型。默认是-1,即每位都置1。

contactTestBitMask:

指定哪种类型的physics body与该body发生接触(几何体交叉)后,通知给physics world。

这个属性在OSX10.11和iOS9以上默认值是0,以下与collisionBitMask相同。

记住重设physics body时,要恢复这些值

注意SCNNode也有一个categoryBitMask,用法与这个类似。但在scene test时,这两个容易搞混。这里吐槽以下苹果的命名。

SCNPhysicsShape

当物理引擎检测碰撞时,使用的是SCNPhysicsShape来计算结果,除了性能,我碰到两个关于physicsShape的问题:

  1. 如果node.geometry是不可见的,那个虽然它有physics shape,在调试时也会显示,但不会参与物理模拟。

  2. SceneKit的物理引擎是不支持缩放变换的。如果一个node做缩放变换后,physics body将仍是原来的尺寸。这种情况看我的回答,重点是当attach body之前如果没有指定形状,那么SceneKit才会使用scale信息,使用SCNPhysicsShapeScaleKey也有一样的效果。

    //still has identity scale
    SCNPhysicsBody *body = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeKinematic shape:[SCNPhysicsShape shapeWithGeometry:ramp.geometry options:nil]];
    //this did worked
    SCNPhysicsBody *body = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeKinematic                                                     shape:nil];

SceneKit automatically creates a physics shape for the body when you attach it to a node, based on that node’s geometry property

物理模拟与其他动画的冲突

从SceneKit的render loop可以看到,物理模拟实际上也是一种动画,只不过动画的参数由物理引擎控制。SceneKit也遵循iOS的传统,具有隐式、显式动画,同时有SCNAction接口。由于物理引擎 是将所有的计算结果应用到动画层上,即node.presentationNode,这会让新加入的动画显得不正常。因为其他动画的初始值是从node.transform中读取的。对于这种问题,需要读出node.presentationNode.transform的值用于动画的初始值。

process of collision

对于简单的碰撞,只要设置好physics body和category bit mask,collision bit mask等参数,其他的就由物理引擎接管了。 碰撞的处理过程由3个部分组成。

collision detection

物理引擎会在渲染时检测物体之间的physics body是否发生重叠,这一过程我们可以通过中的方法观察。

collision determination

与操作两个物体的之间的categoryBitMask和collisionBitMask,若返回非0,则发生碰撞。

collision respond

物理引擎会在渲染之前,计算物理碰撞的结果并应用到物体上。

contact test

scene test

SceneKit与ARKit中共有以下几种scene test,用以观察世界中的物体关系,作用类似UIKit的 hitTest: 方法。

AR scene test

//ARSCNView
- (NSArray<ARHitTestResult *> *)hitTest:(CGPoint)point 
                                  types:(ARHitTestResultType)types;

根据ARSCNView中的点,构造一条3D世界的射线,搜索ARAnchor或真实物体(特征点或已检测出的平面)。 scene test

//SCNSceneRenderer

  • (NSArray *)hitTest:(CGPoint)point

                               options:(NSDictionary<SCNHitTestOption, id> 

//SCNNode

  • (NSArray *)hitTestWithSegmentFromPoint:(SCNVector3)pointA

                                                   toPoint:(SCNVector3)pointB 
                                                   options:(NSDictionary<NSString *,id> *)options;

第一个方法:根据SCNSceneRenderer(SCNView等)中的点,构造一条3D世界的射线,搜索与射线相交的几何体,node.geometry为nil则忽视。 第二个方法:在目标node的局部空间中,搜索与pointA-pointB线段相交的子node。

physics body test

//SCNPhysicsBody
- (NSArray<SCNHitTestResult *> *)rayTestWithSegmentFromPoint:(SCNVector3)origin 
                                                     toPoint:(SCNVector3)dest 
                                                     options:(NSDictionary<SCNPhysicsTestOption, id> *)options;
- (NSArray<SCNPhysicsContact *> *)convexSweepTestWithShape:(SCNPhysicsShape *)shape 
                                             fromTransform:(SCNMatrix4)from 
                                               toTransform:(SCNMatrix4)to 
                                                   options:(NSDictionary<SCNPhysicsTestOption, id> *)options;

第一个方法:在物理世界中,返回在两点之间的physics body所属的node。 第二个方法:在物理世界中,按form-to变换滑动指定的形状,返回相交的physics body所属的node。

contact test

//contact test
- (NSArray<SCNPhysicsContact *> *)contactTestBetweenBody:(SCNPhysicsBody *)bodyA 
                                                 andBody:(SCNPhysicsBody *)bodyB 
                                                 options:(NSDictionary<SCNPhysicsTestOption, id> *)options;
- (NSArray<SCNPhysicsContact *> *)contactTestWithBody:(SCNPhysicsBody *)body 
                                              options:(NSDictionary<SCNPhysicsTestOption, id> *)options;

第一个方法:检测物理世界中,两个body是否发生接触,返回所有的接触点。 第二个方法:返回所有在物理世界中与指定body发生contact的node。

最后

物理引擎能够帮助我们模拟真实世界的效果,虽然高级的特效一般都是自己在渲染循环中实现的,但它大大减轻了我们计算成本。拥有良好的物理特效,能够让用户有真实的感受,希望本篇文章能够帮助大家。

当有两个物体相接触,若categoryBitMask和contactTestBitMask相与不为零,那么会调用的方法。很显然这个结果的集合是小于碰撞结果的。通过这个方法,我们能够控制两个物体之间的碰撞,这在物理引擎接管的碰撞动画不理想时,是非常有用的。 当接触发生时,代理方法会传来SCNPhysicsContact对象,它包含了接触的对象、部位、法线与重叠距离。通过它可以修正错误的动画。例如我将一个石块从高处坠落,如果速度特别大,那么它会直接穿过底部的平面。因为在render loop的渲染时,两者相接触的那一帧在物理模拟时,石块已经大部分穿过了平面,这样在下一帧石块会直接穿过去,而不是回弹。可以看。

我的回答
视频链接