SceneKit的着色器修改器非常适合这种任务。
您可以在此处查看最终结果的镜头。
片段着色器修改器
![_lightingContribution.diffuse的视觉表示和着色器修改器的最终结果](https://istack.dev59.com/OmFCr.webp)
我们可以使用_lightingContribution.diffuse
(代表应用于漫反射的光照的RGB(vec3
)颜色),来确定一个对象(在本例中为地球)的被照亮区域,并将其用于在片段着色器修改器中遮罩发射纹理。
您可以随意使用它,这是我想出的最简单的解决方案(使用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))
vec4 emission = texture2D(emissionTexture, _surface.diffuseTexcoord) * lum
_output.color += emission
- 计算_lightingContribution.diffuse颜色的亮度(使用此处的公式)(如果光照不是纯白色)
- 从1中减去它,以获得“暗面”的亮度
- 使用漫反射UV坐标从自定义纹理获取发射,并通过乘法应用亮度
- 将其添加到最终输出颜色中(与常规发射应用方式相同)
着色器部分就这样,现在让我们看看Swift方面的事情。
Swift设置
首先,我们不会使用材质的emission.contents
属性,而是需要创建一个自定义的SCNMaterialProperty
。
let emissionTexture = UIImage(named: "earthEmission.jpg")!
let emission = SCNMaterialProperty(contents: emissionTexture)
使用setValue(_:forKey:)
将其设置为材料
earthMaterial.setValue(emission, forKey: "emissionTexture")
注意:关注关键点 - 它应该与着色器修饰符中的统一变量相同。此外,您不需要自己持久化材质属性,setValue
会创建一个强引用。
现在只需将片段着色器修改器设置为材质即可:
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]
以下是这个着色器修改器的运动镜头视频。
请注意,光源必须非常亮,否则“球体”周围会出现暗淡的灯光。在您的设置中,我不得不将lightNode.light?.intensity
设置为至少2000才能按预期工作。您可能需要尝试计算发光度并将其应用于发射以获得更好的结果。
如果您需要,_lightingContribution
是片段着色器修改器中可用的结构,还具有ambient
和specular
成员(下面是Metal
语法):
struct SCNShaderLightingContribution {
float3 ambient;
float3 diffuse;
float3 specular;
} _lightingContribution;