ZOA Rendering HDRP
The High Definition Render Pipeline adapter, with preview contexts, mapping validation, and a documented URP-to-HDRP delta list.
HDRP is the second adapter behind Rendering Core's contracts, and it carries more than URP does. Beyond the material and effect resolvers it ships a preview service, a validation service, and a catalogue of documented behavioural differences between the two pipelines. Moving a project to HDRP is a migration, and a migration needs a list of what will change.
The mapping tables mirror URP's ids one for one. The same fifteen material semantic ids and the same twelve effect ids resolve here, just to HDRP/Lit materials and to VFX Graph assets. Because the id sets stay aligned, swapping pipelines is a registry change and leaves authored content alone.
The core assembly is engine-free. It carries no HDRP reference, so a project that does not install HDRP still compiles and unit-tests the mappings and the fog conversion maths. The Unity-side applier that drives HDRP volumes is gated on HDRP presence through versionDefines and compiles only where the pipeline is installed.
Depends on (2)
Depended on by (0)
How it works
Concepts
The same adapter shape, a different answer
HdrpRenderPipelineAdapter implements IRenderPipelineAdapter, reports "hdrp" and "High Definition Render Pipeline", and constructs an HdrpMaterialVariantResolver. Registering it with RenderPipelineAdapterRegistry and calling SetActive is all it takes to redirect every semantic resolution in the project.
HdrpMaterialMapping carries a shader name alongside the asset path, so a mapping can point at HDRP/Lit today and at HDRP/Hair or HDRP/Eye where a project needs the specialised shaders. HdrpFxMapping is wider than its URP counterpart: it holds both a VFX Graph asset path and a particle prefab path, because HDRP effects commonly exist in both forms. TryResolve returns the VFX Graph path, with the prefab available through GetMapping when the caller needs it.
Availability is a static switch here too: a test calls HdrpRenderPipelineAdapter.SetAvailability(false) to drive RenderingValidator into its RENDER_003 branch.
Previews as leased contexts
HdrpRenderPreviewService implements IRenderPreviewService over an internal dictionary of active contexts. CreatePreview mints a GUID-keyed PreviewContext for a visual profile and records it; ReleasePreview removes it from the dictionary and disposes it, which flips the context's IsActive to false.
The service is strict about preconditions and forgiving about cleanup. CreatePreview throws InvalidOperationException when HDRP is not available and ArgumentException when the profile id is invalid, but ReleasePreview accepts null without complaint. GetActiveContextCount and ReleaseAllPreviews exist so tooling can assert it is not leaking previews between editor sessions.
Validation walks every mapping it has
HdrpValidationService takes both resolvers in its constructor and validates by round-tripping. ValidateMappings iterates every registered material and effect id, resolves it, and records an issue when resolution fails or comes back with an empty asset path. The four codes it can produce are MATERIAL_RESOLUTION_FAILED, MATERIAL_EMPTY_PATH, FX_RESOLUTION_FAILED, and FX_EMPTY_PATH.
HdrpValidationReport wraps the resulting issues with the questions a build step actually asks: IsValid when there are no errors, HasWarnings, ErrorCount, WarningCount, and a ToString summary. ValidateVisualProfile is narrower, checking the profile id and warning when breaking visual deltas are on record that the profile has not been reviewed against.
Visual deltas are documentation with a severity
The validation service seeds itself with nine HdrpVisualDelta entries covering ambient occlusion, global illumination, shadows, particle transparency, post-processing, subsurface scattering, hair, eyes, and cloth. Each records the URP behaviour, the HDRP behaviour, a severity, and a workaround.
Severity is the useful part. Cosmetic means the difference does not affect gameplay, Functional means it may affect play or experience, and Breaking means content or code has to change. Global illumination is the one entry marked Breaking, and ValidateVisualProfile raises PROFILE_HAS_BREAKING_DELTAS as a warning on any profile that has not been reviewed against it.
AddVisualDelta and ClearVisualDeltas extend or reset the list. ClearVisualDeltas restores the nine defaults; it does not leave the catalogue empty.
Fog conversion is maths, and it is tested
HDRP expresses fog density as a mean free path: the distance in metres at which transmittance falls to roughly one over e. URP and the built-in pipeline use a density coefficient instead, so applying a pipeline-neutral VisualEnvironmentState means converting between the two.
HdrpEnvironmentMapping.TryGetMeanFreePath is that conversion, and it lives in the engine-free assembly so it can be unit-tested without HDRP installed. For both exponential modes the mean free path is the inverse of the density, since both reach one over e at one over d. Linear mode is approximated as the end distance divided by three. The value is clamped between MinMeanFreePathMeters of 1 and MaxMeanFreePathMeters of 5000, and the method returns false when the state authors no applicable fog so the caller leaves the HDRP override untouched.
The HDRP applier owns more than URP's does
Under HDRP the pipeline-agnostic RenderSettings pass drives neither fog nor exposure, so HdrpVisualEnvironmentApplier covers both. It registers itself before scene load when GraphicsSettings.currentRenderPipeline is an HDRenderPipelineAsset, and owns a global volume named "[ZOA] VisualEnvironment (HDRP)" at priority 1000 carrying ColorAdjustments and Fog overrides.
Exposure maps to ColorAdjustments.postExposure, not to HDRP's Exposure override. HDRP's Exposure in Fixed mode sets an absolute EV100, which would misread ExposureEv's offset intent. ColorAdjustments shares URP's offset semantic, so the value maps one to one across both pipelines.
Two things are documented as unmapped. Ambient is skipped because HDRP derives ambient from its sky and static lighting sky, not from RenderSettings.ambientLight. The semantic skybox id is skipped because HDRP sky is volume-authored.
In the editor
Screens
Screenshot pending
/screenshots/rendering-hdrp-visual-deltas.png
The Workbench with the HDRP module selected and Visual Deltas active, showing several delta entries including the Breaking global-illumination row, each with its URP behaviour, HDRP behaviour, severity, and workaround visible.
Screenshot pending
/screenshots/rendering-hdrp-setup-wizard.png
The wizard embedded in the Workbench at Step 1 of 3, with the adapter status text, the Register HDRP Adapter button, and the What This Does info box explaining registration against RenderPipelineAdapterRegistry.
Setup
Workflow
- 01
Register the HDRP adapter
Run the first step of the HDRP Setup Wizard, or call RenderPipelineAdapterRegistry.Register with a new HdrpRenderPipelineAdapter and follow it with SetActive("hdrp") from your own bootstrap.
- 02
Review the mapping tables
The wizard's second step reports the counts of default material and effect mappings; the Workbench Material Mappings capability lists them individually. Register your own HdrpMaterialMapping and HdrpFxMapping rows for the ids whose baseline paths do not match your content.
- 03
Read the visual deltas before migrating content
Construct an HdrpValidationService with both resolvers and call GetVisualDeltas, or open the Visual Deltas capability in the Workbench. Schedule the Breaking entry on global illumination as work. The rest tell you where HDRP will look different from the URP build you were tuning against.
- 04
Validate the mappings
Call ValidateMappings from a build step and treat IsValid as the gate. It resolves every registered id, so an empty path or a mapping that fails to round-trip is caught before content depends on it.
- 05
Let the environment applier attach itself
Fog and exposure need no wiring. When HDRP is the active pipeline, HdrpVisualEnvironmentApplier registers itself with the visual environment service before scene load, and applying a VisualEnvironmentState with HasFog or HasExposure drives the volume from there.
Surface
Key types
HdrpRenderPipelineAdapter
class
The IRenderPipelineAdapter implementation for HDRP. Reports "hdrp", constructs an HdrpMaterialVariantResolver, and exposes a static availability switch.
- string PipelineId => "hdrp"
- string DisplayName => "High Definition Render Pipeline"
- bool IsAvailable { get; }
- IMaterialVariantResolver MaterialResolver { get; }
- static void SetAvailability(bool available)
HdrpMaterialVariantResolver
class
Resolves MaterialSemanticId values to HDRP material paths, seeded from DefaultHdrpMappings and extensible through RegisterMapping.
- bool TryResolve(MaterialSemanticId semanticId, out ResolvedMaterial result)
- IReadOnlyList<MaterialSemanticId> RegisteredIds { get; }
- void RegisterMapping(HdrpMaterialMapping mapping)
- HdrpMaterialMapping GetMapping(string semanticId)
HdrpFxResolver
class
The IFxResolver implementation for HDRP. TryResolve returns the VFX Graph asset path; the paired particle prefab path stays reachable through GetMapping.
- bool TryResolve(FxSemanticId semanticId, out ResolvedFx result)
- IReadOnlyList<FxSemanticId> RegisteredIds { get; }
- void RegisterMapping(HdrpFxMapping mapping)
- HdrpFxMapping GetMapping(string semanticId)
HdrpFxMapping
class
One effect row, wider than URP's: semantic id, VFX Graph asset path, particle prefab path, and a note. All three required fields are validated in the constructor.
- string SemanticId { get; }
- string HdrpVfxAssetPath { get; }
- string HdrpParticlePrefabPath { get; }
- string Notes { get; }
HdrpMaterialMapping
class
One material row: semantic id, HDRP shader name, asset path, and a note. The shader name lets a mapping opt into HDRP's specialised shaders instead of HDRP/Lit.
- string SemanticId { get; }
- string HdrpShaderName { get; }
- string HdrpAssetPath { get; }
- string Notes { get; }
IHdrpValidationService
interface
The validation contract: check every mapping, check one visual profile, and read the documented URP-to-HDRP deltas.
- HdrpValidationReport ValidateMappings()
- IReadOnlyList<HdrpValidationIssue> ValidateVisualProfile(string profileId)
- IReadOnlyList<HdrpVisualDelta> GetVisualDeltas()
HdrpValidationService
service
Round-trips every registered material and effect id through its resolver and reports failures and empty paths. Seeds nine default visual deltas; ClearVisualDeltas restores those defaults instead of emptying the list.
- HdrpValidationReport ValidateMappings()
- IReadOnlyList<HdrpValidationIssue> ValidateVisualProfile(string profileId)
- void AddVisualDelta(HdrpVisualDelta delta)
- void ClearVisualDeltas()
HdrpValidationReport
class
The result of a mapping validation pass, shaped for a build gate: an immutable issue list plus IsValid, HasWarnings, and the two counts.
- IReadOnlyList<HdrpValidationIssue> Issues { get; }
- bool IsValid { get; }
- bool HasWarnings { get; }
- int ErrorCount { get; }
- int WarningCount { get; }
- static HdrpValidationReport CreateValid()
HdrpValidationIssue
struct
One finding: a Warning or Error severity, a stable Code such as MATERIAL_EMPTY_PATH, a message, and the semantic id it concerns.
- HdrpValidationSeverity Severity { get; }
- string Code { get; }
- string Message { get; }
- string SemanticId { get; }
HdrpVisualDelta
class
A documented behavioural difference between the pipelines, with the URP behaviour, the HDRP behaviour, a Cosmetic, Functional, or Breaking severity, and a workaround.
- string Feature { get; }
- string UrpBehavior { get; }
- string HdrpBehavior { get; }
- VisualDeltaSeverity Severity { get; }
- string Workaround { get; }
HdrpRenderPreviewService
service
IRenderPreviewService for HDRP. Leases GUID-keyed PreviewContexts, refuses to create one when the adapter reports unavailable, and can report or release everything it is holding.
- bool IsSupported { get; }
- PreviewContext CreatePreview(VisualProfileId profileId)
- void ReleasePreview(PreviewContext context)
- int GetActiveContextCount()
- void ReleaseAllPreviews()
HdrpEnvironmentMapping
class
Engine-free conversion from pipeline-neutral fog intent to HDRP's mean-free-path distance, clamped between 1 and 5000 metres. Returns false when the state authors no applicable fog.
- const float MinMeanFreePathMeters = 1f
- const float MaxMeanFreePathMeters = 5000f
- static bool TryGetMeanFreePath(in VisualEnvironmentState state, out float meters)
HdrpVisualEnvironmentApplier
class
The HDRP slice of the environment pipeline. Owns a global volume with ColorAdjustments and Fog overrides, driving post-exposure and fog albedo plus mean free path. Ambient and skybox are documented as unmapped.
- string ApplierId => "hdrp.environment"
- void Apply(in VisualEnvironmentState state)
DefaultHdrpMappings
class
The baseline tables: fifteen HDRP/Lit material mappings and twelve effect mappings, using the same semantic ids as the URP defaults.
- static IReadOnlyList<HdrpMaterialMapping> GetDefaultMaterialMappings()
- static IReadOnlyList<HdrpFxMapping> GetDefaultFxMappings()
Usage
Examples
using ZOA.Rendering.Core.Models;
using ZOA.Rendering.Core.Registry;
using ZOA.Rendering.Hdrp.Core.Runtime;
var adapter = new HdrpRenderPipelineAdapter();
RenderPipelineAdapterRegistry.Register(adapter);
RenderPipelineAdapterRegistry.SetActive(adapter.PipelineId);
if (adapter.MaterialResolver.TryResolve(new MaterialSemanticId("player.skin"), out var skin))
{
// skin.AssetPath points at the HDRP subsurface-scattering skin material.
// skin.Pipeline == RenderPipelineTarget.HDRP
}using ZOA.Rendering.Hdrp.Core.Models;
using ZOA.Rendering.Hdrp.Core.Runtime;
var validation = new HdrpValidationService(
new HdrpMaterialVariantResolver(),
new HdrpFxResolver());
var report = validation.ValidateMappings();
if (!report.IsValid)
{
foreach (var issue in report.Issues)
UnityEngine.Debug.LogError(issue.Code + " on " + issue.SemanticId + ": " + issue.Message);
}
foreach (var delta in validation.GetVisualDeltas())
{
if (delta.Severity == VisualDeltaSeverity.Breaking)
UnityEngine.Debug.LogWarning(delta.Feature + " needs migration work: " + delta.Workaround);
}using ZOA.Rendering.Core.Models;
using ZOA.Rendering.Hdrp.Core.Runtime;
var adapter = new HdrpRenderPipelineAdapter();
var previews = new HdrpRenderPreviewService(adapter);
if (previews.IsSupported)
{
var context = previews.CreatePreview(new VisualProfileId("weapon.showcase"));
// ... render the preview ...
previews.ReleasePreview(context); // context.IsActive is now false
}
// Editor teardown: assert nothing leaked.
previews.ReleaseAllPreviews();using ZOA.Rendering.Core.Environment;
using ZOA.Rendering.Hdrp.Core.Runtime;
var state = new VisualEnvironmentState
{
HasFog = true,
FogEnabled = true,
FogMode = VisualEnvironmentFogMode.Exponential,
FogDensity = 0.02f,
};
// 1 / 0.02 = 50 m, inside the 1 to 5000 m clamp.
if (HdrpEnvironmentMapping.TryGetMeanFreePath(in state, out var meanFreePath))
{
// meanFreePath == 50f — feed it to the HDRP Fog override.
}Tooling
Editor tools
HDRP Setup Wizard
ZOA Workbench > HDRP > Setup Wizard
A guided three-step process: Adapter Registration registers an HdrpRenderPipelineAdapter with the global registry, Material Mappings reports the default material and effect mapping counts, and Validation checks the adapter and its mappings. Show routes into the Workbench rather than opening a floating window.
HDRP Workbench module
ZOA Workbench > HDRP
Registered as "rendering-hdrp" in the workflow.rendering lane at order 53. Five capabilities: the embedded setup wizard, Material Mappings, Visual Deltas listing the URP-to-HDRP differences and their workarounds, Validation, and an Overview of the adapter architecture.
Read this
Notes and caveats
See also