我正在回答我自己的问题。为了提供更多上下文,我们需要知道 LOD 何时会更改以停止空闲动画,我们的模型是蒙皮的。
如果您在 LOD 更改时不禁用此动画,会发生什么情况,那就是地理遍布整个地方并且看起来不对。
我们的解决方案是克隆渲染的节点地理,使用这个地理克隆创建一个节点,并在它很远时给它我们不同的 LOD,将此节点添加到层次结构中。这样节点就不会受到关节和动画的影响。我们将此节点称为lowLODNode。
当它太远时,您的渲染节点的 LOD 将为 nil,因此它会消失并且 lowLODNode 启动。请注意,lowLODNode 靠近时的 LOD 为 nil。
为了禁用动画,我们使用渲染器循环来计算与 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 的使用率。