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.
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:
Back up the Collision.contacts[0].point along its hit normal
Raycast down the negated hit normal for float.MaxValue, on Collision.collider
If that fails, repeat steps 1 and 2 with the non-negated normal
If that fails, try steps 1 to 3 with Collision.contacts[1]
Repeat 4 until successful or until all contact points exhausted.
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?