I have an Entity called "Table" that represents a table with a sticker on it. This Entity has Model Entities as children (leg1, leg2, leg3, leg4, plank and sticker). Each ModelEntity has a PBR texture applied to its materials ('lightWood' for the legs, darkWood for the plank and an image for the sticker placed on top of the table).
I would like to create a 'ghost' / transparent version of the Table entity to show before the user places it in the scene.
Is there a simple way to set the opacity of materials before or after they are created?
Is what I am trying possible without having to create a Metal shader (which I don't know how to do)?
Material for the Sticker Model Entity
// Setup image material
var pictureMaterial = PhysicallyBasedMaterial()
var imageTexture: TextureResource? = nil
if let cgImage = image?.cgImage,
let resource = try? TextureResource.generate(from: cgImage, options: TextureResource.CreateOptions.init(semantic: nil)) {
imageTexture = resource
}
let baseColor = MaterialParameters.Texture(imageTexture ?? StickerEntity.defaultImage)
pictureMaterial.baseColor = PhysicallyBasedMaterial.BaseColor(texture:baseColor)
self.stickerEntity.components[ModelComponent.self] =
ModelComponent(mesh: .generateBox(width: width!,
height: self.pictureDepth,
depth: height!,
cornerRadius: 0,
splitFaces: true),
materials: [canvasMaterial,
pictureMaterial,
canvasMaterial,
canvasMaterial,
canvasMaterial,
canvasMaterial
])
Function I use to create PBR materials for the legs and plank
static func generateMaterialFromImages(uiColor: UIColor? = nil,
baseColor: String? = nil,
normal: String? = nil,
roughness: String? = nil,
ambientOcclusion: String? = nil,
metallic: String? = nil,
clearcoat: String? = nil,
emissiveColor: String? = nil) -> PhysicallyBasedMaterial {
var material = PhysicallyBasedMaterial()
if let color = uiColor {
material.baseColor = PhysicallyBasedMaterial.BaseColor(tint: color)
} else if let resourceFileName = baseColor, let resource = try? TextureResource.load(named: resourceFileName) {
let baseColor = MaterialParameters.Texture(resource)
material.baseColor = PhysicallyBasedMaterial.BaseColor(texture: baseColor)
}
if let resourceFileName = normal, let resource = try? TextureResource.load(named: resourceFileName) {
let normal = MaterialParameters.Texture(resource)
material.normal = PhysicallyBasedMaterial.Normal(texture:normal)
}
if let resourceFileName = roughness, let resource = try? TextureResource.load(named: resourceFileName) {
let roughness = MaterialParameters.Texture(resource)
material.roughness = PhysicallyBasedMaterial.Roughness(texture:roughness)
}
if let resourceFileName = ambientOcclusion, let resource = try? TextureResource.load(named: resourceFileName) {
let ambientOcclusion = MaterialParameters.Texture(resource)
material.ambientOcclusion = PhysicallyBasedMaterial.AmbientOcclusion(texture:ambientOcclusion)
}
if let resourceFileName = metallic, let resource = try? TextureResource.load(named: resourceFileName) {
let metallic = MaterialParameters.Texture(resource)
material.metallic = PhysicallyBasedMaterial.Metallic(texture:metallic)
}
if let resourceFileName = clearcoat, let resource = try? TextureResource.load(named: resourceFileName) {
let clearcoat = MaterialParameters.Texture(resource)
material.clearcoat = PhysicallyBasedMaterial.Clearcoat(texture:clearcoat)
}
if let resourceFileName = emissiveColor, let resource = try? TextureResource.load(named: resourceFileName) {
let emissiveColor = MaterialParameters.Texture(resource)
material.emissiveColor = PhysicallyBasedMaterial.EmissiveColor(texture:emissiveColor)
}
return material
}
I have looked for documentation or functions that would allow to change the opacity of materials but couldn't find any documentation on the matter.
I used usdz
table model found in Reality Composer's Furnishing
section (iconic look).
Here's how I changed an opacity of PBR material.
import UIKit
import RealityKit
class ViewController : UIViewController {
@IBOutlet var arView: ARView!
override func viewDidLoad() {
super.viewDidLoad()
let tableScene = try! Experience.loadTable()
if let table = tableScene.findEntity(named: "table_end_base_iconic_lod0")
as? ModelEntity {
table.model?.materials[0] = semiTransparentShader(0.5)
table.setPosition([0,-0.4, 0], relativeTo: nil)
}
arView.scene.anchors.append(tableScene)
}
func semiTransparentShader(_ value: Float) -> Material {
var material = PhysicallyBasedMaterial()
material.baseColor.texture = try! .init(.load(named: "image", in: nil))
material.blending = .transparent(opacity: .init(floatLiteral: value))
return material
}
}
P. S.
In visionOS, to create a semi-transparent material has become even easier than before:
table.components[OpacityComponent.self] = .init(opacity: 0.5)