12
\$\begingroup\$

Unity's on collision event gives you a Collision object that gives you some information about the collision that happened (including a list of ContactPoints with hit normals).

But what you don't get is surface normals for the collider that you hit. Here's a screenshot to illustrate. The red line is from ContactPoint.normal and the blue line is from RaycastHit.normal.

enter image description here

Is this an instance of Unity hiding information to provide a simplified API? Or do standard 3D realtime collision detection techniques just not collect this information?

And for the second part of the question, what's a surefire and relatively efficient way to get a surface normal for a collision?

I know that raycasting gives you surface normals, but it seems I need to do several raycasts to accomplish this for all scenarios (maybe a contact point/normal combination misses the collider on the first cast, or maybe you need to do some average of all the contact points' normals to get the best result).

My current method:

  1. Back up the Collision.contacts[0].point along its hit normal

  2. Raycast down the negated hit normal for float.MaxValue, on Collision.collider

  3. If that fails, repeat steps 1 and 2 with the non-negated normal

  4. If that fails, try steps 1 to 3 with Collision.contacts[1]

  5. Repeat 4 until successful or until all contact points exhausted.

  6. Give up, return Vector3.zero.

This seems to catch everything, but all those raycasts make me queasy, and I'm not sure how to test that this works for enough cases. Is there a better way?

EDIT If this really is just the way things are with 3D collision, an overview of why in the general case would be just as welcome as something specific to Unity.

\$\endgroup\$
2
  • \$\begingroup\$ Is this just for curiosity, or are you trying to do something and getting stuck? That is, why do you think you need the surface normal? Ask a question about what effect you're trying to achieve, rather than how to "fix" a solution to an unspecified problem, and someone might be able to help you solve it. :) \$\endgroup\$ Oct 17, 2012 at 23:30
  • \$\begingroup\$ @SeanMiddleditch I don't agree. The question is well put and it's exactly was I was looking for. This question and its answers helped me to correct some stuff I was doing wrong. \$\endgroup\$ Apr 22, 2016 at 8:37

1 Answer 1

13
\$\begingroup\$

This really is just the way things are with collision. Not just 3D, but also 2D. Take the following example:

Overlapping AABBs

The green and red AABBs are colliding, and the contact manifold is the blue area. The contact points will be in the blue area somewhere (exactly where can vary with algorithm, but the corners where the blue/red/green meets are ideal-ish).

What surface normal should be returned? The top edge of the red AABB or the left edge? If the green box is falling down, maybe we can reasonably guess the top edge. if it's moving right, maybe we can reasonably guess the left edge. What if it was moving down and right? Do we take the axis of least penetration? The velocity axis of greatest speed? Some heuristic of both? What if the boxes would have collided exactly on the corners?

Extend this to a complex 3D surface potentially made up of 100s of tris/faces. You're still only going to have a small number of ideal-ish contact points. What surface normal should be returned? The average surface normal over the whole tri-mesh (which doesn't make sense for most objects)? The points directly "under" the colliding box's corners (which aren't well defined for most other shapes)? Do you try to find the closest face to the contact points generated (which would require a second pass, since contact points are not calculated directly from any mesh faces)? If you do find the closest face, do you take the face's normal or do you interpolate the face's vertices at the point of contact to get the correct normal for "smooth" objects?

Really, the primary problem is that the contact points aren't all the contact points. In many cases, that would be an infinite set of points, after all. They're just a few points that are distributed well that make it reasonably feasible to approximate physical reaction by applying forces at said points to push the colliding objects around in a reaslitic-ish manner. The specific points/locations of actual object contact are abstracted away behind a simplified mathematical model. Hence, the idea of a specific surface normal of contact simply doesn't make much sense in the general case.

Of course, with more specific constraints and limitations on your objects, world, and motion, you can create alternative collision algorithms that can tell you about surface normal. In the 2D case above, if we assume that the boxes never rotate and that we know the relative velocity and last position of each, would could use continuous collision detection to find out exactly when they would collide and which features collided, supplying us with the exact feature where collision occured, which could then be used as a contact/collision/surface normal. Platformer games are built entirely on such assumptions and special tricks (Which is why using a general physics library like Box2D or Havok or the light never produces the kind of tight, precise control you find in classic platformers like Mario or Sonic; I wouldn't want to say it's impossible, but... it's impossible).

General Newtonian physics libraries, like that used in Unity3D, can't make those kinds of simplifications and assumptions. Therefor, you don't get collision surface normals, you get a contact manifold, generally simplified down to contact points, and that's it.

\$\endgroup\$
1
  • \$\begingroup\$ This is a fantastic answer, and exactly what I was looking for, thank you. The specific problem was getting a convincing reflection vector on a kinematic rigidbody on collision. Is the usual approach if you want this sort of information to choose whether you want to raycast or grab 3 contact points (if available) and take the cross product? \$\endgroup\$ Oct 18, 2012 at 9:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .