SCNLevelOfDetails delegate/notification

时光毁灭记忆、已成空白 提交于 2020-01-16 14:35:40

问题


I'm using SCNLevelOfDetails with SceneKit.

I was wondering if there's a way to know when the engine changes the levelOfDetails for a particular node's geometry.

Many thanks.


回答1:


I'm answering my own question. To give more context, we needed to know when the LOD would change in order to stop an idle animation, our model is skinned. What happens if you don't disable this animation when the LOD changes is that the geo goes all over the place and it looks wrong.

The solution for us was to clone the rendered node geo, create a node using this geo clone and give it our different LOD for when it's far, add this node to the hierarchy. This way the node is not affected by the joints and the animation. We call this node lowLODNode.

Your rendered node will have LOD of nil when it's too far so it disappear and the lowLODNode kicks in. Note the the lowLODNode has a LOD of nil when it's close.

To disable the animation we use the renderer loop to compute distance from POV and disable it if the LOD threshold is reached. Note that we are iterating through more than a 1000 nodes every 0.25 seconds, this calculation only takes 0.0016 second in average thanks to the simd library.

I'm adding a code example to show off the technique:

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)
    }
}

The only downside to this is that we have to use LOD based on distance NOT pixel size because we need to know when to disable the animation and don't want to compute the size of the object in pixels.

We were able to really lower the CPU and GPU usage using this technique.



来源:https://stackoverflow.com/questions/55936873/scnlevelofdetails-delegate-notification

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!