SCNLevelOfDetails委托/通知

时间:2019-05-01 13:16:16

标签: ios swift scenekit arkit scnnode

我在SceneKit中使用SCNLevelOfDetails。

我想知道是否有办法知道引擎何时更改特定节点的几何图形的levelOfDetails。

非常感谢。

1 个答案:

答案 0 :(得分:0)

我在回答自己的问题。为了提供更多的上下文,我们需要知道LOD何时会更改以停止空闲动画,我们的模型是蒙皮的。 如果在LOD更改时不禁用此动画,会发生以下情况,即地理遍历整个地方,而且看起来不对。

对我们来说,解决方案是克隆渲染的节点地理区域,使用此地理区域克隆创建一个节点,并为它提供远处的其他LOD,然后将该节点添加到层次结构中。这样,节点不受关节和动画的影响。我们将此节点称为lowLODNode。

您渲染的节点的LOD为零时,它的LOD为零,因此消失,并且lowLODNode起作用。请注意,当lowLODNode关闭时,其LOD为零。

要禁用动画,我们使用渲染器循环来计算距POV的距离,并在达到LOD阈值时将其禁用。请注意,我们每0.25秒对1000个以上的节点进行迭代,这要归功于simd库,该计算平均仅需要0.0016秒。

我要添加一个代码示例来展示该技术:

private func setupLOD() {
    // Keep animation and LOD working with a rigged node.
    // Scene graph
    // YourNodeSubclass
    //  - loadedDAENode
    //      - rigNode
    //      - geoGroupNode
    //          - geoNode -> this one has a SCNGeometry, empty geo when far LOD = [ geoNode.geo, nil ]
    //      - lowLODNode -> this one has the low poly versions and an empty geo when close LOD = [ nil, lowPoly1, lowPoly2, ... ]

    // LOD for the geometry affected by the rig. The geo disappear when far enough. Animations are working.
    let lowLevelMain = SCNLevelOfDetail(geometry: nil, worldSpaceDistance: 3.0)
    renderedNode.childNode(withName: "\(renderedNodeName)_geo", recursively: true)?.geometry?.levelsOfDetail = [lowLevelMain]

    // 1) Load low poly geo and eventual LOD geos from folder(s).
    // 2) Copy each LOD geo.
    // 3) Create a geo, make it transparent. This will be the high LOD geo the default one.
    // 4) Create a lowLODNode using this geo.
    // 5) Ceate your [LOD]
    // 6) Assign LOD to lowLODNode geometry
    // 7) Add the lowLODNode as a child to the rendered node

    // 1) 2)
    if let lowVersionScene = SCNScene(named: "art.scnassets/yourScene.dae"), let itemNode = lowVersionScene.rootNode.childNode(withName: "rootNodeName", recursively: false), let geo = itemNode.childNode(withName: "nodeWithGeo", recursively: true)?.geometry?.copy() as? SCNGeometry {
        // 3)
        let tranparentCube = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.0)
        tranparentCube.firstMaterial?.diffuse.contents = UIColor.clear
        // 4)
        let lowLODNode = SCNNode(geometry: tranparentCube)
        // 5)
        let lowLOD = SCNLevelOfDetail(geometry: geo, worldSpaceDistance: 3.0)
        // 6)
        lowLODNode?.levelsOfDetail = [lowLOD]
        // 7)
        renderedNode.addChildNode(lowLODNode)
    }
}

唯一的缺点是我们必须根据距离而不是像素大小使用LOD,因为我们需要知道何时禁用动画并且不想以像素为单位计算对象的大小。

使用这种技术,我们能够真正降低CPU和GPU的使用率。