Lab 089 – Loading Materials
Exploring a few ways to load materials from Reality Composer Pro.
Overview
In a recent meeting of Office Hours, we were discussing various ways of loading materials from USD files in Reality Composer Pro. Ivan Campos and John Haney pointed out a way to load ShaderGraphMaterial that I had not used.
Get the material from an entity
This method gets the material(s) from the model component of an entity. I’ve used this many times on Step Into Vision. This works for PhysicallyBasedMaterial and for ShaderGraphMaterial
if let material = subject.components[ModelComponent.self]?.materials.first as? PhysicallyBasedMaterial {
mat1 = material
}I’ve used this method to create a material loading process in Project Graveyard. I created a scene to hold all the materials and assigned each one to an entity.

I load the scene and grab the material from each entity. I save these in an @Observable model that I can use throughout the app.
var materialCache: [String: ShaderGraphMaterial] = [:]
func setupMaterialCache(_ content: RealityViewContent) async {
if let materialLoaderEntity = try? await Entity(named: "Loaders/StoneMaterialLoader", in: realityKitContentBundle) {
content.add(materialLoaderEntity)
let materialNames = [
"DefaultConcrete",
"Granite02",
... // all material names
]
// Load each material from the entity
for name in materialNames {
if let matEntity = materialLoaderEntity.findEntity(named: "\(name)_Entity") {
if let material = matEntity.components[ModelComponent.self]?.materials.first as? ShaderGraphMaterial {
materialCache[name] = material
}
matEntity.isEnabled = false
}
}
print("material cache: \(materialCache)")
}
}There is room for improvement here, but this works well for me.
Loading material using initializers
If the material(s) we need to load are ShaderGraphMaterial, then we have another option. We can load the materials directly from files.
- named: keep in mind that this needs to be the pull path to the material in the scene
- from: this is the file name of the scene. You can use a file extension, but don’t need to if the file name is unique.
- in: the RealityKit content bundle
if let material = try? await ShaderGraphMaterial(
named: "/Root/Subject/GlossyRed",
from: "MaterialLoadingLab",
in: realityKitContentBundle
) {
mat2 = material
}Using this with the materials from the Reality Composer Pro content library takes some trial and error. Most materials have the Root prim replaced by the material name.
if let material = try? await ShaderGraphMaterial(
named: "/MaplePlywood",
from: "MaplePlywood",
in: realityKitContentBundle
) {
mat3 = material
}Unfortunately, this second method only works for ShaderGraphMaterial. I haven’t found a way to load PhysicallyBasedMaterial this way because it lacks the initializer.
Video demo
Full Lab Code
struct Lab089: View {
@State var subject = Entity()
@State var mat1: PhysicallyBasedMaterial?
@State var mat2: ShaderGraphMaterial?
@State var mat3: ShaderGraphMaterial?
var body: some View {
VStack {
Spacer()
RealityView { content in
guard let scene = try? await Entity(named: "MaterialLoadingLab", in: realityKitContentBundle) else { return }
content.add(scene)
guard let subject = scene.findEntity(named: "Subject") else { return }
self.subject = subject
// Example 01: Get the first material from an entity Model Component
// This is the only way I know of to load a PhysicallyBasedMaterial from Reality Composer Pro
if let material = subject.components[ModelComponent.self]?.materials.first as? PhysicallyBasedMaterial {
mat1 = material
}
// Example 02: Load a material using name and filename
// named: the full path to the material in the graph
// from: the full filename, may include extension
if let material = try? await ShaderGraphMaterial(
named: "/Root/Subject/GlossyRed",
from: "MaterialLoadingLab",
in: realityKitContentBundle
) {
mat2 = material
}
// Example 03: Load one of the materials from the Content Library
// These materials are provided as USDZ files. Most use the material name as the root prim
if let material = try? await ShaderGraphMaterial(
named: "/MaplePlywood",
from: "MaplePlywood",
in: realityKitContentBundle
) {
mat3 = material
}
}
.realityViewLayoutBehavior(.flexible)
.preferredWindowClippingMargins(.all, 200)
}
.toolbar {
ToolbarItem(
placement: .bottomOrnament,
content: {
HStack {
Button(action: {
if let mat1 = mat1 {
subject.components[ModelComponent.self]?.materials[0] = mat1
}
}, label: {
Text("PBR From Entity")
})
Button(action: {
if let mat2 = mat2 {
subject.components[ModelComponent.self]?.materials[0] = mat2
}
},
label: {
Text("Shader Graph 02")
})
Button(action: {
if let mat3 = mat3 {
subject.components[ModelComponent.self]?.materials[0] = mat3
}
},
label: {
Text("Shader Graph 02")
})
}
})
}
}
}Support our work so we can continue to bring you new examples and articles.
Download the Xcode project with this and many more labs from Step Into Vision.

Follow Step Into Vision