我正在开发一个关于太阳系的应用程序。
我正在尝试关闭光线照射到行星表面的发射纹理。但问题是,默认情况下发射纹理始终显示发射点,而不管光是否存在。
简而言之,我的要求:(我想隐藏发光点,在光线照射到表面的地方)
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene()
let earth = SCNSphere(radius: 1)
let earthNode = SCNNode()
let earthMaterial = SCNMaterial()
earthMaterial.diffuse.contents = UIImage(named: "earth.jpg")
earthMaterial.emission.contents = UIImage(named: "earthEmission.jpg")
earth.materials = [earthMaterial]
earthNode.geometry = earth
scene.rootNode.addChildNode(earthNode)
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 5)
scene.rootNode.addChildNode(lightNode)
sceneView.scene = scene
}
最佳答案
SceneKit 的 shader modifiers 非常适合这种任务。
您可以看到最终结果 here 的镜头。
片段着色器修改器
我们可以使用 _lightingContribution.diffuse
(RGB ( vec3
) 颜色表示应用于漫反射的灯光)来确定对象(在这种情况下 - 地球)被照亮的区域,然后使用它来掩盖 fragment shader modifier 中的发射纹理。
您使用它的方式完全取决于您。这是我想出的最简单的解决方案(使用 GLSL
语法,但如果您正在使用它,它将在运行时自动转换为 Metal
)
uniform sampler2D emissionTexture;
vec3 light = _lightingContribution.diffuse;
float lum = max(0.0, 1 - (0.2126*light.r + 0.7152*light.g + 0.0722*light.b)); // 1
vec4 emission = texture2D(emissionTexture, _surface.diffuseTexcoord) * lum; // 2, 3
_output.color += emission; // 4
_lightingContribution.diffuse
颜色的亮度(使用公式 from here )(如果照明不是纯白色) 着色器部分就是这样,现在让我们来看看 Swift 的一面。
快速设置
首先,我们 不是 要使用 Material 的
emission.contents
属性,而是需要创建自定义 SCNMaterialProperty
let emissionTexture = UIImage(named: "earthEmission.jpg")!
let emission = SCNMaterialProperty(contents: emissionTexture)
并使用
setValue(_:forKey:)
将其设置为 Material earthMaterial.setValue(emission, forKey: "emissionTexture")
密切注意 键——它应该与着色器修改器中的统一体相同。此外,您不需要自己保留 Material 属性,
setValue
创建了一个强引用。剩下要做的就是将片段着色器修改器设置为 Material :
let shaderModifier =
"""
uniform sampler2D emissionTexture;
vec3 light = _lightingContribution.diffuse;
float lum = max(0.0, 1 - (0.2126*light.r + 0.7152*light.g + 0.0722*light.b));
vec4 emission = texture2D(emissionTexture, _surface.diffuseTexcoord) * lum;
_output.color += emission;
"""
earthMaterial.shaderModifiers = [.fragment: shaderModifier]
这是运动中的着色器修改器的 footage。
请注意,光源必须非常明亮,否则会在“地球”周围看到昏暗的灯光。我必须在您的设置中将
lightNode.light?.intensity
设置为至少 2000,才能按预期工作。您可能想要尝试计算光度并将其应用于发射的方式以获得更好的结果。如果您可能需要它,
_lightingContribution
是片段着色器修改器中可用的结构,它还具有 ambient
和 specular
成员(以下是 Metal
语法):struct SCNShaderLightingContribution {
float3 ambient;
float3 diffuse;
float3 specular;
} _lightingContribution;