相關(guān)
到底有多強器躏?蘋果的增強現(xiàn)實框架:ARKit
ARKit進階:材質(zhì)
ARKit實戰(zhàn):如何實現(xiàn)任意門
寫在前面
ARKit的渲染能力是由其他框架實現(xiàn)的箩兽,除了蘋果的SceneKit, Unity3D、UE, 或者其他自定義的OpenGL、Metal渲染引擎都可以與ARKit相結(jié)合。本文所介紹的技術(shù)都是基于SceneKit。
Demo
這個Demo使用蘋果的demo改的湖苞,視頻鏈接。
關(guān)于物理模擬
雖然物理引擎都具有真實的物理變量详囤,如質(zhì)量财骨、重力、摩擦力等,但當(dāng)我們說道物理模擬隆箩,不是要真的去用真實世界的數(shù)值去模擬物理行為该贾,事實上那樣反而會失真。我們要做的是維護好各種變量的相對關(guān)系摘仅,制造一種真實的物理感官即可靶庙。
SCNPhysicsWorld
游戲中的物理引擎用來模擬3D世界中的物理特效,使物體具備的真實的動態(tài)行為娃属。SceneKit使用SCNPhysicsWorld
來管理這種物理模擬六荒,讓物體的碰撞、連接矾端、掉落等具有真實感掏击。
ARSCNScene
具有繼承自SCNScene
的默認SCNPhysicsWorld
。任何添加到ARSCNScene
的物理對象秩铆,都會注冊到SCNPhysicsWorld
中砚亭,維護其中的物理關(guān)系是重點。
利用SCNPhysicsWorld
殴玛,我們主要做以下工作:
- 管理全局的物理變量捅膘。
- 利用其代理方法觀察物理行為。
- 使用contact/ray/convex test方法滚粟,檢測物理之間的物理關(guān)系寻仗。
SCNPhysicsBody
想要一個SCNNode
參與到物理模擬中,只需要給node.physicsBody
賦值一個合適的值凡壤。
所有擁有physics body的node署尤,會在render loop的physics simulation階段,計算該node的物理行為亚侠,在接下來的渲染階段對node做相應(yīng)的變換曹体。
一個合適的
SCNPhysicsBody
需要合理設(shè)置其type
與physicsShape
。type:
- dynamic: 可以被碰撞硝烂、力影響箕别。適合場景中物理引擎可以完全接管的類型,如掉落的石塊钢坦。
- static: 不受碰撞究孕、力影響,且不能移動爹凹。適合場景中地面厨诸、墻體等。
- kinematic: 不受碰撞禾酱、力影響微酬,但移動的時候會影響其他body绘趋。適合場景中的角色,畢竟我們不想角色的移動不想被太多力影響颗管。
physicsShape:
當(dāng)physics body參與到物理模擬時陷遮,一個更貼合的形狀能得到一個更令人滿意的結(jié)果。但是對于一個比較復(fù)雜的幾何體垦江,簡單的convex會顯得過大帽馋,concave又會太復(fù)雜影響性能。這種情況可以使用若干個簡單的形狀拼裝一個相似的形狀比吭,或者由設(shè)計給出一個合理的形狀绽族,總之形狀的選擇要平衡性能與真實感。
body category
一個場景中會有許多node衩藤,需要給他們設(shè)置category吧慢,讓我們只關(guān)注感興趣的碰撞、接觸赏表。尤其要注意的是它們各自的默認值检诗,不然很容易出現(xiàn)bug。
categoryBitMask:
指定body的類型瓢剿, dynamic/kinematic body默認為1逢慌,static body默認為2。
collisionBitMask:
指定能與該body產(chǎn)生碰撞的physics body類型间狂。默認是-1涕癣,即每位都置1。
contactTestBitMask:
指定哪種類型的physics body與該body發(fā)生接觸(幾何體交叉)后前标,通知給physics world。
這個屬性在OSX10.11和iOS9以上默認值是0距潘,以下與collisionBitMask相同炼列。
記住重設(shè)physics body時,要恢復(fù)這些值
注意SCNNode
也有一個categoryBitMask
音比,用法與這個類似俭尖。但在scene test時,這兩個容易搞混洞翩。這里吐槽以下蘋果的命名稽犁。
SCNPhysicsShape
當(dāng)物理引擎檢測碰撞時,使用的是SCNPhysicsShape
來計算結(jié)果骚亿,除了性能已亥,我碰到兩個關(guān)于physicsShape
的問題:
- 如果
node.geometry
是不可見的,那個雖然它有physics shape来屠,在調(diào)試時也會顯示虑椎,但不會參與物理模擬震鹉。 - SceneKit的物理引擎是不支持縮放變換的。如果一個node做縮放變換后捆姜,physics body將仍是原來的尺寸传趾。這種情況看我的回答,重點是當(dāng)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可以看到珊豹,物理模擬實際上也是一種動畫簸呈,只不過動畫的參數(shù)由物理引擎控制。SceneKit也遵循iOS的傳統(tǒng)平夜,具有隱式蝶棋、顯式動畫,同時有SCNAction
接口忽妒。由于物理引擎
是將所有的計算結(jié)果應(yīng)用到動畫層上玩裙,即node.presentationNode
,這會讓新加入的動畫顯得不正常段直。因為其他動畫的初始值是從node.transform
中讀取的吃溅。對于這種問題,需要讀出node.presentationNode.transform
的值用于動畫的初始值鸯檬。
process of collision
對于簡單的碰撞决侈,只要設(shè)置好physics body和category bit mask,collision bit mask等參數(shù)喧务,其他的就由物理引擎接管了赖歌。
碰撞的處理過程由3個部分組成。
collision detection
物理引擎會在渲染時檢測物體之間的physics body是否發(fā)生重疊功茴,這一過程我們可以通過<SCNPhysicsContactDelegate>中的方法觀察庐冯。
collision determination
與操作兩個物體的之間的categoryBitMask和collisionBitMask,若返回非0坎穿,則發(fā)生碰撞展父。
collision respond
物理引擎會在渲染之前,計算物理碰撞的結(jié)果并應(yīng)用到物體上玲昧。
contact test
當(dāng)有兩個物體相接觸栖茉,若categoryBitMask
和contactTestBitMask
相與不為零,那么會調(diào)用<SCNPhysicsContactDelegate>的方法孵延。很顯然這個結(jié)果的集合是小于碰撞結(jié)果的吕漂。通過這個方法,我們能夠控制兩個物體之間的碰撞隙袁,這在物理引擎接管的碰撞動畫不理想時痰娱,是非常有用的弃榨。
當(dāng)接觸發(fā)生時,代理方法會傳來SCNPhysicsContact
對象梨睁,它包含了接觸的對象鲸睛、部位、法線與重疊距離坡贺。通過它可以修正錯誤的動畫官辈。例如我將一個石塊從高處墜落,如果速度特別大遍坟,那么它會直接穿過底部的平面拳亿。因為在render loop的渲染時,兩者相接觸的那一幀在物理模擬時愿伴,石塊已經(jīng)大部分穿過了平面肺魁,這樣在下一幀石塊會直接穿過去,而不是回彈隔节《炀可以看我的回答。
scene test
SceneKit與ARKit中共有以下幾種scene test怎诫,用以觀察世界中的物體關(guān)系瘾晃,作用類似UIKit的 hitTest: 方法。
AR scene test
//ARSCNView
- (NSArray<ARHitTestResult *> *)hitTest:(CGPoint)point
types:(ARHitTestResultType)types;
根據(jù)ARSCNView中的點幻妓,構(gòu)造一條3D世界的射線蹦误,搜索ARAnchor
或真實物體(特征點或已檢測出的平面)。
scene test
//SCNSceneRenderer
- (NSArray<SCNHitTestResult *> *)hitTest:(CGPoint)point
options:(NSDictionary<SCNHitTestOption, id>
//SCNNode
- (NSArray<SCNHitTestResult *> *)hitTestWithSegmentFromPoint:(SCNVector3)pointA
toPoint:(SCNVector3)pointB
options:(NSDictionary<NSString *,id> *)options;
第一個方法:根據(jù)SCNSceneRenderer(SCNView等)中的點肉津,構(gòu)造一條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是否發(fā)生接觸吗铐,返回所有的接觸點。
第二個方法:返回所有在物理世界中與指定body發(fā)生contact的node杏节。
最后
物理引擎能夠幫助我們模擬真實世界的效果唬渗,雖然高級的特效一般都是自己在渲染循環(huán)中實現(xiàn)的典阵,但它大大減輕了我們計算成本。擁有良好的物理特效镊逝,能夠讓用戶有真實的感受壮啊,希望本篇文章能夠幫助大家。