I have written this function:
@available(macOS 26.0, *)
func instancing() async -> Entity {
let entity = Entity()
do {
// 1. Create a CustomMaterial
let library = offscreenRenderer.pointRenderer!.device.makeDefaultLibrary()!
let surfaceShader = CustomMaterial.SurfaceShader(
named: "surfaceShaderWithCustomUniforms", // This must match the function name in Metal
in: library
)
let instanceCount = 10
// No idea how to actually use this...
// let bufferSize = instanceCount * MemoryLayout<UInt32>.stride
//
// // Create the descriptor
// var descriptor = LowLevelBuffer.Descriptor(capacity: bufferSize, sizeMultiple: MemoryLayout<UInt32>.stride)
//
// // Initialize the buffer
// let lowLevelBuffer = try LowLevelBuffer(descriptor: descriptor)
// lowLevelBuffer.withUnsafeMutableBytes { rawBytes in
// // Bind the raw memory to the UInt32 type
// let pointer = rawBytes.bindMemory(to: UInt32.self)
// pointer[1] = 0xff_0000
// pointer[0] = 0x00_ff00
// pointer[2] = 0x00_00ff
// pointer[3] = 0xff_ff00
// pointer[4] = 0xff_00ff
// pointer[5] = 0x00_ffff
// pointer[6] = 0xff_ffff
// pointer[7] = 0x7f_0000
// pointer[8] = 0x00_7f00
// pointer[9] = 0x00_007f
// }
var material = try CustomMaterial(surfaceShader: surfaceShader, lightingModel: .lit)
material.withMutableUniforms(ofType: SurfaceCustomUniforms.self, stage: .surfaceShader) { params, resources in
params.argb = 0xff_0000
}
// 2. Create the ModelComponent (provides the MESH and MATERIAL)
let mesh = MeshResource.generateSphere(radius: 0.5)
let modelComponent = ModelComponent(mesh: mesh, materials: [material])
// 3. Create the MeshInstancesComponent (provides the INSTANCE TRANSFORMS)
let instanceData = try LowLevelInstanceData(instanceCount: instanceCount)
instanceData.withMutableTransforms { transforms in
for i in 0..<instanceCount {
let instanceAngle = 2 * .pi * Float(i) / Float(instanceCount)
let radialTranslation: SIMD3<Float> = [-sin(instanceAngle), cos(instanceAngle), 0] * 4
// Position each sphere around a circle.
let transform = Transform(
scale: .one,
rotation: simd_quatf(angle: instanceAngle, axis: [0, 0, 1]),
translation: radialTranslation
)
transforms[i] = transform.matrix
}
}
let instancesComponent = try MeshInstancesComponent(mesh: mesh, instances: instanceData)
// 4. Attach BOTH to the same entity
entity.components.set(modelComponent)
entity.components.set(instancesComponent)
} catch {
print("Failed to create mesh instances: \(error)")
}
return entity
}
and this is the corresponding Metal shader
typedef struct {
uint32_t argb;
} SurfaceCustomUniforms;
[[stitchable]]
void surfaceShaderWithCustomUniforms(realitykit::surface_parameters params,
constant SurfaceCustomUniforms &customParams)
{
half3 color = {
static_cast<half>((customParams.argb >> 16) & 0xff),
static_cast<half>((customParams.argb >> 8) & 0xff),
static_cast<half>(customParams.argb & 0xff) };
params.surface().set_base_color(color);
}
which works well and generates 10 red spheres. While listening to the WWDC25 presentation on what's new in RealityKit I am positive to hear the presenter saying that it is possible to customise each instance using a LowLevelBuffer, but so far all my attempts have failed.
Is it possible, and if so how ?
Thanks for reading and for your help.
Kind regards, Christian