ARKit教程12_第九章:幾何拔稳,紋理和燈光

前言

對(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)欠啤,可以將2D3D對(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)
}

這定義了一種在XY維度上在表面上重復(fù)紋理圖像的方法犁柜。

上面的代碼作用如下:

  • 1: 該方法采用SCNGeometry對(duì)象以及XY縮放因子作為輸入?yún)?shù)。紋理映射使用ST坐標(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)描述内狗。我們可以將XY縮放系數(shù)分別設(shè)置為scaleXscaleY怪嫌。

我們只想在用戶進(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()XY維度上重復(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ù)表示YZ維度中節(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()XY維度中包裹紋理圖像,以創(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: 使用YZ偏移將光放在天花板的中心,然后添加lightNode作為rootNode的子節(jié)點(diǎn)贷笛。

makePortal()方法中添加如下代碼:

placeLightSource(rootNode: portal)

運(yùn)行程序应又,效果如下:

這下燈光也有了??。

上一章 目錄 下一章
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乏苦,一起剝皮案震驚了整個(gè)濱河市株扛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汇荐,老刑警劉巖洞就,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拢驾,居然都是意外死亡奖磁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門繁疤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咖为,“玉大人秕狰,你說我怎么就攤上這事≡耆荆” “怎么了鸣哀?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吞彤。 經(jīng)常有香客問我我衬,道長,這世上最難降的妖魔是什么饰恕? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任挠羔,我火速辦了婚禮,結(jié)果婚禮上埋嵌,老公的妹妹穿的比我還像新娘破加。我一直安慰自己,他們只是感情好雹嗦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布范舀。 她就那樣靜靜地躺著,像睡著了一般了罪。 火紅的嫁衣襯著肌膚如雪锭环。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天泊藕,我揣著相機(jī)與錄音辅辩,去河邊找鬼。 笑死吱七,一個(gè)胖子當(dāng)著我的面吹牛汽久,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踊餐,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼臀稚!你這毒婦竟也來了吝岭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤吧寺,失蹤者是張志新(化名)和其女友劉穎窜管,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稚机,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幕帆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赖条。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片失乾。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡常熙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碱茁,到底是詐尸還是另有隱情裸卫,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布纽竣,位于F島的核電站墓贿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蜓氨。R本人自食惡果不足惜聋袋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望穴吹。 院中可真熱鬧舱馅,春花似錦、人聲如沸刀荒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缠借。三九已至干毅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泼返,已是汗流浹背硝逢。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绅喉,地道東北人渠鸽。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像柴罐,于是被迫代替她去往敵國和親徽缚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 對(duì)ARKit感興趣的同學(xué)革屠,可以訂閱ARKit教程專題源代碼地址在這里 正文 在上一章中,我們學(xué)習(xí)了如何設(shè)置 ...
    張芳濤閱讀 1,649評(píng)論 0 1
  • 前言 對(duì)ARKit感興趣的同學(xué)凿试,可以訂閱ARKit教程專題源代碼地址在這里 正文 在接下來的四章中,我們將使用 A...
    張芳濤閱讀 1,749評(píng)論 0 0
  • ??DOM(文檔對(duì)象模型)是針對(duì) HTML 和 XML 文檔的一個(gè) API(應(yīng)用程序編程接口)那婉。 ??DOM 描繪...
    霜天曉閱讀 3,615評(píng)論 0 7
  • feisky云計(jì)算、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 3,815評(píng)論 0 5
  • ??JavaScript 與 HTML 之間的交互是通過事件實(shí)現(xiàn)的。 ??事件寞奸,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,473評(píng)論 1 11