Advanced Topics
advanced conceptsHighlight Plus · Core Concepts
Advanced configuration topics and edge-case notes for experienced Highlight Plus users.
Quick Tips
- Hover over any inspector label in Unity to see a built-in tooltip describing that option. Most settings do not need additional reading.
- Effects can be previewed in Edit mode — enable Preview In Editor on the Highlight Effect.
- If you reparent or restructure a highlighted object at runtime, call
effect.Refresh()so Highlight Plus rebuilds its internal renderer cache (see Refresh After Hierarchy Change).
Normals Option
Highlight Plus can optimise mesh normals for better outline/glow rendering (applies to Fastest, Medium, and High quality modes only):
| Mode | Effect |
|---|---|
| Smooth | Improves outline quality for Fastest and High quality levels. |
| Preserve Original | Forces Highlight Plus to use the original mesh without caching. Use when modifying meshes dynamically at runtime. |
| Reorient | Replaces normals with vectors pointing outward from the object center. Useful for 2D geometry or meshes without normals. |
Volume-Based Triggering
Automatically enable or disable highlighting when objects enter or exit a trigger volume:
- Add a HighlightTrigger component to the object.
- Set Trigger Mode to Volume.
- Ensure the volume has a Collider marked as Is Trigger and is set to Static.
- The entering object must have a Rigidbody for
OnTriggerEnter/OnTriggerExitto fire.
Depth Clip
When enabled, effects are clipped by the depth buffer so they appear correctly behind solid geometry. This is important for objects that overlap or are partially embedded in surfaces.
Static Batching & MeshCollider
Objects marked Static in the Inspector get their meshes combined by Unity at scene load. Once combined, the per-object mesh is no longer accessible, so Highlight Plus cannot draw outline or glow effects for them.
Fix: attach a MeshCollider to every static GameObject you want highlighted. The MeshCollider can be disabled — its only purpose is to give Highlight Plus a reference to the original (un-batched) mesh at startup.
MeshCollider works for this. BoxCollider, SphereCollider and other primitive colliders do not expose the source mesh.
Excluding Submeshes (SubMesh Mask)
The Sub Mesh Mask property is a bitwise mask. A submesh with index i is included when (1 << i) & SubMeshMask != 0. The default value -1 means all submeshes.
| Goal | Sub Mesh Mask value |
|---|---|
| All submeshes (default) | -1 |
| Only submesh 0 | 1 (1 << 0) |
| Only submesh 1 | 2 (1 << 1) |
| Only submesh 2 | 4 (1 << 2) |
| Submeshes 1 and 2 | 6 ((1 << 1) | (1 << 2)) |
| Submeshes 0 and 3 | 9 ((1 << 0) | (1 << 3)) |
Custom Sorting
By default Highlight Plus sorts effects by distance to the camera. To take control of the order (e.g. force back-to-front sorting independent of camera position), set HighlightEffect.customSorting = true and resort the static HighlightEffect.effects list yourself. Full code sample in Scripting Support — Custom Sorting.
Refresh After Hierarchy Change
Highlight Plus caches the renderers and meshes of the highlighted target. If you change the hierarchy at runtime — reparenting, attaching the object to another, swapping out a child mesh — call:
effect.Refresh(); // rebuild renderer cache
effect.Refresh(discardCachedMeshes: true); // also discard cached mesh copies
Without this, the effect may render against the previous transform/mesh until the next major scene event.
Skinned Mesh Renderers
For animated characters using SkinnedMeshRenderer:
- Enable Update When Offscreen on the SkinnedMeshRenderer to ensure bounds are updated during animations.
- Without this, Highest Quality mode may clip effects on one side of the character because the screen-space bounding rectangle is calculated from stale
renderer.bounds.
GPU-Instanced or Combined Meshes
If you use GPU instancing tools (e.g., Dynamic Mesh Combiner) that bypass standard Renderer visibility, enable Ignore Object Visibility on the HighlightEffect to force rendering.
Multiple Highlight Managers
You can use two or more Highlight Managers in the same scene, each targeting different layers. See Demo Scene 6 for a working example.
Custom Vertex Transformations
All Highlight Plus shaders share a common vertex entry point defined in HighlightPlus/Runtime/Resources/HighlightPlus/CustomVertexTransform.cginc. Edit the ComputeVertexPosition function in that file to apply project-wide vertex transforms (e.g. wind, water displacement, custom skinning) before any effect is rendered.
CustomVertexTransform.cginc are overwritten when you upgrade Highlight Plus. Keep a backup of your edits or move them to a project-level include that you re-apply after each upgrade.
Render Feature — Clear Stencil
Some third-party effects also write to the stencil buffer. If you see outline or glow randomly disappearing or being clipped after enabling another effect, open the URP Renderer Asset, locate the Highlight Plus Render Pass Feature entry and enable Clear Stencil. Highlight Plus then resets the stencil buffer at the start of its pass instead of inheriting whatever the previous feature left.
Effects Visible in SceneView but Not GameView
If effects render in the Scene tab but not in the Game tab, this is usually triggered by interactions between Depth Priming and additional render features. Two fixes:
- Disable Depth Priming in the URP Asset (recommended — matches the default URP Setup).
- If you cannot disable Depth Priming, set its mode to Forced so the prepass executes consistently across both views.
See-Through with Walls Only
To show the see-through effect only behind walls (not floors or ceilings):
- Assign walls to a dedicated layer (e.g.,
Walls). - Set the Occlude Layer on the see-through effect to that layer.
- Enable the Accurate option for pixel-perfect detection.
Occlusion & Masking
There are four mechanisms that prevent or constrain Highlight Plus effects on top of other geometry. Pick the one that matches your case — they target different rendering paths.
| Mechanism | Hides effect on top of… | How |
|---|---|---|
| Canvas UI — pipeline strategy | Any UI Canvas (HUD, menus, in-world UI) | Use Screen Space - Overlay, or stack a UI camera on top of the main camera. |
| HighlightUIMask material | Specific Canvas UI elements (single-camera setups) | Stencil-based mask material assigned to Image / RawImage / Text components. |
| HighlightEffectBlocker (3D) | Specific 3D MeshRenderer / SkinnedMeshRenderer occluders | Component on the occluder. Cancels outline / glow / overlay on covered pixels. |
| HighlightSeeThroughOccluder | The see-through (X-ray) effect specifically | Component on the occluder. Disables see-through behind it. |
| Add Depth To Transparent Object | Transparent objects in front of see-through targets | Editor menu that adds a depth-write material to a transparent renderer. |
Working with Canvas UI
Highlight Plus draws its passes through the URP renderer of the main camera, which means a Canvas rendered in the same pass can end up underneath the outline. There are three approaches to keep the UI on top, ordered from simplest to most flexible.
| Approach | When to use | Setup |
|---|---|---|
| Screen Space - Overlay | Default for HUDs and most UI. Simplest and recommended option. | Set the Canvas Render Mode to Screen Space - Overlay. Overlay canvases are drawn after URP finishes the frame, so they always sit on top of every Highlight Plus pass with no extra setup. |
| Dedicated UI camera (stacked) | You need Screen Space - Camera or World Space UI (e.g. UI affected by post-processing, distortion, or attached to world geometry). | Add a second Camera for the UI, set its Render Type to Overlay, and add it to the main camera’s Stack. Assign the canvas to the UI camera. Highlight Plus runs on the base camera; the UI camera draws afterwards, so the outline always stays under the UI. |
HighlightUIMask material |
Single-camera setups where only specific UI elements should occlude the outline. | See HighlightUIMask material below. |
HighlightUIMask material
Assign HighlightPlus/Runtime/Resources/HighlightPlus/HighlightUIMask.mat to the Material slot of the Image, RawImage or Text component that should hide the outline. The shader writes a stencil reference but no color (ColorMask 0); the Highlight Plus outline pass tests against that stencil and skips those pixels.
HighlightUIMask makes the UI element invisible on screen because it only writes the stencil. If the UI must remain visible, duplicate the element — one copy on top with the regular UI material, one copy underneath using HighlightUIMask as the occluder.
HighlightEffectBlocker (3D Renderer Mask)
The HighlightEffectBlocker component cancels the outline, glow and overlay of a highlighted object on the pixels covered by other 3D renderers. Typical use case: a highlighted character walks behind a wall, pillar or transparent prop and you do not want its silhouette bleeding through the obstacle.
Add the component to the GameObject (or a parent of the GameObjects) whose renderers should occlude the highlight effect, and configure how it gathers renderers:
| Property | Description |
|---|---|
| Include | Which renderers participate in the mask: Only This Object, Children, or Layer In Children. |
| Layer Mask | Layer filter applied when Include is set to Layer In Children. |
| Object Name Filter | When Include is Children or Layer In Children, restrict the mask to renderers whose GameObject name matches this string. Leave empty for no filtering. |
| Use Regular Expressions | Treat Object Name Filter as a regex instead of a substring match. |
| Block Outline And Glow | When enabled, the masked pixels suppress the outline and glow effects of any highlighted object behind them. |
| Block Overlay | When enabled, the masked pixels suppress the overlay effect of any highlighted object behind them. |
If you change the children, layer filter or name filter at runtime, call blocker.Refresh() on the component to rebuild its renderer list.
HighlightSeeThroughOccluder
Attach HighlightSeeThroughOccluder to a GameObject to control how it interacts with the see-through (X-ray) effect of objects behind it. The component supports two modes and two detection methods.
| Property | Values | Description |
|---|---|---|
| Mode | BlocksSeeThrough (default) / TriggersSeeThrough | BlocksSeeThrough cancels the see-through silhouette when this object covers a highlighted target. TriggersSeeThrough activates the see-through only when this object is in front of the highlighted target. |
| Detection Method | Stencil (default) / RayCast | Stencil uses a stencil mask pass — cheaper, pixel-accurate, no physics required. RayCast uses Physics raycasts against the occluder bounds — the highlighted object can query IsSeeThroughOccluded(Camera) at runtime when this method is active. |
From script, you can read the occlusion state of a Highlight Effect against the registered raycast occluders:
HighlightEffect effect = GetComponent<HighlightEffect>();
bool blocked = effect.IsSeeThroughOccluded(Camera.main);
IsSeeThroughOccluded(Camera) only returns meaningful values when at least one occluder is registered with BlocksSeeThrough + RayCast. Stencil mode operates entirely on the GPU and is not visible to scripts.
Add Depth To Transparent Object
The see-through effect requires occluders to write to the depth buffer. Transparent shaders typically skip depth writes, so a transparent object cannot occlude a see-through silhouette out of the box. To fix this:
- Select the transparent GameObject in the scene.
- Use the menu GameObject → Effects → Highlight Plus → Add Depth To Transparent Object. The command inserts an extra depth-only material (
HighlightPlusDepthWrite.mat) at the front of the renderer’s material list. - To revert: GameObject → Effects → Highlight Plus → Remove Depth Compatibility.
LightMode = "DepthOnly" tag, so it only contributes when URP fills _CameraDepthTexture via Copy Depth. If your URP Renderer is configured with a Depth Prepass (or Depth Priming is forced), this workaround has no effect. Keep Depth Priming disabled, as recommended in the URP Setup, or the see-through will see straight through the transparent object regardless of this setting.
Suggest an improvement
Help us improve this documentation page.