[Unity 2020.1.8f] Gizmos.DrawWireSphere gets more inaccurate as larger scale gets?

I was debugging an issue where visual gizmo would not get correct representation with code and encountered this.

I’m using gizmo to represent a trigger, and it seems like with large radius frame points are offset slightly.

This can be confirmed by projecting a ray from the center of the sphere to any normalized direction and multiplying with sphere radius. Moreover, math that is used for the detection actually work correctly, but gizmo drawn is incorrect

Here’s an example:
6607414--751720--image (7).png

Where “real” is ray from the center towards player (normalized) * radius:

6607414--751726--upload_2020-12-10_11-14-7.png

Is this caused by actual gizmo having lower precision (too low point count to generate correct representation)?

How to handle large gizmos like these?

So it seems like Gizmos.DrawWireSphere is using a default sphere, which is half sized, and simplified, resulting in this inaccuracy even earlier than it should.

I’ve made GO, attached sphere collider and mesh renderer with a default unity’s sphere and placed it on the same position, scale * 2, collider radius 0.5:
6608599--751945--upload_2020-12-10_17-37-50.png

As you can see, missmatch accurately matches Gizmos.DrawWireSphere behaviour.
And, also, it seems like SphereCollider also doesn’t use default Gizmos.DrawWireSphere for the same reason.

So the only solution it seems like to just make a proper mesh, align it, and use DrawMesh instead.
Or somehow mimick Sphere Collider editor.

Ended up writing a custom gizmos renderer for the sphere which has higher subdivision count;
Probably there’s a more efficient / performant way, but if anyone else encounters same issue:

public static class GizmosExt {
      public static void DrawWireSphere(Vector3 pos, float radius, int subDivNum = 64) {
         float stepAngle = 90f / subDivNum;

         Vector3 lp1 = DrawSubWireSpherePart(pos, radius, Vector3.forward, Vector3.right, stepAngle, subDivNum);
         Vector3 lp2 = DrawSubWireSpherePart(pos, radius, Vector3.forward, -Vector3.right, stepAngle, subDivNum);

         Vector3 lp3 = DrawSubWireSpherePart(pos, radius, -Vector3.forward, Vector3.right, stepAngle, subDivNum);
         Vector3 lp4 = DrawSubWireSpherePart(pos, radius, -Vector3.forward, -Vector3.right, stepAngle, subDivNum);

         Vector3 lp5 = DrawSubWireSpherePart(pos, radius, Vector3.right, Vector3.forward, stepAngle, subDivNum);
         Vector3 lp6 = DrawSubWireSpherePart(pos, radius, Vector3.right, -Vector3.forward, stepAngle, subDivNum);

         Vector3 lp7 = DrawSubWireSpherePart(pos, radius, -Vector3.right, Vector3.forward, stepAngle, subDivNum);
         Vector3 lp8 = DrawSubWireSpherePart(pos, radius, -Vector3.right, -Vector3.forward, stepAngle, subDivNum);

         Gizmos.DrawLine(lp1, lp2);
         Gizmos.DrawLine(lp3, lp4);
         Gizmos.DrawLine(lp5, lp6);
         Gizmos.DrawLine(lp7, lp8);
      }

      private static Vector3 DrawSubWireSpherePart(Vector3 pos,
                                                   float radius,
                                                   Vector3 axis,
                                                   Vector3 rotationAxis,
                                                   float stepAngle,
                                                   int subDivNum) {
         Vector3 dirVector = axis * radius;

         Vector3 lastPoint = pos;

         for (int i = subDivNum - 1; i > 1; i--) {
            Vector3 prevPoint = pos + Quaternion.AngleAxis((i + 1) * stepAngle, rotationAxis) * dirVector;
            lastPoint = pos + Quaternion.AngleAxis(i * stepAngle, rotationAxis) * dirVector;

            Gizmos.DrawLine(prevPoint, lastPoint);
         }

         return lastPoint;
      }
   }
1 Like