UI Shadow
com.zalinteractive.ui-shadow
1 Zal UI Shadow
Zal UI Shadow adds soft outer shadows, inset shadows, and glows to Unity uGUI graphics. It targets Unity 6000.0 and newer and uses Universal Render Pipeline (URP) with RenderGraph.
1.1 Five-minute setup
- Add a concrete uGUI
Graphic, such asImage,RawImage,Text, or TextMesh Pro text. - Add UI > Effects > UI Shadow to the same GameObject.
- Choose Outer or Inset mode and tune Size, Distance, Angle, Spread, and Color.
- Add more UI Shadow components to create layered effects.
Generated renderer objects are transient. Do not edit or save them.
1.2 Compatibility
| Area | Status |
|---|---|
| Unity 6000.0 | Supported |
| Unity 6000.3 | Supported |
| Unity 6000.5 | Supported |
| Unity 2022.3 and earlier | Not supported |
| Universal Render Pipeline with RenderGraph | Required |
| Built-in Render Pipeline or HDRP | Not supported |
| uGUI | Supported |
| UI Toolkit | Not supported |
| TextMesh Pro standard subset | Supported |
| TMP underlay, glow, bevel, or custom shaders | Requires custom integration |
| Unknown standard-like Graphic | Best effort |
| Arbitrary custom shader | Requires explicit capture integration |
| Graphic on its rendering Canvas GameObject in Outer mode | Unsupported; wrap the Graphic |
| Descendant override-sorting Canvas | Outer ordering is limited; a warning is reported |
Runtime targets include mobile, WebGL, PC, and console. Platform texture limits may downscale oversized requests and report the result through diagnostics.
Mutable pixel content, including an in-place modified Texture2D, RenderTexture, or video-like producer, requires explicit invalidation or a Dynamic Caster helper.
1.3 Samples
Import any sample independently from Package Manager:
- Getting Started
- Feature Matrix
- Custom Integration
- Cache and Performance
- Demo Gallery
Each sample contains its own README and assembly definition. TMP examples use the project’s TMP Essential Resources and do not include a font.
1.4 Documentation
The sections below contain the user guide, architecture, public API, TMP support, custom integration, masking, performance, and troubleshooting details.
2 User guide
Add UIShadow beside an enabled uGUI Graphic. Outer mode places a generated sibling behind the caster. Inset mode places a generated child behind user content. Generated objects are hidden, transient implementation details; never edit or serialize them.
Image shadows use the active sprite’s separate alpha texture when Unity supplies one. Changing overrideSprite, returning to the original sprite, or switching to a sprite without separate alpha updates the captured coverage automatically. If external code replaces or changes texture content without changing the sprite, call MarkCasterContentDirty.
Size controls blur support, Spread controls hardness, and Distance plus Angle control the light offset. Zero Size remains a valid hard shadow. Outer Angle and Distance are layout-only and continue sharing the same texture. Inset offsets affect the generated texture.
An inset with positive Size and zero Distance shades every interior edge, including the edges of opaque rectangular Images. Its final output stays within the caster bounds. Both the final output and the surrounding blur working area respect the configured texture dimension and pixel limits; a large inset may therefore reduce capture density to fit those limits.
Applying runtime settings refreshes existing shadows on the next canvas update. Lower dimension and pixel limits also remove incompatible idle textures immediately. Lowering the idle-cache budget evicts the oldest idle textures until the budget fits; textures still used by a shadow remain valid until that shadow updates or releases them. Changes that leave a shadow’s resolved texture unchanged preserve its reuse.
Use Fast for general UI and large radii. Use Accurate for thin lines, small text, or cases where Fast downsampling loses detail. Layer multiple components for ambient/key elevation or colored glows.
Accurate preserves the requested blur support at large sizes, including sizes above 256 capture pixels. It approximates a Gaussian with standard deviation equal to one third of Size. Large radii use progressive area-filtered reduction and bilinear reconstruction, so they can differ slightly from a full-resolution convolution. The working border includes these filter footprints; configured resource limits can reduce capture density for the whole shadow.
Mutable Texture2D, RenderTexture, video, webcam, and procedural GPU producers must call MarkCasterContentDirty, provide an external/key revision, or use UIShadowDynamicCaster. Wrap animated texture properties in BeginTransientUpdates; the interaction helpers do this automatically.
Use finite transition durations for Interaction Animator and Inset On Press. If a duration or supplied time step is NaN or infinity, the helper applies the target appearance immediately instead of leaving an interaction unfinished. Correct the duration or supply valid steps to resume timed animation. Timing warnings appear with the helper warnings in the UI Shadow inspector.
The package requires URP with RenderGraph and supports mobile, WebGL, PC, and console. Inspect downscaling, cache use, warnings, and provider identity in Window > Analysis > UI Shadow Renderer Debugger.
The generation frame identifies when the last successfully adopted texture was generated. Reusing a cached texture preserves its original frame; empty or failed output clears current texture dimensions and memory while keeping that history. The global generation-memory peak describes the latest nonempty generation attempt, including failed attempts and temporary color conversion storage. A smaller generation can lower this value; cache reuse and capture-only inspection leave it unchanged.
3 Architecture
One caster context is shared by every UIShadow on a Graphic. It captures normalized source records after uGUI rebuilds, creates deterministic structured keys, and coordinates generated Outer and Inset renderers.
Static outputs use leases and an idle LRU. Tint, blend mode, caster alpha, CanvasGroup alpha, masks, and Outer offsets remain composite/layout state so compatible components share textures. Transient scopes own reusable component output and return to the shared cache when the outermost scope closes.
Capture, blur, spread/dither, inset restriction, and compositing are separate phases. Fast uses a bounded multilevel blur; Accurate uses normalized separable weights. Generated textures are premultiplied. UI compositing preserves stencil, RectMask2D clipping, clip softness, and source-over destination alpha.
Static registries reset at subsystem registration because domain reload may be disabled. Profiling markers exist only when ENABLE_PROFILER is defined. Package console messages exist only when ENABLE_UI_SHADOW_LOGS is defined; operational failures remain visible in renderer diagnostics.
4 Public API
The runtime assembly intentionally exposes only component configuration, diagnostics, presets, and custom-integration contracts. Renderer adapters, snapshots, object identifiers, cache entries, generated renderers, and editor/TMP bootstrapping are implementation details.
UIShadow exposes Algorithm, Mode, Size, Spread, Angle, Distance, Color, BlendMode, caster-color/alpha options, text fingerprint mode, external content version, cache eviction, fit compensation, explicit caster/composite invalidation, diagnostics, setting copy helpers, and transient scopes.
UIShadowGlobalSettings.GlobalAngle is mutable runtime state copied from project settings. It does not mutate a settings asset.
UIShadowDynamicCaster supplies one-shot or every-frame invalidation. UIShadowInsetOnPress crossfades existing mode groups. UIShadowInteractionAnimator maps EventSystem and Selectable state to animated appearances. UIShadowCustomMaterial provides property-only tint or a validated replacement composite material.
Both interaction helpers expose Duration and Advance(deltaTime). Non-finite duration values become zero and immediately finish an active transition at its target appearance. A non-finite time step also finishes the target, including releasing the animator’s transient scopes. Finite negative durations clamp to zero; finite negative steps leave progress unchanged. Automatic updates use the same advancement rules. Warning exposes recovered timing problems alongside target-configuration problems.
Built-in presets are available through UIShadowPresets. User presets use UIShadowPreset and explicit UIShadowPresetField ownership.
UIShadowPresets.GetBuiltIn returns Result<UIShadowPresetValues, UIShadowPresetError>. Built-in and asset preset Apply methods return Optional<UIShadowPresetError>. UIShadow.CopySettingsTo returns Optional<UIShadowCopyError> for either a component or a GameObject target. Check results before reading values and stop dependent work when an optional error is present. An empty optional means the operation succeeded. Presets preserve settings they do not own; copying creates independent settings rather than a live binding.
UIShadowCustomMaterial.ValidateConfiguration() returns Optional<UIShadowMaterialValidationError> without modifying its material. Both interaction helpers’ RefreshTargets() methods return Optional<UIShadowTargetConfigurationError>; correct missing targets or ownership conflicts and refresh before starting dependent interaction work. UIShadow.SetExternalContentVersion(version) returns Optional<UIShadowContentVersionError> and preserves the existing version when negative input is rejected. Compare errors with their canonical values rather than parsing diagnostic text. Failures are reported through the package logger and Renderer Debugger.
Integration contracts are IUIShadowCasterProvider, IUIShadowCacheKeyProvider, IUIShadowCasterPropertyProvider, IUIShadowCompositeMaterialProvider, and IUIShadowCompositeMeshModifier. Import Zal.Common and reference the Zal.Common assembly in custom integration assemblies. Value-producing operations return Result<TValue, TError>; property-writing operations return Optional<TError>, empty on success. Each operation uses its dedicated UI Shadow error type. Check IsFailure before reading a result value, and HasValue before reading an optional error. Providers return typed failures instead of throwing; UI Shadow also contains exceptions from external provider properties and callbacks.
Implement GetCaptureRecords to return the combined caster-local Rect, GetFingerprint and GetCapturePropertyFingerprint to return UIShadowFingerprint128, and ModifyMesh to return the final mesh bounds. ApplyCaptureProperties and ApplyCompositeProperties return an empty optional when their values have been written. Composite material providers declare Mode explicitly: PropertiesOnly uses the package material, while Replacement requires a valid CompositeMaterial and masking compatibility declarations.
Custom providers can build stable keys with UIShadowFingerprintWriter and return them as UIShadowFingerprint128. Full caster providers populate UIShadowCaptureRecord values, explicitly declare optional resources with UIShadowCaptureOverrides, and declare their UIShadowOutputColorSpace. A capture record always requires a mesh and main texture; setting an optional resource without its matching override flag is rejected.
Composite providers implement Optional<UIShadowCompositePropertiesError> ApplyCompositeProperties(Material material, ref Color tint). The supplied material belongs to that shadow renderer and is reset before each application. Set supported shader properties directly on it; update tint explicitly to multiply or replace the tint from preceding providers. Providers execute in component order. Do not retain or destroy the supplied material or change its shader. Shadow texture binding, stencil, rectangular clipping and softness remain renderer-owned and are restored after customization. Return a typed failure to hide only the affected shadow; notify a property change after correction to retry.
CompositeMaterial and UIShadowCustomMaterial.ReplacementMaterial use Optional<Material>. Replacement mode requires a present, live material; properties-only mode does not select a replacement. Multiple active replacement providers are rejected. Disabling or removing a custom provider requires refreshing the affected shadows’ integrations; the built-in helper does this automatically. Raise CompositeMaterialChanged when values change. Property-only changes update compositing without regenerating the shadow texture. Shared source materials remain unchanged, and each renderer releases its customization material on replacement or disable.
Unchanged providers are not evaluated repeatedly. Raise the change event or call UIShadow.MarkCompositeDirty() after externally managed edits. Ordinary shadow tint and transition-alpha changes reuse provider state. The built-in helper also handles animated serialized values. Unmasked built-in helpers keep shared materials. When uGUI derives a masking material, the renderer uses an owned copy so edits to the source still become visible without changing shared stencil state. External providers receive isolated materials even in properties-only mode.
5 TextMesh Pro support
The optional Zal.UIShadow.TMP assembly registers a TMP adapter at runtime and in the Editor. It captures the primary mesh plus active direct TMP_SubMeshUI children, and tells the hierarchy coordinator which children belong to TMP.
Distance-field fonts preserve glyph coverage, face color and alpha, face dilation, outline color and width, and outline softness. Standard, mobile, overlay, and screen-space derivative (SSD) distance-field shader families are supported. Vertex gradients, rich text, fallback fonts, multiple materials, sprite submeshes, and max-visible-character geometry participate in capture. Bitmap fonts retain bitmap coverage and text tint.
Editing these appearance properties on an existing font material updates the shadow on the next canvas update. No manual shadow refresh or material replacement is needed. Unchanged canvas updates reuse the existing shadow without forcing a TMP mesh rebuild. Paint-only material changes update capture without rebuilding the text mesh; changes to padding still refresh geometry.
Both bitmap shader families retain their material tint. Standard bitmap fonts also preserve face textures and their tiling and offset. Sprite submeshes retain material tint, opacity, and atlas tiling and offset. Canvas vertex-color settings and the standard SSD shader’s FORCE_LINEAR keyword are respected.
UseCasterAlpha controls the uniform TMP component alpha; font-material face and outline opacity still define glyph coverage. IgnoreCasterColor removes captured color while retaining that coverage. Invalid font-material values hide the affected shadow and report a recoverable failure through its diagnostics and the Renderer Debugger. Correcting the material restores the shadow.
TMP underlay, glow, bevel, and arbitrary custom shader effects require explicit caster integration. Unsupported effects on the primary material or active fallback submeshes produce a diagnostic; custom distance-field shader families require an explicit integration rather than being treated as bitmap fonts.
The package does not bundle a font. Import TMP Essential Resources before enabling TMP sample content. When those resources are missing, samples show setup guidance instead of leaving missing references.
6 Custom integration
Guaranteed Image, RawImage, legacy Text, and supported TMP sources use built-in adapters. Standard-like custom Graphic implementations use best-effort normalized capture. Exact procedural, multi-pass, or custom-shader behavior should implement IUIShadowCasterProvider.
A caster provider returns one or more UIShadowCaptureRecord values, stable schema/revision fields, bounds, color-space intent, and a fingerprint. Every record requires a mesh and main texture. Set UIShadowCaptureRecord.Overrides when supplying a custom capture material, separate alpha texture, or property block; undeclared resources and declared resources without a value are rejected. Capture materials must produce transparent premultiplied output without depending on destination depth or stencil state.
Composable cache-key and property providers must include provider type, schema, and every visual input in their fingerprints. A replacement composite provider must support UI stencil, RectMask2D clipping, clip softness, tint, and premultiplied textures. Only one replacement is allowed; property-only providers may compose.
Call RefreshIntegrations when provider components change. Provider failures and conflicts render nothing and report deterministic diagnostics; they do not throw.
7 Masking and canvases
Generated renderers inherit the caster’s effective CanvasGroup state and participate in uGUI stencil and RectMask2D clipping. A caster’s self Mask or RectMask2D does not accidentally clip its Outer shadow. Ancestor masks still apply.
Outer renderers are coordinated as parent siblings; Inset renderers remain after source-owned children and before user-authored children. A Graphic on the same GameObject as its rendering Canvas cannot place an Outer sibling correctly and is unsupported. Descendant overrideSorting canvases may escape ordering and produce a warning.
Screen Space Overlay, Screen Space Camera, and World Space canvases use root-canvas coordinates for light direction. Rotation, non-uniform scale, negative scale, and mirroring are included in offset conversion and capture density.
8 Performance
Static compatible shadows share generated textures. Component tint, blend mode, alpha, masks, CanvasGroup state, and Outer Angle/Distance do not fragment the texture key. Inspect entries, leases, hits, misses, generations, evictions, and separate idle/active/transient/temporary bytes in Project Settings or Renderer Debugger.
Fast is the default for broad UI use. Accurate is intended for detail-sensitive edges. Oversized requests respect the configured pixel/dimension budgets and device limits; deterministic downscaling is reported rather than allocating unbounded textures.
Accurate reduces large captures progressively before its bounded separable convolution. This preserves thin-caster coverage and the complete requested support, at the cost of additional reduction passes and temporary targets during generation. Unchanged cached output performs no blur work. Measure UIShadow.TextureGenerator.AccurateBlur when choosing between algorithms for animated shadows.
Use BeginTransientUpdates for Size, Spread, or Inset offset animation. Nested scopes are supported. Every-frame dynamic casters enter transient mode automatically. Static warm frames should produce no package generations; clear idle entries with UIShadowGlobalDiagnostics.ClearIdleCache.
Enable profiler instrumentation with ENABLE_PROFILER. Profiling never creates retained diagnostic snapshots or reports failures.
9 Troubleshooting
- No shadow: confirm URP RenderGraph is active, the source Graphic is enabled, and Renderer Debugger has no failed request.
- Stale mutable texture: call
MarkCasterContentDirty, increment a stable external/key revision, or addUIShadowDynamicCaster. - TMP effect differs: underlay, glow, bevel, and custom TMP shaders require explicit integration.
- Outer ordering warning: move a Graphic off its rendering Canvas GameObject and avoid descendant override-sorting canvases.
- Inset ordering warning: keep source-owned children as a prefix and user-authored content after them.
- Custom material warning: include
_MainTex,_Color, stencil,_ClipRect, and mask-softness properties and consume premultiplied texture data. - Downscaled output: reduce Size/capture quality or increase project limits within the device maximum.
- Build blocked: remove duplicate assets resolving to
Zal.UIShadow/UIShadowRuntimeSettings. - First transition hitch: pre-create both Outer and Inset components; interaction helpers prewarm existing groups.