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
在某些 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