cupertino-sample-code/metal-training-a-neural-network-to-render-irradiance-in-real-time
Mihaela Mihaljevic 6a4d8ee491 data(samples): add 15 iOS-27 beta sample projects
Apple Intelligence: appintents calendar/messaging/music/photo, foundationmodels-origami,
evaluations-book-tracker. Plus declaredagerange age-assurance, immersivemediasupport,
metal ML (inline ops, irradiance NN), avfoundation camera, avfaudio sequencer, appkit
state restoration, healthkit heart-rate zones. Extracted from Apple sample archives.
2026-06-20 12:42:17 +02:00
..
Application data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
Configuration data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
Model data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
NeuralRendering.xcodeproj data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
Renderer data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
.gitignore data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
LICENSE.txt data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
pyproject.toml data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
README.md data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
train_irradiance.py data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00
uv.lock data(samples): add 15 iOS-27 beta sample projects 2026-06-20 12:42:17 +02:00

Training a neural network to render irradiance in real time

Train a small neural network on the GPU to approximate diffuse irradiance, and compare the result against Monte Carlo integration and a pre-trained ML model.

Overview

This sample renders a PBR sphere lit by an HDR environment map, computing per-pixel diffuse irradiance using three different techniques that you can switch between at runtime:

  • Monte Carlo integrates the hemisphere per pixel each frame, producing a ground-truth reference.
  • MPP trains a lightweight MLP on the GPU at launch using Metal Performance Primitives cooperative matrix operations, then evaluates it per pixel.
  • ML Encoder dispatches a pre-trained Core ML model through MTL4MachineLearningCommandEncoder.

The renderer uses a three-pass deferred approach. A geometry pass rasterizes world-space normals and material parameters to textures. The selected irradiance technique then takes the normals as input and outputs a tensor with the irradiance values. A composite pass then evaluates direct lighting with a Cook-Torrance BRDF, adds indirect lighting from the irradiance tensor, then tone-maps the result.

Configure the sample code project

To run this sample, you need:

  • A Mac running macOS 26 or later
  • Xcode 26 or later
  • Note: This sample requires Metal 4.

Training on the GPU

When the MPP technique is selected, the sample generates 512 ground-truth normal/irradiance pairs via Monte Carlo integration, then runs multiple training iterations per frame using a three-layer MLP. The MLP has 3 inputs (normal xyz), 2 hidden layers of 16 neurons, and 3 outputs (irradiance rgb). The input layer is padded to 16 elements, the output layer is padded to 8 elements, and irradiance values are converted to and from log space for numerical stability.

The forward pass uses tensor ops and cooperative tensors to evaluate the network cooperatively across a SIMD group, where 32 threads process 32 pixels simultaneously. The backward pass works in a similar way to compute weight gradients and propagate activation gradients through each layer, with Adam as the optimizer.

Training offline with PyTorch

When the ML Encoder technique is selected, a MTL4MachineLearningCommandEncoder is used to execute a pre-trained version of the same MLP network. Unlike the MPP approach, the pre-trained network does not skip empty pixels, so this technique is less efficient yet is much simpler.

The train_irradiance.py script has an implementation of the Monte Carlo integration from Irradiance.metal, and can be used to regenerate the offline model (Irradiance.mtlpackage) using PyTorch. Run it with:

uv run ./train_irradiance.py