Странная ошибка / артефакты при рендеринге SCNNode

На некоторых устройствах iOS (iPhone 6s Plus) происходит частичное и произвольное исчезновение частей объекта. Как этого избежать?

введите описание изображения здесь введите описание изображения здесь

Все палочки должны быть одинаковыми и быть клонами одного SCNNode. 16 комплекса SCNNodes, от 3 SCNNode: коробка, мяч и палка. Узел, содержащий геометрию 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.

Glitch наблюдается только на ультрасовременном iPhone 6s Plus (128 Гб).

Проблема ясно видна на видео ->

введите описание изображения здесь

Проблема с графикой может быть решена путем изменения API рендеринга по умолчанию для OpenGL ES …

… но у вас могут возникнуть непредвиденные проблемы в чистых вычислительных модулях, которые не связаны с графикой на iPhone 6S Plus. (у iPhone 6 таких проблем нет).

Что не так?

TL; DR

Добавьте scnView.prepareObject(boxmain, shouldAbortBlock: nil) в конец вашего initBox .

Я быстро просмотрел ваш код, работающий на моем 6s Plus, и увидел похожие результаты. Один из угловых узлов отсутствовал и постоянно пропускал каждый прогон. Но у нас не работает тот же код, у меня отсутствуют данные о материалах …

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.

По какой-то причине 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> 

Я нашел решение проблемы просто:

Я просто добавил один маленький, незаметный элемент с diffuse.contents = UIImage (…) к существующим 3 элементам с diffuse.contents = UIColor (…), и он отлично поработал.

Итак, мои рекомендации:

  • будьте осторожны при работе с Metal. (У меня проблемы с устройствами 5S и выше)

  • тщательно протестировать приложения SceneKit на реальных устройствах, не доверяйте только симулятору

Надеюсь, это временные ошибки, и это будет исправлено в будущих выпусках Xcode.

Приятные приложения!

PS Кстати, готовое приложение полностью бесплатное в AppStore теперь Qubic: tic-tac-toc 4x4x4