简体   繁体   中英

Strange bug/artefacts on SCNNode rendering

On some iOS devices (iPhone 6s Plus) there is a partial and arbitrary disappearance of object parts. How to avoid this?

在此处输入图片说明 在此处输入图片说明

All sticks must be the same and are clones of one SCNNode. 16 complex SCNNodes, from 3 SCNNode: box, ball and stick. Node containing a geometry by node.flattenedClone().

It must be like this:

在此处输入图片说明

Сode fragment:

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

Add nodes to the scene:

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

This is tested and works great on the simulator - all devices, on the physical devices - iPad Retina and iPhone 5.

Glitch is observed only at ultra modern iPhone 6s Plus (128 Gb).

The problem is clearly visible on the video ->

在此处输入图片说明

The problem with graphics can be solved by changing the Default rendering API to OpenGL ES...

...but you may have unexpected problems in pure computing modules that are not associated with graphics on iPhone 6S Plus. (the iPhone 6 has no such problems).

What's wrong?

TL;DR

Add scnView.prepareObject(boxmain, shouldAbortBlock: nil) to the end of your initBox .

I had a quick look at your code running on my 6s Plus and saw similar results. One of the corner nodes was missing, and was consistently missing each run. But we're not running the same code, mine's missing the materials data...

SceneKit is lazy, often things are not done until an object is added to a scene. I first came across this extracting geometry from a SceneKit primitive ( SCNSphere etc), you're finding it when you clone a clone of something via the following lines.

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

I'd say SceneKit is simply not completing the clone before the second clone occurs consistently. I have no way of knowing this for sure.

Removing the first clone fixes this issue for me. For example replace boxmain = box.flattenedClone() with boxmain = box . But I'd say what you've done is best practice, flattening these nodes will reduce the number of draw calls and improve performance (probably not an issue on the 6s).

SceneKit also provides a method - prepareObject:shouldAbortBlock: that will perform the operations required before an object is added to a scene (in this case the .flattenedClone() ).

Adding the following line to the end of your initBox function also fixes the problem and is a better solution.

scnView.prepareObject(boxmain, shouldAbortBlock: nil)

Just say, I don't know the right answer to my question, but I found an acceptable solution for myself.

It turned out, it's all in the "diffuse" property of the SCNMaterial.

For whatever reason, Metal does not very like when diffuse = UIColor(...) But if at least one element in a compound SCNNode (as in my case) is diffuse.contents = UIImage(...), then everything begins to work perfectly.

it works

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

it doesn't work

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

I have found the solution of the problem is simply:

I just added one small, inconspicuous element with diffuse.contents = UIImage(...) to the existing 3 elements with diffuse.contents = UIColor(...) and it worked great.

So, my recommendations:

  • be careful when working with Metal. (I have a problems on the 5S devices and above)

  • thoroughly test the SceneKit applications on real devices, don't trust only the simulator

I hope, it's temporary bugs and it will be fix in future releases of Xcode.

Have a nice apps!

PS By the way, the finished app is completely free in the AppStore now Qubic: tic-tac-toe 4x4x4

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM