ZOA Rendering URP
The Universal Render Pipeline adapter: semantic ids resolved to URP shaders, prefabs, and post-exposure.
Rendering Core declares what an adapter is. A Universal Render Pipeline project registers this package to satisfy that contract. UrpRenderPipelineAdapter reports the id "urp", the display name "Universal Render Pipeline", and hands back a resolver seeded with the project's default URP mappings.
URP is the Foundry baseline, and the package keeps a narrow scope. Material mappings target Universal Render Pipeline/Lit, effect mappings point at prefabs rather than VFX Graph assets, and renderer features stay authored on the URP renderer asset where Unity expects them. The Workbench module surfaces detection, registration, and validation; URP configuration itself stays in Unity's own inspectors.
The runtime assembly stays a semantic layer with no engine graphics calls, so the mappings are unit-testable. The one Unity-facing piece is the exposure applier, which plugs into Rendering Core's visual environment service and owns a lazily created global volume carrying a single ColorAdjustments override.
Depended on by (0)
How it works
Concepts
How the adapter plugs into Rendering Core
UrpRenderPipelineAdapter implements IRenderPipelineAdapter and constructs a UrpMaterialVariantResolver in its constructor. Registering it is two calls against RenderPipelineAdapterRegistry: Register, then SetActive with the PipelineId. From that point every consumer that goes through GetActive resolves URP assets, and no gameplay code changed.
Availability is a static flag with a static setter, SetAvailability. It is a project-wide switch, not a per-instance property, so a test or a headless tool can declare URP unavailable and watch RenderingValidator raise RENDER_003.
The FX side sits off the adapter interface. UrpFxResolver implements IFxResolver and is constructed directly by whoever needs effect resolution, because the core adapter contract promises only a material resolver.
Mappings are data, seeded and then extended
DefaultUrpMappings is a static table of the project's baseline: fifteen material mappings covering weapon, impact, player, and environment surfaces, and twelve effect mappings covering muzzle flashes, tracers, impacts, explosions, smoke, blood, and flashbang light. Each material mapping carries a semantic id, a URP shader name, an asset path, and a note; each effect mapping carries an id, a prefab path, and a note.
Both resolvers seed themselves from those tables in their constructors and then accept RegisterMapping at runtime, keyed by semantic id, so a project extends or overrides the baseline without editing the package. GetMapping returns the raw mapping when a tool wants the shader name or the note rather than the resolved path.
Mapping constructors validate their arguments. A null or whitespace semantic id, shader name, or asset path throws an ArgumentException at construction, so a broken mapping fails on the line that wrote it instead of at the first impact in play mode.
Exposure is the one thing RenderSettings cannot do
Under URP, the pipeline-agnostic RenderSettings pass in Rendering Core already drives ambient and fog, so no hook is needed for either. Exposure is a post-processing concept, and UrpExposureEnvironmentApplier owns that slice.
The applier registers itself at RuntimeInitializeLoadType.BeforeSceneLoad, but only when GraphicsSettings.currentRenderPipeline is actually a UniversalRenderPipelineAsset. In a project where URP is installed but not active, the hook never attaches.
Its volume is created at runtime, never pulled from project assets. On first use it finds or creates a GameObject named "[ZOA] VisualEnvironment (URP)", makes the volume global, sets priority 1000 so environment intent wins over scene-authored volumes, and adds a profile containing only the ColorAdjustments override it manages. Applying a state overrides postExposure with the state's ExposureEv.
Renderer capability is probed by type name
UrpRendererCapabilityUtility probes the project by type name rather than by assembly reference, so the editor tooling compiles whether or not URP is present. It reports whether the URP asset type resolves, whether UniversalRendererData resolves, and counts UniversalRendererData and ScriptableRendererData assets in the project.
The distinction matters when diagnosing a broken setup. The package being installed, the renderer data type being loadable, and renderer assets existing are three separate conditions. The wizard and Workbench module display all four numbers side by side instead of collapsing them into one pass or fail.
In the editor
Screens
Screenshot pending
/screenshots/rendering-urp-setup-wizard.png
The wizard embedded in the Workbench with the step indicator reading Step 2 of 3, the four status rows visible (URP package, Universal Renderer data type, Universal Renderer assets, Scriptable Renderer assets), and the Renderer Features info box below them.
Screenshot pending
/screenshots/rendering-urp-material-mappings.png
The Workbench with the URP module selected and Material Mappings active, scrolled to show several mapping cards each rendering its semantic id in bold with the shader name, asset path, and note underneath.
Setup
Workflow
- 01
Confirm URP is present
Open the URP module in the Workbench and read the Renderer Capabilities panel. It reports whether the URP package and the UniversalRendererData type resolve, and how many Universal Renderer and Scriptable Renderer assets exist in the project. All four lines being populated is the precondition for everything else.
- 02
Register the adapter
Run step one of the setup wizard, or call RenderPipelineAdapterRegistry.Register with a new UrpRenderPipelineAdapter from your own bootstrap. Follow it with SetActive("urp") so RenderingValidator does not raise RENDER_002.
- 03
Point the default mappings at real assets
The baseline table names paths under Assets/Rendering/URP/. Either author materials and prefabs at those paths or register your own UrpMaterialMapping and UrpFxMapping rows over the ids you use, which replaces the seeded entry for that id.
- 04
Validate
The wizard's third step resolves weapon.metal.primary through a fresh adapter and checks that the result reports RenderPipelineTarget.URP, alongside the adapter's own availability. The Workbench validation capability runs the lighter check that the default mapping table is non-empty.
- 05
Leave renderer features where Unity puts them
Renderer features are authored on the URP renderer asset, not through this package. The adapter's job stops at keeping gameplay code free of shader names; the renderer stack stays Unity-authored and inspectable.
Surface
Key types
UrpRenderPipelineAdapter
class
The IRenderPipelineAdapter implementation for URP. Reports "urp", constructs a UrpMaterialVariantResolver, and exposes a static availability switch.
- string PipelineId => "urp"
- string DisplayName => "Universal Render Pipeline"
- bool IsAvailable { get; }
- IMaterialVariantResolver MaterialResolver { get; }
- static void SetAvailability(bool available)
UrpMaterialVariantResolver
class
Resolves MaterialSemanticId values to URP material asset paths. Seeded from DefaultUrpMappings and extensible at runtime.
- bool TryResolve(MaterialSemanticId semanticId, out ResolvedMaterial result)
- IReadOnlyList<MaterialSemanticId> RegisteredIds { get; }
- void RegisterMapping(UrpMaterialMapping mapping)
- UrpMaterialMapping GetMapping(string semanticId)
UrpFxResolver
class
The IFxResolver implementation for URP. Resolves FxSemanticId values to particle prefab paths, seeded from the default effect table.
- bool TryResolve(FxSemanticId semanticId, out ResolvedFx result)
- IReadOnlyList<FxSemanticId> RegisteredIds { get; }
- void RegisterMapping(UrpFxMapping mapping)
- UrpFxMapping GetMapping(string semanticId)
UrpMaterialMapping
class
One material row: semantic id, URP shader name, asset path, and an authoring note. Constructor throws on any blank required field.
- string SemanticId { get; }
- string UrpShaderName { get; }
- string UrpAssetPath { get; }
- string Notes { get; }
UrpFxMapping
class
One effect row: semantic id, particle prefab path, and a note. URP effects resolve to prefabs rather than to a separate graph asset.
- string SemanticId { get; }
- string UrpParticlePrefabPath { get; }
- string Notes { get; }
DefaultUrpMappings
class
The baseline tables both resolvers seed from: fifteen material mappings on Universal Render Pipeline/Lit and twelve effect prefab mappings.
- static IReadOnlyList<UrpMaterialMapping> GetDefaultMaterialMappings()
- static IReadOnlyList<UrpFxMapping> GetDefaultFxMappings()
UrpExposureEnvironmentApplier
class
The URP slice of the visual environment pipeline. Self-registers before scene load when URP is the active pipeline, and drives ExposureEv onto a service-owned global volume's ColorAdjustments.postExposure.
- string ApplierId => "urp.exposure"
- void Apply(in VisualEnvironmentState state)
UrpSetupWizard
component
A three-step EditorWindow hosted inside the Workbench: adapter registration, renderer capability checks, and validation. Show routes into the Workbench rather than floating a separate window.
- const string CapabilityId = "setup-wizard"
- static void Show()
UrpWorkbenchModule
class
The Workbench module for URP, registered under the workflow.rendering lane. Hosts the setup wizard plus renderer-capability, material-mapping, validation, and overview capabilities.
- string Id => "rendering-urp"
- string Title => "URP"
- int Order => 52
Usage
Examples
using ZOA.Rendering.Core.Registry;
using ZOA.Rendering.Urp.Core.Runtime;
var adapter = new UrpRenderPipelineAdapter();
RenderPipelineAdapterRegistry.Register(adapter);
RenderPipelineAdapterRegistry.SetActive(adapter.PipelineId);using ZOA.Rendering.Core.Models;
using ZOA.Rendering.Urp.Core.Models;
using ZOA.Rendering.Urp.Core.Runtime;
var resolver = new UrpMaterialVariantResolver();
// Replaces the seeded entry for this id; other ids keep the defaults.
resolver.RegisterMapping(new UrpMaterialMapping(
"weapon.metal.primary",
"Universal Render Pipeline/Lit",
"Assets/Game/Art/Materials/Rifle_Receiver.mat",
"Project-specific receiver material"));
if (resolver.TryResolve(new MaterialSemanticId("weapon.metal.primary"), out var resolved))
{
// resolved.AssetPath == "Assets/Game/Art/Materials/Rifle_Receiver.mat"
// resolved.Pipeline == RenderPipelineTarget.URP
}using ZOA.Rendering.Core.Models;
using ZOA.Rendering.Urp.Core.Runtime;
var fx = new UrpFxResolver();
if (fx.TryResolve(new FxSemanticId("fx.muzzle.flash.rifle"), out var resolvedFx))
{
// resolvedFx.AssetPath points at the URP prefab for this effect.
// Load and spawn it through your own pooling layer.
}
// The raw mapping still carries the authoring note.
var mapping = fx.GetMapping("fx.bullet.tracer");Tooling
Editor tools
URP Setup Wizard
ZOA Workbench > URP > Setup Wizard
Three steps. Adapter Registration registers a UrpRenderPipelineAdapter with the global registry. Renderer Capabilities reports URP package presence, UniversalRendererData type resolution, and counts of Universal Renderer and Scriptable Renderer assets. Validation resolves a known semantic id through a fresh adapter and reports PASSED or REVIEW, spelling out the four contributing conditions.
URP Workbench module
ZOA Workbench > URP
Registered as "rendering-urp" in the workflow.rendering lane at order 52. Five capabilities: the embedded setup wizard, Renderer Capabilities, Material Mappings (a scrolling list of every default mapping with its shader, asset path, and note), Validation, and an Overview describing the adapter's scope.
Read this
Notes and caveats
See also