SceneKit 中的背面剔除
Back face culling in SceneKit
我目前正在尝试在场景工具包中设置一个旋转球。我创建了球并为其应用了纹理。
ballMaterial.diffuse.contents = UIImage(named: ballTexture)
ballMaterial.doubleSided = true
ballGeometry.materials = [ballMaterial]
当前的 ballTexture 是一个半透明的纹理,因为我希望看到背面滚动。
然而,我得到了一些奇怪的剔除,即使双面 属性 设置为 true,也只显示了背面多边形的一半。
如有任何帮助,我们将不胜感激。
发生这种情况是因为透明度的效果取决于绘制顺序。 SceneKit 不知道在前面的多边形之前绘制球体的背面多边形。 (事实上 ,如果不为每一帧重新组织 GPU 上的顶点缓冲区,它就无法真正做到这一点,这将极大地拖累渲染性能。)
SCNSphere
的顶点布局设置得像地球上的 lat/long 网格:三角形沿着从 0° 到 360° 的子午线按顺序渲染,因此取决于如何球体相对于相机定向,球体远侧的一些面会先于较近的面渲染。
要解决此问题,您需要强制渲染顺序 — 直接或通过深度缓冲区。这是一种方法,对内表面使用单独的 material 来说明差异。
// add two balls, one a child of the other
let node = SCNNode(geometry: SCNSphere(radius: 1))
let node2 = SCNNode(geometry: SCNSphere(radius: 1))
scene.rootNode.addChildNode(node)
node.addChildNode(node2)
// cull back-facing polygons on the first ball
// so we only see the outside
let mat1 = node.geometry!.firstMaterial!
mat1.cullMode = .Back
mat1.transparent.contents = bwCheckers
// my "bwCheckers" uses black for transparent, white for opaque
mat1.transparencyMode = .RGBZero
// cull front-facing polygons on the second ball
// so we only see the inside
let mat2 = node2.geometry!.firstMaterial!
mat2.cullMode = .Front
mat2.diffuse.contents = rgCheckers
// sphere normals face outward, so to make the inside respond
// to lighting, we need to invert them
let shader = "_geometry.normal *= -1.0;"
mat2.shaderModifiers = [SCNShaderModifierEntryPointGeometry: shader]
(不需要末尾的着色器修饰符位——它只是使内部 material 获得漫反射着色。您也可以使用 material 属性不涉及法线或光照,例如 emission
,具体取决于您想要的外观。)
您也可以使用具有双面 material 的单个节点来执行此操作,方法是在这种情况下禁用 writesToDepthBuffer
, but that could also lead to undesirable interactions with the rest of your scene content — you might also need to mess with renderingOrder
。
macOS 10.13 和 iOS 11 添加了 SCNTransparencyMode.dualLayer 据我所知甚至不需要将 isDoubleSided
设置为 true(文档未提供任何信息全部)。因此,一个对我有用的简单解决方案是:
ballMaterial.diffuse.contents = UIImage(named: ballTexture)
ballMaterial.transparencyMode = .dualLayer
ballGeometry.materials = [ballMaterial]
我目前正在尝试在场景工具包中设置一个旋转球。我创建了球并为其应用了纹理。
ballMaterial.diffuse.contents = UIImage(named: ballTexture)
ballMaterial.doubleSided = true
ballGeometry.materials = [ballMaterial]
当前的 ballTexture 是一个半透明的纹理,因为我希望看到背面滚动。
然而,我得到了一些奇怪的剔除,即使双面 属性 设置为 true,也只显示了背面多边形的一半。
如有任何帮助,我们将不胜感激。
发生这种情况是因为透明度的效果取决于绘制顺序。 SceneKit 不知道在前面的多边形之前绘制球体的背面多边形。 (事实上 ,如果不为每一帧重新组织 GPU 上的顶点缓冲区,它就无法真正做到这一点,这将极大地拖累渲染性能。)
SCNSphere
的顶点布局设置得像地球上的 lat/long 网格:三角形沿着从 0° 到 360° 的子午线按顺序渲染,因此取决于如何球体相对于相机定向,球体远侧的一些面会先于较近的面渲染。
要解决此问题,您需要强制渲染顺序 — 直接或通过深度缓冲区。这是一种方法,对内表面使用单独的 material 来说明差异。
// add two balls, one a child of the other
let node = SCNNode(geometry: SCNSphere(radius: 1))
let node2 = SCNNode(geometry: SCNSphere(radius: 1))
scene.rootNode.addChildNode(node)
node.addChildNode(node2)
// cull back-facing polygons on the first ball
// so we only see the outside
let mat1 = node.geometry!.firstMaterial!
mat1.cullMode = .Back
mat1.transparent.contents = bwCheckers
// my "bwCheckers" uses black for transparent, white for opaque
mat1.transparencyMode = .RGBZero
// cull front-facing polygons on the second ball
// so we only see the inside
let mat2 = node2.geometry!.firstMaterial!
mat2.cullMode = .Front
mat2.diffuse.contents = rgCheckers
// sphere normals face outward, so to make the inside respond
// to lighting, we need to invert them
let shader = "_geometry.normal *= -1.0;"
mat2.shaderModifiers = [SCNShaderModifierEntryPointGeometry: shader]
(不需要末尾的着色器修饰符位——它只是使内部 material 获得漫反射着色。您也可以使用 material 属性不涉及法线或光照,例如 emission
,具体取决于您想要的外观。)
您也可以使用具有双面 material 的单个节点来执行此操作,方法是在这种情况下禁用 writesToDepthBuffer
, but that could also lead to undesirable interactions with the rest of your scene content — you might also need to mess with renderingOrder
。
macOS 10.13 和 iOS 11 添加了 SCNTransparencyMode.dualLayer 据我所知甚至不需要将 isDoubleSided
设置为 true(文档未提供任何信息全部)。因此,一个对我有用的简单解决方案是:
ballMaterial.diffuse.contents = UIImage(named: ballTexture)
ballMaterial.transparencyMode = .dualLayer
ballGeometry.materials = [ballMaterial]