为什么自定义SCNGeometry的黑色色调比同参数SCNSphere更浅
问题:相同RGB值下SCNSphere与自定义Point图元渲染颜色不一致
在本场景中:
- 左侧球体列使用
SCNSphere()创建 - 右侧“圆形”列使用
.point图元的自定义SCNGeometry()创建 - 场景仅存在1个环境光光源
- 所有几何体均采用
.constant光照模型 - 每组配对的球体与圆形使用相同RGB值定义颜色
为什么最后两组配对元素中,圆形的黑色、灰色色调比对应球体更浅?

可复现该问题的完整Playground代码如下:
创建场景
import UIKit import SceneKit import PlaygroundSupport // 定义最深灰色的常量 let B_RED: CGFloat = 0.05 let B_GREEN: CGFloat = 0.05 let B_BLUE: CGFloat = 0.05 let B_ALPHA: CGFloat = 1.0 let BLACK_COLOUR = UIColor(red: B_RED, green: B_GREEN, blue: B_BLUE, alpha: B_ALPHA) let scene: SCNScene = { let s = SCNScene() return s }() let sceneView: SCNView = { let v = SCNView(frame: CGRect(x: 0, y: 0, width: 600, height: 800)) v.scene = scene v.backgroundColor = UIColor(white: 0.66, alpha: 1.0) v.allowsCameraControl = true v.debugOptions = [SCNDebugOptions.showLightInfluences] return v }() let ambientLigntNode: SCNNode = { let n = SCNNode() n.light = SCNLight() n.light!.type = SCNLight.LightType.ambient n.light!.color = UIColor(white: 1, alpha: 1.0) return n }() PlaygroundPage.current.liveView = sceneView // 相机 var cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.simdPosition = simd_float3(0,0,8) scene.rootNode.addChildNode(cameraNode) scene.rootNode.addChildNode(ambientLigntNode)
球体列实现
// ---------------------------------------------------- // 白色球体 let whiteSphere = SCNSphere(radius: 0.3) let whiteMaterial = SCNMaterial() whiteMaterial.diffuse.contents = simd_float3(1,1,1) whiteMaterial.lightingModel = .constant whiteSphere.materials = [whiteMaterial] let whiteSphereNode = SCNNode(geometry: whiteSphere) whiteSphereNode.simdPosition = simd_float3(-1,2,0) scene.rootNode.addChildNode(whiteSphereNode) // ---------------------------------------------------- // 黑色球体 let blackSphere = SCNSphere(radius: 0.3) let blackMaterial = SCNMaterial() blackMaterial.diffuse.contents = BLACK_COLOUR blackMaterial.lightingModel = .constant blackSphere.materials = [blackMaterial] let blackSphereNode = SCNNode(geometry: blackSphere) blackSphereNode.simdPosition = simd_float3(-1,-2,0) scene.rootNode.addChildNode(blackSphereNode) // ---------------------------------------------------- // 红色球体 let redSphere = SCNSphere(radius: 0.3) let redMaterial = SCNMaterial() redMaterial.diffuse.contents = UIColor( red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0 ) redMaterial.lightingModel = .constant redSphere.materials = [redMaterial] let redSphereNode = SCNNode(geometry: redSphere) redSphereNode.simdPosition = simd_float3(-1, 1, 0) scene.rootNode.addChildNode(redSphereNode) // ---------------------------------------------------- // 绿色球体 let greenSphere = SCNSphere(radius: 0.3) let greenMaterial = SCNMaterial() greenMaterial.diffuse.contents = UIColor( red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0 ) greenMaterial.lightingModel = .constant greenSphere.materials = [greenMaterial] let greenSphereNode = SCNNode(geometry: greenSphere) greenSphereNode.simdPosition = simd_float3(-1, 0, 0) scene.rootNode.addChildNode(greenSphereNode) // ---------------------------------------------------- // 蓝色球体 let blueSphere = SCNSphere(radius: 0.3) let blueMaterial = SCNMaterial() blueMaterial.diffuse.contents = UIColor( red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0 ) blueMaterial.lightingModel = .constant blueSphere.materials = [blueMaterial] let blueSphereNode = SCNNode(geometry: blueSphere) blueSphereNode.simdPosition = simd_float3(-1, -1, 0) scene.rootNode.addChildNode(blueSphereNode) // ---------------------------------------------------- // 灰色球体 let greySphere = SCNSphere(radius: 0.3) let greyMaterial = SCNMaterial() greyMaterial.diffuse.contents = UIColor( red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0 ) greyMaterial.lightingModel = .constant greySphere.materials = [greyMaterial] let greySphereNode = SCNNode(geometry: greySphere) greySphereNode.simdPosition = simd_float3(-1, -3, 0) scene.rootNode.addChildNode(greySphereNode)
圆形列实现
// ---------------------------------------------------- // 基于顶点数据的自定义SCNGeometry struct Vertex { let x: Float let y: Float let z: Float let r: Float let g: Float let b: Float let a: Float } let vertices: [Vertex] = [ Vertex(x: 0.0, y: 2.0, z: 0.0, r: 1.0, g: 1.0, b: 1.0, a: 1.0), // white Vertex(x: 0.0, y: 1.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0, a: 1.0), // red Vertex(x: 0.0, y: 0.0, z: 0.0, r: 0.0, g: 1.0, b: 0.0, a: 1.0), // green Vertex(x: 0.0, y: -1.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0, a: 1.0), // blue Vertex(x: 0.0, y: -3.0, z: 0.0, r: 0.5, g: 0.5, b: 0.5, a: 1.0), // rgb Vertex( x: 0.0, y: -2.0, z: 0.0, r: Float(B_RED), g: Float(B_GREEN), b: Float(B_BLUE), a: Float(B_ALPHA) ) ] let vertexData = Data( bytes: vertices, count: MemoryLayout<Vertex>.size * vertices.count ) let positionSource = SCNGeometrySource( data: vertexData, semantic: SCNGeometrySource.Semantic.vertex, vectorCount: vertices.count, usesFloatComponents: true, componentsPerVector: 3, bytesPerComponent: MemoryLayout<Float>.size, dataOffset: 0, dataStride: MemoryLayout<Vertex>.size ) let colourSource = SCNGeometrySource( data: vertexData, semantic: SCNGeometrySource.Semantic.color, vectorCount: vertices.count, usesFloatComponents: true, componentsPerVector: 4, bytesPerComponent: MemoryLayout<Float>.size, dataOffset: MemoryLayout<Float>.size * 3, dataStride: MemoryLayout<Vertex>.size ) let elements = SCNGeometryElement( data: nil, primitiveType: .point, primitiveCount: vertices.count, bytesPerIndex: MemoryLayout<Int>.size ) elements.pointSize = 100 elements.minimumPointScreenSpaceRadius = 100 elements.maximumPointScreenSpaceRadius = 100 let spheres = SCNGeometry( sources: [positionSource, colourSource], elements: [elements] ) let sphereNode = SCNNode(geometry: spheres) let sphereMaterial = SCNMaterial() sphereMaterial.lightingModel = .constant spheres.materials = [sphereMaterial] sphereNode.simdPosition = simd_float3(0,0,0) scene.rootNode.addChildNode(sphereNode)
原因与修复
该差异是sRGB与线性RGB色彩空间转换不一致导致的:UIColor初始化传入的是sRGB色彩空间的数值,而自定义顶点颜色传入的数值默认被当作线性RGB处理,导致低亮度数值渲染结果偏亮。将sRGB数值转换为线性RGB后二者渲染结果一致,修复代码如下:
func srgbToLinear(x: Float) -> Float { if x <= 0.04045 { return x / 12.92; } return powf((x + 0.055) / 1.055, 2.4) } let vertices: [Vertex] = [ Vertex(x: 0.0, y: 2.0, z: 0.0, r: 1.0, g: 1.0, b: 1.0, a: 1.0), // white Vertex(x: 0.0, y: 1.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0, a: 1.0), // red Vertex(x: 0.0, y: 0.0, z: 0.0, r: 0.0, g: 1.0, b: 0.0, a: 1.0), // green Vertex(x: 0.0, y: -1.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0, a: 1.0), // blue Vertex(x: 0.0, y: -3.0, z: 0.0, r: srgbToLinear(x: 0.5), g: srgbToLinear(x: 0.5), b: srgbToLinear(x: 0.5), a: 1.0), // rgb Vertex( x: 0.0, y: -2.0, z: 0.0, r: srgbToLinear(x: Float(B_RED)), g: srgbToLinear(x: Float(B_GREEN)), b: srgbToLinear(x: Float(B_GREEN)), a: 1.0 ) ]

内容的提问来源于stack exchange,提问作者tospig
相关产品推荐
相关产品推荐

