Search Unity

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

Discussion in 'Editor & General Support' started by xVergilx, Dec 10, 2020.

  1. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    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:
    image (7).png

    Where "real" is ray from the center towards player (normalized) * radius:
    upload_2020-12-10_11-12-15.png

    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?
     
  2. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    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:
    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.
     
  3. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    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:
    Code (CSharp):
    1. public static class GizmosExt {
    2.       public static void DrawWireSphere(Vector3 pos, float radius, int subDivNum = 64) {
    3.          float stepAngle = 90f / subDivNum;
    4.  
    5.          Vector3 lp1 = DrawSubWireSpherePart(pos, radius, Vector3.forward, Vector3.right, stepAngle, subDivNum);
    6.          Vector3 lp2 = DrawSubWireSpherePart(pos, radius, Vector3.forward, -Vector3.right, stepAngle, subDivNum);
    7.  
    8.          Vector3 lp3 = DrawSubWireSpherePart(pos, radius, -Vector3.forward, Vector3.right, stepAngle, subDivNum);
    9.          Vector3 lp4 = DrawSubWireSpherePart(pos, radius, -Vector3.forward, -Vector3.right, stepAngle, subDivNum);
    10.  
    11.          Vector3 lp5 = DrawSubWireSpherePart(pos, radius, Vector3.right, Vector3.forward, stepAngle, subDivNum);
    12.          Vector3 lp6 = DrawSubWireSpherePart(pos, radius, Vector3.right, -Vector3.forward, stepAngle, subDivNum);
    13.  
    14.          Vector3 lp7 = DrawSubWireSpherePart(pos, radius, -Vector3.right, Vector3.forward, stepAngle, subDivNum);
    15.          Vector3 lp8 = DrawSubWireSpherePart(pos, radius, -Vector3.right, -Vector3.forward, stepAngle, subDivNum);
    16.  
    17.          Gizmos.DrawLine(lp1, lp2);
    18.          Gizmos.DrawLine(lp3, lp4);
    19.          Gizmos.DrawLine(lp5, lp6);
    20.          Gizmos.DrawLine(lp7, lp8);
    21.       }
    22.  
    23.       private static Vector3 DrawSubWireSpherePart(Vector3 pos,
    24.                                                    float radius,
    25.                                                    Vector3 axis,
    26.                                                    Vector3 rotationAxis,
    27.                                                    float stepAngle,
    28.                                                    int subDivNum) {
    29.          Vector3 dirVector = axis * radius;
    30.  
    31.          Vector3 lastPoint = pos;
    32.  
    33.          for (int i = subDivNum - 1; i > 1; i--) {
    34.             Vector3 prevPoint = pos + Quaternion.AngleAxis((i + 1) * stepAngle, rotationAxis) * dirVector;
    35.             lastPoint = pos + Quaternion.AngleAxis(i * stepAngle, rotationAxis) * dirVector;
    36.  
    37.             Gizmos.DrawLine(prevPoint, lastPoint);
    38.          }
    39.  
    40.          return lastPoint;
    41.       }
    42.    }