当我添加半透明图像(示例)作为SCNNode的纹理时,如何为图像透明的节点指定颜色属性。由于我可以指定颜色或图像作为材质属性,因此无法指定节点的颜色值。是否有方法为材质属性指定颜色和图像,或者是否有解决此问题的方法。
如果将图像指定给transparent
材质特性的contents
,则可以将材质transparencyMode
更改为.AOne
或.RGBZero
。
.AOne
意味着透明度是从图像阿尔法通道导出的.RGBZero
意味着从图像中的亮度(红色、绿色和蓝色的总和)导出透明度
如果没有自定义着色器,就无法将任意颜色配置为透明。
但是,从示例图像的外观来看,我认为将示例图像指定给transparent
材质属性contents
并使用.AOne
透明度模式会得到您想要的结果。
我将此作为新答案发布,因为它与其他答案不同。
根据您的评论,我理解您希望使用具有透明度的图像作为材质的漫反射内容,但在图像透明的地方使用背景色。换句话说,您不需要使用图像在颜色上的合成作为漫反射内容。
使用UIImage
有几种不同的方法可以实现这种合成图像。最简单也可能是最熟悉的解决方案是创建一个新的UIImage,在颜色上绘制图像。此新图像的大小和比例与您的图像相同,但可能是不透明的,因为它具有纯色背景。
func imageByComposing(image: UIImage, over color: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(image.size, true, image.scale)
defer {
UIGraphicsEndImageContext()
}
let imageRect = CGRect(origin: .zero, size: image.size)
// fill with background color
color.set()
UIRectFill(imageRect)
// draw image on top
image.drawInRect(imageRect)
return UIGraphicsGetImageFromCurrentImageContext()
}
使用此图像作为漫反射材质属性的内容将为您提供所追求的效果。
使用着色器修改器
如果您发现自己必须非常频繁地更改颜色(可能为其设置动画),也可以使用自定义着色器或着色器修改器在颜色上合成图像。
在这种情况下,您希望将图像A合成到颜色B上,以便输出颜色(CO)为:
CO=CA+CB*(1-?A)
通过将图像作为漫反射内容传递,并将输出分配给漫反射内容,表达式可以简化为:
C漫射=C散射+C颜色*(1-?漫反射)
C漫射+=C颜色*(1-?散射)
通常,输出alpha将取决于A和B的alpha,但由于B(颜色)是不透明的(1),因此输出alpha也是1。
这可以写成一个小的着色器修改器。由于这种解决方案的动机是能够更改颜色,因此颜色被创建为可以在代码中更新的统一变量。
// Define a color that can be set/changed from code
uniform vec3 backgroundColor;
#pragma body
// Composit A (the image) over B (the color):
// output = image + color * (1-alpha_image)
float alpha = _surface.diffuse.a;
_surface.diffuse.rgb += backgroundColor * (1.0 - alpha);
// make fully opaque (since the color is fully opaque)
_surface.diffuse.a = 1.0;
然后将从文件中读取该着色器修改器,并在材质着色器修改器字典中进行设置
enum ShaderLoadingError: ErrorType {
case FileNotFound, FailedToLoad
}
func shaderModifier(named shaderName: String, fileExtension: String = "glsl") throws -> String {
guard let url = NSBundle.mainBundle().URLForResource(shaderName, withExtension: fileExtension) else {
throw ShaderLoadingError.FileNotFound
}
do {
return try String(contentsOfURL: url)
} catch {
throw ShaderLoadingError.FailedToLoad
}
}
// later, in the code that configures the material ...
do {
let modifier = try shaderModifier(named: "Composit") // the name of the shader modifier file (assuming 'glsl' file extension)
theMaterial.shaderModifiers = [SCNShaderModifierEntryPointSurface: modifier]
} catch {
// Handle the error here
print(error)
}
然后,您可以通过为材质的"backgroundColor"设置一个新值来更改颜色。请注意,没有初始值,因此必须设置一个初始值。
let backgroundColor = SCNVector3Make(1.0, 0.0, 0.7) // r, g, b
// Set the color components as an SCNVector3 wrapped in an NSValue
// for the same key as the name of the uniform variable in the sahder modifier
theMaterial.setValue(NSValue(SCNVector3: backgroundColor), forKey: "backgroundColor")
正如你所看到的,第一种解决方案更简单,如果它适合你的需求,我会推荐它。第二种解决方案比较复杂,但可以设置背景颜色的动画。
万一将来有人遇到这个。。。对于某些任务,ricksters解决方案可能是最简单的。在我的例子中,我想在映射到球体的图像顶部显示一个网格。我最初把这些图像合成一个并应用它们,但随着时间的推移,我变得越来越花哨,这开始变得复杂。所以我做了两个球体,一个在另一个里面。我把网格放在里面,把图像放在外面,然后。。。
let outSphereGeometry = SCNSphere(radius: 20)
outSphereGeometry.segmentCount = 100
let outSphereMaterial = SCNMaterial()
outSphereMaterial.diffuse.contents = topImage
outSphereMaterial.isDoubleSided = true
outSphereGeometry.materials = [outSphereMaterial]
outSphere = SCNNode(geometry: outSphereGeometry)
outSphere.position = SCNVector3(x: 0, y: 0, z: 0)
let sphereGeometry = SCNSphere(radius: 10)
sphereGeometry.segmentCount = 100
sphereMaterial.diffuse.contents = gridImage
sphereMaterial.isDoubleSided = true
sphereGeometry.materials = [sphereMaterial]
sphere = SCNNode(geometry: sphereGeometry)
sphere.position = SCNVector3(x: 0, y: 0, z: 0)
我很惊讶,我不需要设置sphereMaterial.transparency,它似乎会自动得到这个。