为什么自定义 SCNGeometry 中的黑色阴影比 SCNSphere 中的黑色阴影浅
Why are the shades of black in a custom SCNGeometry lighter than in an SCNSphere
在这个场景中
- 左侧的球体列是使用
SCNSphere()
创建的
- 右侧 'circles' 的列是使用
SCNGeometry()
使用 .point
原语创建的
- 只有一个环境光源
- 所有几何图形都使用
.constant
光照模型
- 每对球体使用相同的 RGB 值来定义颜色。
为什么最后两对圆的黑色阴影比它们的等效球体浅?
重现此图像的完整 playground 代码:
创建场景
import UIKit
import SceneKit
import PlaygroundSupport
// Constants I'm using for the darkest grey colour
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]
v.backgroundColor
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
// a camera
var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.simdPosition = simd_float3(0,0,8)
scene.rootNode.addChildNode(cameraNode)
scene.rootNode.addChildNode(ambientLigntNode)
球体列
// ----------------------------------------------------
// White Sphere
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)
// ----------------------------------------------------
// Black Sphere
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)
// ----------------------------------------------------
// Red Sphere
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)
// ----------------------------------------------------
// Green Sphere
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)
// ----------------------------------------------------
// Blue Sphere
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)
// ----------------------------------------------------
// Grey Sphere
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)
圆列
// ----------------------------------------------------
// Custom SCNGeometry using vertex data
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 转换为 LinearRGB 得到相同的结果
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
)
]
如果两种类型的图形以不同的颜色 space 生成,就会出现这种情况。例如,如果球体的灰色被解释为 sRGB 中的颜色 space,而圆被解释为通用 RGB space,那么您会看到不同的颜色值。
考虑以下 playground 代码:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
import CoreGraphics
class MyViewController : UIViewController {
override func loadView() {
let view = CustomView()
view.backgroundColor = UIColor.darkGray
self.view = view
}
}
class CustomView : UIView {
override func draw(_ rect: CGRect) {
if let cgContext = UIGraphicsGetCurrentContext() {
cgContext.saveGState()
let gray : [CGFloat] = [0.5, 0.5, 0.5, 1.0]
let srgbGray = CGColor(
colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!, components: gray)!
cgContext.setFillColor(srgbGray)
cgContext.fillEllipse(in: CGRect(x:10, y:20, width:72, height: 72))
let genericGray = CGColor(colorSpace: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, components: gray)!
cgContext.setFillColor(genericGray)
cgContext.fillEllipse(in: CGRect(x:110, y:20, width:72, height: 72))
cgContext.restoreGState()
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
它产生的效果与您所看到的效果几乎相同。我怀疑圆圈是假设通用颜色 space 绘制的,球体是假设 sRGB 绘制的。
在这个场景中
- 左侧的球体列是使用
SCNSphere()
创建的
- 右侧 'circles' 的列是使用
SCNGeometry()
使用.point
原语创建的 - 只有一个环境光源
- 所有几何图形都使用
.constant
光照模型 - 每对球体使用相同的 RGB 值来定义颜色。
为什么最后两对圆的黑色阴影比它们的等效球体浅?
重现此图像的完整 playground 代码:
创建场景
import UIKit
import SceneKit
import PlaygroundSupport
// Constants I'm using for the darkest grey colour
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]
v.backgroundColor
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
// a camera
var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.simdPosition = simd_float3(0,0,8)
scene.rootNode.addChildNode(cameraNode)
scene.rootNode.addChildNode(ambientLigntNode)
球体列
// ----------------------------------------------------
// White Sphere
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)
// ----------------------------------------------------
// Black Sphere
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)
// ----------------------------------------------------
// Red Sphere
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)
// ----------------------------------------------------
// Green Sphere
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)
// ----------------------------------------------------
// Blue Sphere
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)
// ----------------------------------------------------
// Grey Sphere
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)
圆列
// ----------------------------------------------------
// Custom SCNGeometry using vertex data
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 转换为 LinearRGB 得到相同的结果
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
)
]
如果两种类型的图形以不同的颜色 space 生成,就会出现这种情况。例如,如果球体的灰色被解释为 sRGB 中的颜色 space,而圆被解释为通用 RGB space,那么您会看到不同的颜色值。
考虑以下 playground 代码:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
import CoreGraphics
class MyViewController : UIViewController {
override func loadView() {
let view = CustomView()
view.backgroundColor = UIColor.darkGray
self.view = view
}
}
class CustomView : UIView {
override func draw(_ rect: CGRect) {
if let cgContext = UIGraphicsGetCurrentContext() {
cgContext.saveGState()
let gray : [CGFloat] = [0.5, 0.5, 0.5, 1.0]
let srgbGray = CGColor(
colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!, components: gray)!
cgContext.setFillColor(srgbGray)
cgContext.fillEllipse(in: CGRect(x:10, y:20, width:72, height: 72))
let genericGray = CGColor(colorSpace: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, components: gray)!
cgContext.setFillColor(genericGray)
cgContext.fillEllipse(in: CGRect(x:110, y:20, width:72, height: 72))
cgContext.restoreGState()
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
它产生的效果与您所看到的效果几乎相同。我怀疑圆圈是假设通用颜色 space 绘制的,球体是假设 sRGB 绘制的。