前言
對(duì)ARKit感興趣的同學(xué)校哎,可以訂閱ARKit教程專題
源代碼地址在這里
正文
這一章诡宗,我們會(huì)想辦法創(chuàng)建一個(gè)逼真的房子:有墻友地板還有天花板的那種滔蝉。
相關(guān)概念介紹
SceneKit坐標(biāo)系統(tǒng)
SceneKit可用于向視圖添加虛擬3D對(duì)象。 SceneKit內(nèi)容視圖由節(jié)點(diǎn)的分層樹結(jié)構(gòu)組成塔沃,也稱為場(chǎng)景圖蝠引。一個(gè)場(chǎng)景由根節(jié)點(diǎn)和其他節(jié)點(diǎn)組成,根節(jié)點(diǎn)定義場(chǎng)景世界的坐標(biāo)空間蛀柴,其他節(jié)點(diǎn)用可見內(nèi)容填充世界螃概。在屏幕上呈現(xiàn)的每個(gè)節(jié)點(diǎn)或3D對(duì)象都是SCNNode類型的對(duì)象。 SCNNode對(duì)象定義了相對(duì)于其父節(jié)點(diǎn)的坐標(biāo)空間變換(位置鸽疾,方向和比例)吊洼。其實(shí)它本身沒有任何可見內(nèi)容。
場(chǎng)景中的rootNode對(duì)象定義了SceneKit渲染的世界的坐標(biāo)系制肮。添加到此根節(jié)點(diǎn)的每個(gè)子節(jié)點(diǎn)都會(huì)創(chuàng)建自己的坐標(biāo)系冒窍,而該坐標(biāo)系又由其自己的子節(jié)點(diǎn)繼承。
SceneKit使用右手坐標(biāo)系豺鼻,其中(默認(rèn)情況下)視圖方向沿負(fù)Z軸综液,如下圖所示:
SCNNode對(duì)象的位置是使用SCNVector3定義的,SCNVector3將其定位在其父級(jí)的坐標(biāo)系內(nèi)儒飒。默認(rèn)位置是零向量谬莹,表示該節(jié)點(diǎn)位于父節(jié)點(diǎn)坐標(biāo)系的原點(diǎn)。在這種情況下,SCNVector3是一個(gè)三分量向量附帽,其中每個(gè)分量都是一個(gè)Float類型的數(shù)值埠戳,表示每個(gè)軸上的坐標(biāo)。
SCNNode對(duì)象的方向 - 表示為俯仰士葫,偏航和滾轉(zhuǎn)角度 - 由其eulerAngles屬性定義乞而。這也由SCNVector3結(jié)構(gòu)表示送悔,其中每個(gè)矢量分量是以弧度表示的角度慢显。
紋理
SCNNode對(duì)象本身沒有任何可見內(nèi)容。通過將SCNGeometry對(duì)象附加到節(jié)點(diǎn)欠啤,可以將2D和3D對(duì)象添加到場(chǎng)景中荚藻。幾何圖形附加了確定其外觀的SCNMaterial對(duì)象。
SCNMaterial具有多個(gè)可視屬性洁段。每個(gè)可視屬性都是SCNMaterialProperty類的一個(gè)實(shí)例应狱,它提供純色,紋理或其他2D內(nèi)容祠丝〖采耄基本著色,基于物理的著色以及可用于使材質(zhì)看起來更逼真的特殊效果有多種視覺屬性写半。
使用SceneKit岸蜗,還可以使用附加了SCNLight對(duì)象的節(jié)點(diǎn)來遮擋場(chǎng)景中具有光照和陰影效果的幾何體。
創(chuàng)建門戶
讓我們直接創(chuàng)建門戶的內(nèi)容叠蝇。打開SCNNodeHelpers.swift文件并添加以下內(nèi)容璃岳。
let surfaceLength: CGFloat = 3.0
let surfaceHeight: CGFloat = 0.1
let surfaceWidth: CGFloat = 3.0
let scaleX: Float = 2.0
let scaleY: Float = 2.0
let wallWidth: CGFloat = 0.1
let wallHeight: CGFloat = 3.0
let wallLength: CGFloat = 3.0
上面的代碼作用如下:
- 1: 我們可以定義門戶的樓層和天花板的尺寸。屋頂和天花板的高度對(duì)應(yīng)于厚度悔捶。
- 2: 這些是在表面上縮放和重復(fù)紋理的常量铃慷。
- 3:這些定義了墻壁節(jié)點(diǎn)的寬度,高度和長度蜕该。
之后我們?cè)偬砑尤缦麓a:
func repeatTextures(geometry: SCNGeometry, scaleX: Float, scaleY: Float){
let firstMaterial = geometry.firstMaterial
let modeRepeat = SCNWrapMode.repeat
firstMaterial?.diffuse.wrapS = modeRepeat
firstMaterial?.selfIllumination.wrapS = modeRepeat
firstMaterial?.normal.wrapS = modeRepeat
firstMaterial?.specular.wrapS = modeRepeat
firstMaterial?.emission.wrapS = modeRepeat
firstMaterial?.roughness.wrapS = modeRepeat
firstMaterial?.diffuse.wrapT = modeRepeat
firstMaterial?.selfIllumination.wrapT = modeRepeat
firstMaterial?.normal.wrapT = modeRepeat
firstMaterial?.specular.wrapT = modeRepeat
firstMaterial?.emission.wrapT = modeRepeat
firstMaterial?.roughness.wrapT = modeRepeat
firstMaterial?.diffuse.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
firstMaterial?.selfIllumination.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
firstMaterial?.normal.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
firstMaterial?.specular.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
firstMaterial?.emission.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
firstMaterial?.roughness.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
}
這定義了一種在X和Y維度上在表面上重復(fù)紋理圖像的方法犁柜。
上面的代碼作用如下:
- 1: 該方法采用SCNGeometry對(duì)象以及X和Y縮放因子作為輸入?yún)?shù)。紋理映射使用S和T坐標(biāo)系統(tǒng)堂淡,這種坐標(biāo)系統(tǒng)只是一種命名協(xié)議而已:S對(duì)應(yīng)于X赁温,T對(duì)應(yīng)于Y。在這里淤齐,可以將S維度的包裝模式定義為SCNWrapMode.repeat股囊,用于材料的所有可視屬性。
- 2: 我們可以將T維度的包裝模式定義為SCNWrapMode.repeat以及所有可視屬性更啄。對(duì)于重復(fù)模式稚疹,紋理采樣僅使用紋理坐標(biāo)的小數(shù)部分。
- 3: 這里,每個(gè)視覺屬性contentsTransform被設(shè)置為由SIGMatrix4結(jié)構(gòu)描述内狗。我們可以將X和Y縮放系數(shù)分別設(shè)置為scaleX和scaleY怪嫌。
我們只想在用戶進(jìn)入門戶時(shí)顯示floor(地板)和ceiling(天花板)節(jié)點(diǎn);任何其他時(shí)間,你需要隱藏它們柳沙。要實(shí)現(xiàn)此功能岩灭,需要在SCNNodeHelpers文件中添加如下代碼:
func makeOuterSurfaceNode(width: CGFloat, height: CGFloat, length: CGFloat) -> SCNNode{
let outerSurface = SCNBox(width: surfaceWidth, height: surfaceHeight, length: surfaceLength, chamferRadius: 0)
outerSurface.firstMaterial?.diffuse.contents = UIColor.white
outerSurface.firstMaterial?.transparency = 0.000001
let outerSurfaceNode = SCNNode(geometry: outerSurface)
outerSurfaceNode.renderingOrder = 10
return outerSurfaceNode
}
上面的代碼作用如下:
- 1: 使用floor(地板)和ceiling(天花板)的尺寸創(chuàng)建outerSurface場(chǎng)景框幾何對(duì)象。
- 2: 將可見內(nèi)容添加到框?qū)ο蟮穆瓷鋵傩灾新咐穑@樣方便渲染噪径。將透明度設(shè)置為較低的值,因此對(duì)象將從視圖中隱藏数初。
- 3: 從outerSurface幾何體創(chuàng)建SCNNode對(duì)象找爱。將節(jié)點(diǎn)的renderingOrder設(shè)置為10。最后渲染具有較大渲染順序的節(jié)點(diǎn)泡孩。為了使天花板和窗戶從門戶外部看不見车摄,需要將使內(nèi)部天花板和地面節(jié)點(diǎn)的渲染順序遠(yuǎn)大于10。
創(chuàng)建地板節(jié)點(diǎn)的代碼如下:
func makeFloorNode() -> SCNNode{
let outerFloorNode = makeOuterSurfaceNode(width: surfaceWidth, height: surfaceHeight, length: surfaceLength)
outerFloorNode.position = SCNVector3(surfaceHeight * 0.5, -surfaceHeight, 0)
let floorNode = SCNNode()
floorNode.addChildNode(outerFloorNode)
let innerFloor = SCNBox(width: surfaceWidth, height: surfaceHeight, length: surfaceLength, chamferRadius: 0)
innerFloor.firstMaterial?.lightingModel = .physicallyBased
innerFloor.firstMaterial?.diffuse.contents = UIImage(named: "ARResource.scnassets/floor/textures/FloorDiffuse.png")
innerFloor.firstMaterial?.normal.contents = UIImage(named: "ARResource.scnassets/floor/textures/FloorNormal.png")
innerFloor.firstMaterial?.roughness.contents = UIImage(named: "ARResource.scnassets/floor/textures/FloorRoughness.png")
innerFloor.firstMaterial?.specular.contents = UIImage(named: "ARResource.scnassets/floor/textures/FloorSpecular.png")
innerFloor.firstMaterial?.selfIllumination.contents = UIImage(named: "ARResource.scnassets/floor/textures/FloorGloss.png")
repeatTextures(geometry: innerFloor, scaleX: scaleX, scaleY: scaleY)
let innerFloorNode = SCNNode(geometry: innerFloor)
innerFloorNode.renderingOrder = 100
innerFloorNode.position = SCNVector3(surfaceHeight * 0.5, 0, 0)
floorNode.addChildNode(innerFloorNode)
return floorNode
}
上面的代碼作用如下:
- 1:使用floor的尺寸創(chuàng)建floor節(jié)點(diǎn)的下側(cè)仑鸥。
- 2: 定位outerFloorNode吮播,使其布置在floor節(jié)點(diǎn)的底部。將節(jié)點(diǎn)添加到floorNode眼俊,該節(jié)點(diǎn)包含floor的內(nèi)表面和外表面意狠。
- 3: 使用先前為每個(gè)維度聲明的常量初始化的SCNBox對(duì)象來創(chuàng)建面板的幾何體。
- 4: 地板材料的lightingModel設(shè)置為physicalBased泵琳。這種類型的陰影包含了物理燈光和材料的逼真抽象摄职。使用ARResource.scnassets目錄中的紋理圖像設(shè)置材質(zhì)的各種視覺屬性的內(nèi)容。
- 5: 使用之前定義的repeatTextures()在X和Y維度上重復(fù)材質(zhì)的紋理获列。
- 6: 使用innerFloor幾何對(duì)象為floor創(chuàng)建節(jié)點(diǎn)谷市,并將渲染順序設(shè)置為高于outerFloorNode的渲染順序。這確保了當(dāng)用戶在門戶外部時(shí)击孩,該節(jié)點(diǎn)將是不可見的迫悠。
- 7: 最后,將innerFloorNode的位置設(shè)置為位于outerFloorNode上方巩梢,并將其作為子項(xiàng)添加到floorNode创泄。將floor節(jié)點(diǎn)對(duì)象返回給調(diào)用者。
打開ViewController.swift并且添加如下代碼:
let positionY: CGFloat = -WALL_HEIGHT*0.5
let positionZ: CGFloat = -SURFACE_LENGTH*0.5
這些常數(shù)表示Y和Z維度中節(jié)點(diǎn)的位置偏移括蝠。通過替換makePortal()將floor節(jié)點(diǎn)添加到門戶中鞠抑。
func makePortal() -> SCNNode {
// 1
let portal = SCNNode()
// 2
let floorNode = makeFloorNode()
floorNode.position = SCNVector3(0, POSITION_Y, POSITION_Z)
// 3
portal.addChildNode(floorNode)
return portal
}
上面的代碼作用如下:
- 1: 創(chuàng)建一個(gè)SCNNode對(duì)象來保存門戶。
- 2: 我們可以使用SCNNodeHelpers中定義的makeFloorNode()創(chuàng)建floor節(jié)點(diǎn)忌警。我們可以使用常量偏移設(shè)置floorNode的位置搁拙。 SCNGeometry的中心在節(jié)點(diǎn)的父坐標(biāo)系中設(shè)置為此位置。
- 3: 將floorNode添加到門戶節(jié)點(diǎn)并返回門戶節(jié)點(diǎn)。請(qǐng)注意箕速,當(dāng)用戶在renderer(_ :, didAdd:, for:)中點(diǎn)擊視圖時(shí)酪碘,門戶節(jié)點(diǎn)將添加到在錨點(diǎn)位置創(chuàng)建的節(jié)點(diǎn)。
運(yùn)行程序效果如下:
你會(huì)注意到floor節(jié)點(diǎn)是黑暗的盐茎。那是因?yàn)槲覀冞€沒有添加光源兴垦。
打開SCNNodeHelpers.swift,添加如下代碼:
func makeCeilingNode() -> SCNNode{
let outerCeilingNode = makeOuterSurfaceNode(width: surfaceWidth, height: surfaceHeight, length: surfaceLength)
outerCeilingNode.position = SCNVector3(surfaceWidth * 0.5, surfaceHeight, 0)
let ceilingNode = SCNNode()
ceilingNode.addChildNode(outerCeilingNode)
let innerCeiling = SCNBox(width: surfaceWidth, height: surfaceHeight, length: surfaceLength, chamferRadius: 0)
innerCeiling.firstMaterial?.lightingModel = .physicallyBased
innerCeiling.firstMaterial?.diffuse.contents = UIImage(named: "ARResource.scnassets/ceiling/textures/CeilingDiffuse.png")
innerCeiling.firstMaterial?.emission.contents = UIImage(named: "ARResource.scnassets/ceiling/textures/CeilingEmis.png")
innerCeiling.firstMaterial?.normal.contents = UIImage(named: "ARResource.scnassets/ceiling/textures/CeilingNormal.png")
innerCeiling.firstMaterial?.specular.contents = UIImage(named: "ARResource.scnassets/ceiling/textures/CeilingSpecular.png")
innerCeiling.firstMaterial?.selfIllumination.contents = UIImage(named: "ARResource.scnassets/ceiling/textures/CeilingGloss.png")
repeatTextures(geometry: innerCeiling, scaleX: scaleX, scaleY: scaleY)
let innerCeilingNode = SCNNode(geometry: innerCeiling)
innerCeilingNode.renderingOrder = 100
innerCeilingNode.position = SCNVector3(surfaceHeight * 0.5, 0, 0)
ceilingNode.addChildNode(innerCeilingNode)
return ceilingNode
}
上面的代碼作用如下:
- 1: 與floor類似字柠,我們創(chuàng)建一個(gè)帶有天花板尺寸的outerCeilingNode探越。
- 2: 設(shè)置外部天花板節(jié)點(diǎn)的位置,使其位于天花板頂部募谎。創(chuàng)建一個(gè)節(jié)點(diǎn)來固定天花板的內(nèi)側(cè)和外側(cè)扶关。添加outerCeilingNode作為ceilingNode的子節(jié)點(diǎn)阴汇。
- 3: 使innerCeiling成為具有相應(yīng)尺寸的SCNBox對(duì)象数冬。
- 4: 將lightingModel設(shè)置為physicalBased。此外搀庶,設(shè)置ARResource.scnassets目錄中的各種紋理圖像定義的可視屬性的內(nèi)容拐纱。
- 5: repeatTextures()在X和Y維度中包裹紋理圖像,以創(chuàng)建天花板的重復(fù)圖案哥倔。
- 6: 使用innerCeiling幾何創(chuàng)建innerCeilingNode秸架,并將其renderingOrder屬性設(shè)置為一個(gè)比較大的值,以便在outerCeilingNode之后呈現(xiàn)它咆蒿。
- 7: 將innerCeilingNode放在其父節(jié)點(diǎn)中东抹,并將其添加為ceilingNode的子節(jié)點(diǎn)。返回ceilingNode給調(diào)用者沃测。
現(xiàn)在從某個(gè)地方調(diào)用它缭黔。打開PortalViewController.swift并在return語句之前將以下代碼塊添加到makePortal():
// 1
let ceilingNode = makeCeilingNode()
ceilingNode.position = SCNVector3(0, POSITION_Y+WALL_HEIGHT, POSITION_Z)
// 2
portal.addChildNode(ceilingNode)
上面的代碼作用如下:
- 1:使用剛剛定義的makeCeilingNode()創(chuàng)建天花板節(jié)點(diǎn)。將ceilingNode中心的位置設(shè)置為SCNVector3結(jié)構(gòu)蒂破。中心的Y坐標(biāo)被添加到墻壁高度的地板的Y位置偏移馏谨。
- 2:添加ceilingNode作為門戶的子節(jié)點(diǎn)。
運(yùn)行程序附迷,顯示效果如下:
往下看是地板惧互,上面是天花板,就差墻了喇伯。接下來喊儡,砌墻!
打開SCNNodeHelpers.swift文件稻据,添加以下代碼:
func makeWallNode(length: CGFloat = wallLength, height: CGFloat = wallHeight, maskLowerSide: Bool = false) -> SCNNode{
let outerWall = SCNBox(width: wallWidth, height: height, length: length, chamferRadius: 0)
outerWall.firstMaterial?.diffuse.contents = UIColor.white
outerWall.firstMaterial?.transparency = 0.000001
let outerWallNode = SCNNode(geometry: outerWall)
let multiplier: CGFloat = maskLowerSide ? -1 : 1
outerWallNode.position = SCNVector3(wallWidth * multiplier, 0, 0)
outerWallNode.renderingOrder = 10
let wallNode = SCNNode()
wallNode.addChildNode(outerWallNode)
let innerWall = SCNBox(width: wallWidth, height: height, length: length, chamferRadius: 0)
innerWall.firstMaterial?.lightingModel = .physicallyBased
innerWall.firstMaterial?.diffuse.contents = UIImage(named: "ARResource.scnassets/wall/textures/WallsDiffuse.png")
innerWall.firstMaterial?.metalness.contents = UIImage(named: "ARResource.scnassets/wall/textures/WallsMetalness.png")
innerWall.firstMaterial?.roughness.contents = UIImage(named: "ARResource.scnassets/wall/textures/WallsRoughness.png")
innerWall.firstMaterial?.normal.contents = UIImage(named: "ARResource.scnassets/wall/textures/WallsNormal.png")
innerWall.firstMaterial?.specular.contents = UIImage(named: "ARResource.scnassets/wall/textures/WallsSpec.png")
innerWall.firstMaterial?.selfIllumination.contents = UIImage(named: "ARResource.scnassets/wall/textures/WallsGloss.png")
let innerWallNode = SCNNode(geometry: innerWall)
innerWallNode.renderingOrder = 100
wallNode.addChildNode(innerWallNode)
return wallNode
}
上面的代碼作用如下:
- 1: 創(chuàng)建一個(gè)外墻節(jié)點(diǎn)艾猜,該節(jié)點(diǎn)將位于墻的外側(cè),使其從外部看起來是透明的。創(chuàng)建一個(gè)與墻的尺寸匹配的SCNBox對(duì)象箩朴。
- 2: 可以將材質(zhì)的漫反射內(nèi)容設(shè)置為單色白色岗喉,將透明度設(shè)置為較低的數(shù)字。如果從房間外面看墻炸庞,這有助于實(shí)現(xiàn)透視效果钱床。
- 3: 使用outerWall幾何創(chuàng)建節(jié)點(diǎn)。根據(jù)需要渲染外墻的墻的哪一側(cè)設(shè)置乘數(shù)埠居。如果maskLowerSide設(shè)置為true查牌,則外墻位于墻節(jié)點(diǎn)坐標(biāo)系中的內(nèi)壁下方;否則,它將放在上面滥壕。
我們可以設(shè)置節(jié)點(diǎn)的位置纸颜,使外墻偏移X維度中的墻寬度。將外墻的渲染順序設(shè)置為較小的數(shù)字绎橘,以便首先渲染它胁孙。這使得墻壁從外面看不見。 - 4:還可以創(chuàng)建一個(gè)節(jié)點(diǎn)來保存墻称鳞,并將outerWallNode添加為其子節(jié)點(diǎn)涮较。
- 5:使innerWall成為具有相應(yīng)墻尺寸的SCNBox對(duì)象。
- 6:將lightingModel設(shè)置為physicalBased冈止。與天花板和地面節(jié)點(diǎn)類似狂票,我們可以設(shè)置由墻壁的各種紋理圖像定義的視覺屬性的內(nèi)容。
- 7: 最后熙暴,使用innerWall幾何創(chuàng)建innerWallNode對(duì)象闺属。將此節(jié)點(diǎn)添加到父wallNode對(duì)象。默認(rèn)情況下周霉,innerWallNode位于wallNode的原點(diǎn)掂器。將節(jié)點(diǎn)返回給調(diào)用者。
現(xiàn)在诗眨,打開ViewController.swift唉匾,之后在makePortal()函數(shù)中添加如下代碼:
- 1: 為遠(yuǎn)墻創(chuàng)建一個(gè)節(jié)點(diǎn)。 farWallNode需要下方的掩碼匠楚。所以maskLowerSide的默認(rèn)值為false巍膘。
- 2: 將eulerAngles添加到節(jié)點(diǎn)。由于壁沿Y軸旋轉(zhuǎn)并垂直于攝像機(jī)芋簿,因此第二部件的旋轉(zhuǎn)角度為90度峡懈。墻壁沒有X軸和Z軸的旋轉(zhuǎn)角度。
- 3: 設(shè)置farWallNode中心的位置与斤,使其高度偏移positionY肪康。它的深度是通過將天花板中心的深度加到從天花板中心到遠(yuǎn)端的距離來計(jì)算的荚恶。
現(xiàn)在只有一面墻,我們需要把其他幾面墻也添加上:
let rightSideWallNode = makeWallNode(maskLowerSide: true)
rightSideWallNode.eulerAngles = SCNVector3(0, 180.0.degreeToRadians, 0)
rightSideWallNode.position = SCNVector3(wallLength*0.5,
positionY+wallHeight*0.5,
positionZ)
portal.addChildNode(rightSideWallNode)
let leftSideWallNode = makeWallNode(maskLowerSide: true)
leftSideWallNode.position = SCNVector3(-wallLength*0.5,
positionY+wallHeight*0.5,
positionZ)
portal.addChildNode(leftSideWallNode)
上面代碼作用如下:
- 1: 為右墻創(chuàng)建一個(gè)節(jié)點(diǎn)磷支。我們希望將外墻放在節(jié)點(diǎn)的下邊谒撼,因此將maskLowerSide設(shè)置為true。
- 2:我們可以將墻的旋轉(zhuǎn)沿Y軸設(shè)置為180度雾狈。這確保了墻壁的內(nèi)側(cè)朝向正確的方向廓潜。
- 3: 設(shè)置墻壁的位置,使其與遠(yuǎn)墻善榛,天花板和地板的右邊緣相連辩蛋。將rightSideWallNode添加為門戶的子節(jié)點(diǎn)。
- 4: 與右墻節(jié)點(diǎn)類似移盆,創(chuàng)建一個(gè)節(jié)點(diǎn)來表示左墻悼院,并將maskLowerSide設(shè)置為true。
- 5: 左墻沒有任何旋轉(zhuǎn)咒循,但調(diào)整它的位置据途,以便它與遠(yuǎn)墻,天花板和天花板的左邊緣相連剑鞍。我們將左墻節(jié)點(diǎn)添加為門戶節(jié)點(diǎn)的子節(jié)點(diǎn)昨凡。
運(yùn)行程序爽醋,效果如下:
添加門口
現(xiàn)在三面墻都有了蚁署,最后一面墻應(yīng)該留出來點(diǎn)空間,充當(dāng)一個(gè)門蚂四。我們需要先定義一個(gè)門的寬度和高度:
let doorWidth: CGFloat = 1.0
let doorHeight: CGFloat = 2.4
然后添加一個(gè)addDoorway(node: SCNNode)方法:
func addDoorway(node: SCNNode){
let halfWalllength: CGFloat = wallLength * 0.5
let frontHalfWallLength: CGFloat = (wallLength - doorWidth) * 0.5
let rightDoorSideNode = makeWallNode(length: frontHalfWallLength)
rightDoorSideNode.eulerAngles = SCNVector3(0, 270.0.degreeToRadians, 0)
rightDoorSideNode.position = SCNVector3(halfWalllength - 0.5 * doorWidth, positionY + wallLength * 0.5, positionZ + surfaceLength * 0.5)
node.addChildNode(rightDoorSideNode)
let leftDoorSideNode = makeWallNode(length: frontHalfWallLength)
leftDoorSideNode.eulerAngles = SCNVector3(0, 270.0.degreeToRadians, 0)
leftDoorSideNode.position = SCNVector3(-halfWalllength + 0.5 * frontHalfWallLength, positionY + wallHeight * 0.5, positionZ + surfaceLength * 0.5)
node.addChildNode(leftDoorSideNode)
let aboveDoorNode = makeWallNode(length: doorWidth, height: wallHeight - doorHeight)
aboveDoorNode.eulerAngles = SCNVector3(0, 270.0.degreeToRadians, 0)
aboveDoorNode.position = SCNVector3(0, positionY + (wallHeight - doorHeight) * 0.5 + doorHeight, positionZ + surfaceLength * 0.5)
node.addChildNode(aboveDoorNode)
}
上面的代碼作用如下:
- 1: 定義用于存儲(chǔ)門兩側(cè)的半壁長度和前壁長度的常數(shù)光戈。
- 2: 使用上一步中聲明的常量創(chuàng)建一個(gè)節(jié)點(diǎn)來表示入口右側(cè)的墻。我們還可以調(diào)整節(jié)點(diǎn)的旋轉(zhuǎn)和位置遂赠,使其連接到右墻久妆,天花板和地板的前邊緣。然后跷睦,將rightDoorSideNode添加為給定節(jié)點(diǎn)的子節(jié)點(diǎn)筷弦。
- 3: 與步驟2類似,我們可以為門口左側(cè)創(chuàng)建一個(gè)節(jié)點(diǎn)抑诸,并設(shè)置leftDoorSideNode的旋轉(zhuǎn)和位置烂琴,使其與左側(cè)墻壁,天花板和地面節(jié)點(diǎn)的前邊緣相連蜕乡。最后奸绷,使用addChildNode()將其作為子節(jié)點(diǎn)添加到節(jié)點(diǎn)。
之后在makePortal()中添加下面的代碼:
addDoorway(node: portal)
構(gòu)建并運(yùn)行應(yīng)用程序层玲。你會(huì)看到門口的門口号醉,但門的頂部正在接觸天花板反症。我們需要添加另一塊墻,以使門口跨越預(yù)先設(shè)定的doorHeight畔派。
我們?cè)?strong>addDoorway(node:)函數(shù)中添加如下代碼:
let aboveDoorNode = makeWallNode(length: doorWidth, height: wallHeight - doorHeight)
aboveDoorNode.eulerAngles = SCNVector3(0, 270.0.degreeToRadians, 0)
aboveDoorNode.position = SCNVector3(0, positionY + (wallHeight - doorHeight) * 0.5 + doorHeight, positionZ + surfaceLength * 0.5)
node.addChildNode(aboveDoorNode)
上面的代碼作用如下:
- 1: 創(chuàng)建一個(gè)具有相應(yīng)尺寸的墻節(jié)點(diǎn)铅碍,使其位于門戶入口上方。
- 2: 調(diào)整aboveDoorNode的旋轉(zhuǎn)线椰,使其位于門戶的前面该酗。蒙面?zhèn)确旁谕饷妗?/li>
- 3: 設(shè)置節(jié)點(diǎn)的位置,使其位于我們剛剛構(gòu)建的門口的頂部士嚎。將其添加為節(jié)點(diǎn)的子節(jié)點(diǎn)呜魄。
運(yùn)行程序效果如下:
添加燈光效果
添加燈光代碼如下:
func placeLightSource(rootNode: SCNNode){
let light = SCNLight()
light.intensity = 10
light.type = .omni
let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(0, positionY + wallHeight, positionZ)
rootNode.addChildNode(lightNode)
}
上面的代碼作用如下:
- 1: 創(chuàng)建一個(gè)SCNLight對(duì)象并設(shè)置其強(qiáng)度。由于我們使用的是基于物理的照明模型莱衩,因此該值是光源的發(fā)光流量爵嗅。默認(rèn)值為1000流明,但是我們希望強(qiáng)度更低笨蚁,使其外觀稍暗睹晒。
- 2: 燈光類型決定燈光提供的照明形狀和方向,以及可用于修改燈光行為的屬性集括细。在這里伪很,我們可以將光的類型設(shè)置為全向,也稱為點(diǎn)光源奋单。全向光具有恒定的強(qiáng)度和方向锉试。燈光相對(duì)于場(chǎng)景中其他物體的位置決定了它的方向。
- 3: 我們可以創(chuàng)建一個(gè)節(jié)點(diǎn)來保持燈光览濒,并使用燈光屬性將燈光對(duì)象附加到節(jié)點(diǎn)呆盖。
- 4: 使用Y和Z偏移將光放在天花板的中心,然后添加lightNode作為rootNode的子節(jié)點(diǎn)贷笛。
在makePortal()方法中添加如下代碼:
placeLightSource(rootNode: portal)
運(yùn)行程序应又,效果如下:
這下燈光也有了??。
上一章 | 目錄 | 下一章 |
---|