Learn how to build node-based materials in Reality Composer Pro's Shader Graph, from a basic surface to a parameterized material your Swift code can drive at runtime.
Reality Composer Pro includes a node-based material editor commonly called Shader Graph. Instead of hand-writing shader code, you connect nodes to define how a surface looks.
This matters for two reasons. First, it is far more approachable for designers and generalist engineers who do not want to maintain low-level shader code.
Second, it stays inside Apple's pipeline. The materials you build preview closely to how RealityKit renders them, so you spend less time reconciling differences between an editor and the runtime.
You can still expose parameters that your Swift code changes at runtime, which gives you the best of both worlds: visual authoring plus dynamic control.
Start by selecting the entity you want to shade in your scene. In the inspector you can create or assign a material to it.
If the model arrived with materials, you can edit those or replace them. If it has none, create a fresh material to work from.
Give the material a clear, descriptive name. When a scene grows, generic names like Material_1 become impossible to track, whereas names tied to purpose stay readable.
With a material assigned, open it in the Shader Graph editor so you can start building the node network that defines its appearance.
A Shader Graph is a network of nodes that ultimately feed a surface output. That output is what the renderer uses to shade the object.
Each node performs a small operation: producing a color, sampling a texture, doing math, or reading geometric information. You wire outputs of one node into inputs of another.
The key mental model is data flowing left to right toward the final surface. Base color, roughness, metallic, normal, and similar properties are the inputs you care about most for a physically based look.
Spend a moment identifying the surface node and its inputs before you build anything. Everything you add exists to feed those inputs meaningfully.
Begin simply. Feed a constant color into the base color input and set reasonable roughness and metallic values to get a clean, predictable surface.
Preview immediately. A flat, correctly lit color confirms your graph is connected properly before you add complexity.
From there, introduce a texture. Add a node that samples an image and route it into base color so the surface shows detail instead of a flat tint.
Build incrementally and preview after each change. If the surface suddenly turns black or wrong, you will know exactly which connection caused it, which is far easier than debugging a large graph all at once.
Real materials usually combine several maps. A color or albedo map drives base color, a roughness map varies how sharp reflections are, and a normal map adds surface detail without extra geometry.
Wire each map into its matching input. Getting the normal map into the normal input, rather than base color, is a frequent thing to double-check.
Use math and adjustment nodes to fine-tune. You can scale, tint, or blend values to dial in the look without re-exporting textures from another tool.
Mind your texture coordinates. If a texture appears stretched or misplaced, the UV handling in the graph or the model's UVs are usually the cause, and fixing it here saves a round trip to your DCC software.
The real power comes from promoting values to parameters. Instead of hard-coding a color or a strength, expose it so it can be changed from outside the graph.
This lets your Swift code drive the material at runtime. For example, you might change a tint based on app state, or animate a value to create a pulsing effect.
Name parameters clearly and keep them minimal. Every exposed parameter is a contract your code depends on, so a tidy, well-named set is easier to maintain than a sprawling one.
Confirm the exact API for reading and writing these parameters against the current RealityKit documentation, since the mechanism can evolve between releases.
Use the built-in preview aggressively while authoring. Because the tool shares rendering foundations with RealityKit, it is a strong predictor of the final result.
Still, preview is not a guarantee. Lighting, environment, and hardware differences mean you should verify important materials on an actual target when you can.
This is especially true for subtle effects like roughness gradients or normal detail, which can read differently under real lighting than in a neutral editor viewport.
Budget time for this verification pass. It is cheaper to catch a material that looks wrong on device now than after it ships inside a larger experience.
As a material grows, a tidy graph is the difference between something you can maintain and a tangle you dread reopening.
Group related nodes and give them clear labels so the intent of each section is obvious at a glance. When you return weeks later, readable structure saves real time.
Think about reuse as well. If several objects share a look, a single well-built material with exposed parameters is easier to maintain than copies that drift out of sync over time.
Also keep performance in mind as you organize. Fewer, well-chosen nodes generally render more efficiently than a sprawling network, and a clean graph makes it easier to spot the expensive parts you might simplify before shipping.
Adopt a consistent left-to-right flow as a habit, with inputs entering on one side and the surface output on the other. A predictable layout means anyone on the team can read the material without untangling crossed wires, and it makes future edits a matter of following the flow rather than reverse-engineering it.
The most common mistake is routing a texture into the wrong input, especially normal maps landing in base color. Label your nodes and trace connections deliberately.
Another is color space confusion. Color textures and data textures like roughness or normals are handled differently, and mixing them up produces washed-out or oddly lit surfaces.
A third is over-building. Huge graphs are hard to debug and can cost performance, so prefer the simplest network that achieves the look.
Finally, remember scope. Shader Graph defines appearance; it does not build or ship your app. You still assemble everything in Xcode and distribute through the Apple Developer Program, and complex source textures still come from dedicated texturing tools.
No. Shader Graph is node-based, so you build materials by connecting nodes rather than writing shader code. That said, understanding physically based rendering inputs like roughness and metallic helps you get good results.
Yes. You can expose values as parameters in the graph and drive them from Swift using RealityKit. Confirm the exact API against current documentation, since it can change between releases.
Often the texture is wired into the wrong input or has a color-space mismatch, or a required input is left disconnected. Build incrementally and preview after each change to isolate the offending connection.
This usually comes from UV coordinate handling in the graph or from the model's UV layout. Check the texture coordinate nodes first, and fix the UVs in your modeling tool if needed.
No. It builds and controls materials inside Apple's pipeline, but authoring complex source textures is still done in dedicated painting or texturing software and imported in.