Here are fixes/workarounds for 3 Immediate Mode Canvas bugs
I have fixes/workarounds for three prohibitive bugs related to the experimental Immediate Mode Canvas feature in Shapes 4.5.1:
- The canvas sometimes renders below other immediate mode shapes in the scene rather than functioning like an overlay.
- The IM Canvas stops being displayed if the currently active main camera has a scale (in the transform hierarchy) larger than 2.
- Text in the IM canvas renders behind 3D objects in the scene.
The first issue can be solved by having the onPreRender callback register a global function that handles sorting of ImmediateModeShapeDrawers rather than independently registering each ImmediateModeShapeDrawer without control over the order. below is a diff of my implementation of this. It relies on a static constructor, which I'm not sure is the best solution, but it works for me so far.
diff -r d50374e60402 -r 2a5ba7d4ef06 Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs
--- a/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs Sat Nov 15 22:28:44 2025 +0200
+++ b/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs Sun Nov 16 00:53:33 2025 +0200
@@ -26,6 +26,8 @@
public void Add( ImmediateModePanel panel ) => panels.Add( panel );
public void Remove( ImmediateModePanel panel ) => panels.Remove( panel );
+ public override int priority { get { return 1000; } }
+
protected void DrawPanels() {
using( Draw.Scope ) {
if( Canvas.renderMode == RenderMode.ScreenSpaceOverlay )
diff -r d50374e60402 -r 2a5ba7d4ef06 Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeShapeDrawer.cs
--- a/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeShapeDrawer.cs Sat Nov 15 22:28:44 2025 +0200
+++ b/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeShapeDrawer.cs Sun Nov 16 00:53:33 2025 +0200
@@ -3,10 +3,12 @@
namespace Shapes {
+ using System;
+ using System.Collections.Generic;
using UnityEngine;
/// <summary>A helper type to inherit from when you want a component that draws immediate mode shapes</summary>
- public class ImmediateModeShapeDrawer : MonoBehaviour {
+ public class ImmediateModeShapeDrawer : MonoBehaviour, IComparable<immediatemodeshapedrawer> {
/// <summary>Whether or not to only draw in cameras that can see the layer of this GameObject</summary>
[Tooltip( "When enabled, shapes will only draw in cameras that can see the layer of this GameObject" )]
@@ -30,7 +32,7 @@
DrawShapes( cam );
}
- #if (SHAPES_URP || SHAPES_HDRP)
+ /*#if (SHAPES_URP || SHAPES_HDRP)
#if UNITY_2019_1_OR_NEWER
public virtual void OnEnable() => UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering += DrawShapesSRP;
public virtual void OnDisable() => UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering -= DrawShapesSRP;
@@ -41,8 +43,42 @@
#else
public virtual void OnEnable() => Camera.onPreRender += OnCameraPreRender;
public virtual void OnDisable() => Camera.onPreRender -= OnCameraPreRender;
+ #endif*/
+
+ public virtual int priority { get; protected set; }
+
+ #if (SHAPES_URP || SHAPES_HDRP)
+ #if UNITY_2019_1_OR_NEWER
+ static ImmediateModeShapeDrawer() => UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering += DrawShapesSRPAll;
+ void DrawShapesSRPAll( UnityEngine.Rendering.ScriptableRenderContext ctx, Camera cam ) => OnCameraPreRenderAll( cam );
+ #else
+ static ImmediateModeShapeDrawer() => Debug.LogWarning( "URP/HDRP immediate mode doesn't really work pre-Unity 2019.1, as there is no OnPreRender or beginCameraRendering callback" );
+ #endif
+ #else
+ static ImmediateModeShapeDrawer() => Camera.onPreRender += OnCameraPreRenderAll;
#endif
+ public virtual void OnEnable() => RegisterShapeDrawer(this);
+ public virtual void OnDisable() => DeregisterShapeDrawer(this);
+
+ static List<immediatemodeshapedrawer> allDrawers = new();
+ static void OnCameraPreRenderAll( Camera cam ) {
+ foreach ( var drawer in allDrawers )
+ drawer.OnCameraPreRender( cam );
+ }
+
+ static void RegisterShapeDrawer( ImmediateModeShapeDrawer drawer ) {
+ allDrawers.Add( drawer );
+ allDrawers.Sort();
+ }
+
+ static void DeregisterShapeDrawer( ImmediateModeShapeDrawer drawer ) {
+ allDrawers.Remove( drawer );
+ }
+
+ public int CompareTo(ImmediateModeShapeDrawer other) {
+ return priority - other.priority;
+ }
}
}The second issue happens because the IM Canvas is placed at a distance half-way between the near clip plane and far clip plane of the current active (main?) camera, but the calculated distance does not take into account that camera clip planes are not affected by transform scale. Here is a diff with my fix:
diff -r aeddbc5174f4 -r d50374e60402 Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs
--- a/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs Sat Nov 15 19:39:25 2025 +0200
+++ b/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs Sat Nov 15 22:28:44 2025 +0200
@@ -90,7 +90,7 @@
float planeDistance = ( cam.nearClipPlane + cam.farClipPlane ) / 2;
Transform camTf = cam.transform;
Vector3 forward = camTf.forward;
- Vector3 origin = camTf.TransformPoint( 0, 0, planeDistance );
+ Vector3 origin = camTf.TransformPoint( 0, 0, planeDistance / camTf.lossyScale.z );
float scale = 1;
RectTransform rtf = (RectTransform)Canvas.transform;
The third issue seems to be caused by the IM Canvas TextMeshPro objects being rendered with 3D depth rather than as an overlay. While I don't know how to fix that directly, I've implemented a workaround which is to render the text very close in front of the near clip plane rather than half way between the near and far clip planes. Here is my diff:
diff -r 2a5ba7d4ef06 -r 77743d42a9bd Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs
--- a/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs Sun Nov 16 00:53:33 2025 +0200
+++ b/Assets/Shapes/Scripts/Runtime/Immediate Mode/ImmediateModeCanvas.cs Sun Nov 16 01:03:41 2025 +0200
@@ -89,7 +89,7 @@
Matrix4x4 GetOverlayToWorldMatrix( Camera cam ) {
// overlay cameras are a little more complicated,
// we have to construct a canvasToWorld matrix
- float planeDistance = ( cam.nearClipPlane + cam.farClipPlane ) / 2;
+ float planeDistance = ( cam.nearClipPlane * 0.99999f + cam.farClipPlane * 0.00001f );
Transform camTf = cam.transform;
Vector3 forward = camTf.forward;
Vector3 origin = camTf.TransformPoint( 0, 0, planeDistance / camTf.lossyScale.z );
I hope these may be of use to others!
thanks a bunch! I'll look at implementing these fixes when I get to working on an update