在Scenekit中将半透明图像添加为纹理

时间:2015-11-18 12:01:04

标签: ios swift scenekit

当我添加半透明图像(sample)作为SCNNode的纹理时,如何为图像透明的节点指定颜色属性。由于我能够将颜色或图像指定为材质属性,因此无法为节点指定颜色值。有没有办法为材料属性指定颜色和图像,或者是否有解决此问题的方法。

3 个答案:

答案 0 :(得分:3)

如果您要将图片分配到contents材料属性的transparent,则可以将材料transparencyMode更改为.AOne.RGBZero

  • .AOne表示透明度来自图片alpha通道。
  • .RGBZero表示透明度来自图像中的亮度(红色,绿色和蓝色)。

如果没有自定义着色器,则无法将任意颜色配置为透明度。

但是,从样本图像的外观来看,我认为将样本图像分配到transparent材质属性contents并使用.AOne透明模式会给您带来结果你在寻找。

答案 1 :(得分:1)

我将此作为新答案发布,因为它与其他答案不同。

根据您的评论,我了解您希望使用具有透明度的图像作为材质的漫反射内容,但在图像透明的任何地方使用背景颜色。换句话说,您不会将a composite of the image over a color用作漫反射内容。

使用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 ,以便输出颜色(C O )是:

C O = C A + C B *(1 - ɑ A

通过将图像作为漫反射内容传递,并将输出分配给漫反射内容,表达式可以简化为:

C diffuse = C diffuse + C color *(1 - ɑ diffuse
C diffuse + = C color *(1 - ɑ diffuse

通常输出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")

正如您所看到的,第一个解决方案更简单,如果它符合您的需求,我会推荐它。第二种解决方案更复杂,但可以使背景颜色动画化。

答案 2 :(得分:0)

以防有人在将来遇到这个问题...对于某些任务,ricksters解决方案可能是最简单的。就我而言,我想在映射到球体的图像上显示一个网格。我最初将图像合成为一个并应用它们,但随着时间的推移,我变得更加花哨,这开始变得复杂。所以我做了两个球,一个在另一个里面。我将网格放在内部网格上,将图像放在外部网格上并预先安装...

DateTime.TryParseExact("20170131", "yyyyMMdd", nothing, DateTimeStyles.None, orderDateTime)

我很惊讶我没有设置sphereMaterial.transparency,它似乎自动得到了。