首页 文章

在 SCNNode 渲染上发生奇怪的 bug/artefacts

提问于
浏览
2

在某些 iOS 设备(iPhone 6s Plus)上,对象部分会有部分和任意消失。怎么避免这个?

在此输入图像描述

在此输入图像描述

所有的棒都必须相同,并且是一个 SCNNode 的克隆。 16 个复杂的 SCNNode,来自 3 个 SCNNode:盒子,球和棒。包含 node.flattenedClone()几何的节点。

一定是这样的:

在此输入图像描述

Сode 片段:

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)上观察到 Glitch。

问题在视频中清晰可见 - >

在此输入图像描述

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

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

怎么了?

2 回答

  • 2

    TL; DR

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

    我快速浏览了在 6s Plus 上运行的代码并看到了类似的结果。其中一个角落节点丢失了,并且每次运行都一直缺失。但我们没有运行相同的代码,我的缺少材料数据......

    SceneKit 是懒惰的,通常在将对象添加到场景之前不会完成任务。我首先从 SceneKit 原语(SCNSphere etc)中看到了这个提取几何体,当你通过以下行克隆某个东西的克隆时,你会发现它。

    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)
    
  • 1

    只是说,我不知道我的问题的正确答案,但我找到了一个可接受的解决方案。

    事实证明,这一切都在 SCNMaterial 的“漫反射”属性中。

    无论出于何种原因,当 diffuse = UIColor(...)时,Metal 不太喜欢但是如果复合 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>
    

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

    我刚刚用 diffuse.contents = UIColor(...)添加了一个带有 diffuse.contents = UIImage(...)的小而不显眼的元素到现有的 3 个元素,并且效果很好。

    所以,我的建议:

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

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

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

    有一个不错的应用!

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

相关问题