- Home /
Why are RaycastHit surface normals of corners not interpolated or otherwise correct?
I am using a BoxCast to test a (custom) kinematic character controller moving through the scene before actually updating the character's transform. Using a traditional collide and slide algorithm, I project the velocity along the RaycastHit's surface normal if I hit something, allowing me to slide along angled surfaces, like the cylinder in the below images.
In the first image, the yellow lines indicate where collisions are detected but are correctly projected. Red lines are where collisions are detected but fail to project (but these red lines in the first image are expected, as these are auto-step tests). The red lines all properly indicate the surface normal, which is easier to see on the lines at the bottom of the mesh.
In the second image, the red ray is pointed in the opposite direction (-Z) of my character movement (+Z). This surface normal results in a projection that puts the character controller moving in the opposite direction, cancelling out the movement.
Why would this surface normal be pointed in this direction, and not simply be an interpolation of the two neighbouring surfaces? Is this expected behaviour, and if so, how does one work around something like this to correctly round a corner? While I cannot use a capsule collider for this character controller (due to the cons of a rounded bottom outweighing the pros of a rounded body), I tested this with a capsule anyway and still get what look like incorrect normals, so I can rule out the type of collider being an issue. Any help at this point would be greatly appreciated! Thanks.
I'd also like to add that the surface normal being returned on the corner is always the inverse of the character's movement. The image below shows the normal along the -X axis, with the character moving into it along the +X axis.
Answer by SmittyA · Jan 31, 2021 at 06:53 PM
I love having to answer my own obscure questions. I still don't know why this happens, but I have a workaround that is holding up so far, so I hope this helps someone else looking for this.
When I hit something in a BoxCast, I check the surface normal. If the dot product between my movement's direction and the surface normal is close to -1, I can assume I'm either walking into a flat wall, or, a corner. In my project, I separate horizontal movement from vertical, so I only check this when moving in my "side pass" Either way, I perform a single Raycast (Collider.Raycast, so I'm only checking against that one collider). If I hit the obstacle again, I update my previous collision's surface normal with the "true" surface normal, because Raycast doesn't seem to have this issue, only box/capsule casts.
This may not catch every possible case, but so far so good for me! Hope this helps.
// In some cases, walking into a corner can result in a normal that is the inverse of the desired movement
if (pass == EPass.SIDE)
{
float threshold = -0.98f;
if (Vector3.Dot(direction, closestHit.normal) <= threshold)
{
// Raycast from the volume's center, into the collision point we know we hit before
Vector3 rayOrigin = m_Collider.transform.position + m_Collider.center;
Vector3 rayDirection = (closestHit.point - rayOrigin).normalized;
float rayDistance = Vector3.Distance(closestHit.point, rayOrigin) + m_Params.ContactOffset;
Ray ray = new Ray(rayOrigin, rayDirection);
// If we were able to hit the collider, grab the "true" surface normal so the volume can project collisions correctly
if (closestHit.collider.Raycast(ray, out RaycastHit hitInfo, Vector3.Distance(closestHit.point, rayOrigin) + Physics.defaultContactOffset))
{
closestHit.normal = hitInfo.normal;
}
}
}
Your answer
Follow this Question
Related Questions
Trigger script does not work. 1 Answer
Raycast bullet collision problem 1 Answer
Jump implementation in multi level platforms game 1 Answer
boxcollider fixed bounds 1 Answer
OnCollisionEnter question 1 Answer