SCNNode 渲染异常 bug/artefacts

Strange bug/artefacts on SCNNode rendering

在某些 iOS 设备(iPhone 6s Plus)上,对象部分会部分和任意消失。 如何避免这种情况?

所有的sticks必须相同并且是一个SCNNode的克隆。 16 个复杂的 SCNNode,来自 3 个 SCNNode:box、ball 和 stick。 node.flattenedClone().

包含几何的节点

一定是这样的:

代码片段:

func initBox()
{
 var min: SCNVector3 = SCNVector3()
 var max: SCNVector3 = SCNVector3()

 let geom1 = SCNBox(width: boxW, height: boxH, length: boxL, chamferRadius: boxR)

 geom1.firstMaterial?.reflective.contents = UIImage(data: BoxData)
 geom1.firstMaterial?.reflective.intensity = 1.2
 geom1.firstMaterial?.fresnelExponent = 0.25
 geom1.firstMaterial?.locksAmbientWithDiffuse = true
 geom1.firstMaterial?.diffuse.wrapS = SCNWrapMode.Repeat

 let geom2 = SCNSphere(radius: 0.5 * boxH)

 geom2.firstMaterial?.reflective.contents = UIImage(data: BalData)
 geom2.firstMaterial?.reflective.intensity = 1.2
 geom2.firstMaterial?.fresnelExponent = 0.25
 geom2.firstMaterial?.locksAmbientWithDiffuse = true
 geom2.firstMaterial?.diffuse.wrapS = SCNWrapMode.Repeat

 let geom3 = SCNCapsule(capRadius: stickR, height: stickH)

 geom3.firstMaterial?.reflective.contents = UIImage(data: StickData)
 geom3.firstMaterial?.reflective.intensity = 1.2
 geom3.firstMaterial?.fresnelExponent = 0.25
 geom3.firstMaterial?.locksAmbientWithDiffuse = true
 geom3.firstMaterial?.diffuse.wrapS = SCNWrapMode.Repeat

 let box = SCNNode()
 box.castsShadow = false
 box.position = SCNVector3Zero
 box.geometry = geom1
 Material.setFirstMaterial(box, materialName: Materials[boxMatId])

 let bal = SCNNode()
 bal.castsShadow = false
 bal.position = SCNVector3(0, 0.15 * boxH, 0)
 bal.geometry = geom2
 Material.setFirstMaterial(bal, materialName: Materials[balMatId])

 let stick = SCNNode()
 stick.castsShadow = false
 stick.position = SCNVector3Zero
 stick.geometry = geom3
 stick.getBoundingBoxMin(&min, max: &max)
 stick.pivot = SCNMatrix4MakeTranslation(0, min.y, 0)
 Material.setFirstMaterial(stick, materialName: Materials[stickMatId])

 box.addChildNode(bal)
 box.addChildNode(stick)

 boxmain = box.flattenedClone()
 boxmain.name = "box"
}

向场景添加节点:

func Boxesset()
{
    let Boxes = SCNNode()
    Boxes.name = "Boxes"

    var z: Float = -4.5 * radius
    for _ in 0..<4
    {
        var x: Float = -4.5 * radius
        for _ in 0..<4
        {
            let B: SCNNode = boxmain.clone()
            B.position = SCNVector3(x: x, y: radius, z: z)
            Boxes.addChildNode(B)
            x += 3 * Float(radius)
        }
        z += 3 * Float(radius)
    }
    self.rootNode.addChildNode(Boxes)
}

这已经过测试并且在模拟器上运行良好 - 所有设备, 在物理设备上 - iPad Retina 和 iPhone 5.

仅在超现代 iPhone 6s Plus (128 Gb) 上观察到故障。

The problem is clearly visible on the video ->

图形问题可以通过将默认渲染 API 更改为 OpenGL ES...

来解决

...但是在iPhone 6S Plus上与图形无关的纯计算模块可能会出现意想不到的问题。 (iPhone 6 没有这样的问题)。

怎么了?

TL;DR

scnView.prepareObject(boxmain, shouldAbortBlock: nil) 添加到 initBox 的末尾。

我在我的 6s Plus 上快速查看了您的代码 运行ning 并看到了类似的结果。一个角节点丢失了,并且每个角节点都丢失了 运行。但是我们 运行 使用相同的代码,我的缺少材料数据...

SceneKit 是懒惰的,通常直到将对象添加到场景中才完成事情。我第一次遇到这种从 SceneKit 基元(SCNSphere 等)中提取几何体的情况,当你通过以下几行克隆某些东西时,你会发现它。

let B: SCNNode = boxmain.clone()
...
boxmain = box.flattenedClone()

我会说 SceneKit 在第二个克隆持续出现之前根本没有完成克隆。我无法确定这一点。

删除第一个克隆为我解决了这个问题。例如,将 boxmain = box.flattenedClone() 替换为 boxmain = box。但我想说你所做的是最佳实践,展平这些节点将减少绘制调用的数量并提高性能(可能不是 6s 的问题)。

SceneKit 还提供了一个方法 - prepareObject:shouldAbortBlock:,它将在将对象添加到场景之前执行所需的操作(在本例中为 .flattenedClone())。

将以下行添加到 initBox 函数的末尾也可以解决问题,是更好的解决方案。

scnView.prepareObject(boxmain, shouldAbortBlock: nil)

就说吧,我不知道我的问题的正确答案,但我找到了一个适合自己的解决方案。

原来,都在SCNMaterial的"diffuse"属性里面

无论出于何种原因,Metal 都不太喜欢 diffuse = UIColor(...) 但是,如果复合 SCNNode 中的至少一个元素(如我的情况)是 diffuse.contents = UIImage(...),那么一切都会开始完美地工作。

有效

diffuse=<SCNMaterialProperty: 0x7a6d50a0 | contents=<UIImage: 0x7a6d5b40> size {128, 128} orientation 0 scale 1.000000>

没用

diffuse=<SCNMaterialProperty: 0x7e611a50 | contents=UIDeviceRGBColorSpace 0.25 0.25 0.25 0.99>

我发现问题的解决方法很简单:

我刚刚在现有的 3 个 diffuse.contents = UIColor(...) 元素中添加了一个不显眼的 diffuse.contents = UIImage(...) 元素,效果很好。

所以,我的建议是:

  • 使用 Metal 时要小心。 (我在5S及以上的设备上有问题)

  • 在真实设备上彻底测试 SceneKit 应用程序,不要只相信模拟器

我希望,这是暂时的错误,会在 Xcode 的未来版本中修复。

有一个不错的应用程序!

P.S。顺便说一句,完成的应用程序现在在 AppStore 中是完全免费的 Qubic: tic-tac-toe 4x4x4