- Home /
Calculate Bounds Intersection Face Normal
I am trying to calculate the normal of the face of the bounds (axis aligned) that a ray intersects. There are a couple modifications to the normal problem of bounds intersections (ray-box intersection) I have made:
Ray direction is guaranteed to be normalized
I only need the closest point in front of the ray if any (solved in the IntersectBounds method already)
From this I can find the position of intersection regardless of ray origin and direction.
What I really need is the normal of the face it hit. This isn't a raycasting or mesh lookup problem and is purely algebraic as this is used in a compute shader.
Due to this being on the GPU when normally executed, floating point precision is a factor and I am looking for a reliable way to find the normal without just enumerating each face and checking if it's close enough.
Any help is appreciated; a formula or equation of some kind is ideal here. Thank you!
Example code:
using UnityEngine;
[RequireComponent(typeof(MeshRenderer))]
public class BoundsIntersection : MonoBehaviour {
public Vector3 origin = Vector3.zero;
public Vector3 direction = Vector3.one;
private void OnDrawGizmos() {
MeshRenderer rend = GetComponent<MeshRenderer>();
Bounds b = rend.bounds;
// draw bounds, min, max points
Gizmos.color = Color.white;
Gizmos.DrawWireCube(b.center, b.size);
Gizmos.DrawWireCube(b.min, Vector3.one * 0.05f);
Gizmos.DrawWireCube(b.max, Vector3.one * 0.05f);
Ray r = new Ray(origin, direction.normalized);
Gizmos.color = Color.red;
Gizmos.DrawRay(r);
float t = IntersectBounds(r, b);
if (t > 0) {
Gizmos.color = Color.blue;
Gizmos.DrawLine(origin, origin + r.direction * t);
Gizmos.DrawWireSphere(origin + r.direction * t, 0.05f);
}
}
float IntersectBounds(Ray ray, Bounds bounds) {
// points on infinite axis that ray intersects, defined by bounds
// we need to keep considering the max to make sure the min is correct
float txmin, txmax, tymin, tymax, tzmin, tzmax;
// solve divie by 0 issues
Vector3 invDir = new Vector3(1f / ray.direction.x, 1f / ray.direction.y, 1f / ray.direction.z);
if (invDir.x >= 0) {
txmin = (bounds.min.x - ray.origin.x) * invDir.x;
txmax = (bounds.max.x - ray.origin.x) * invDir.x;
} else {
txmin = (bounds.max.x - ray.origin.x) * invDir.x;
txmax = (bounds.min.x - ray.origin.x) * invDir.x;
}
if (invDir.y >= 0) {
tymin = (bounds.min.y - ray.origin.y) * invDir.y;
tymax = (bounds.max.y - ray.origin.y) * invDir.y;
} else {
tymin = (bounds.max.y - ray.origin.y) * invDir.y;
tymax = (bounds.min.y - ray.origin.y) * invDir.y;
}
if ((txmin > tymax) || (tymin > txmax)) return 0.0f;
// moves bounds in to leave only a 2d plane to check (x-z plane)
if (tymin > txmin) txmin = tymin;
if (tymax < txmax) txmax = tymax;
if (invDir.z >= 0) {
tzmin = (bounds.min.z - ray.origin.z) * invDir.z;
tzmax = (bounds.max.z - ray.origin.z) * invDir.z;
} else {
tzmin = (bounds.max.z - ray.origin.z) * invDir.z;
tzmax = (bounds.min.z - ray.origin.z) * invDir.z;
}
if ((txmin > tzmax) || (tzmin > txmax)) return 0.0f;
if (tzmin > txmin) txmin = tzmin;
if (tzmax < txmax) txmax = tzmax;
if (txmin <= 0f) {
if (txmax <= 0f) return 0f;
return txmax;
}
return txmin;
}
}
What it looks like in scene with cube near the ray (ray kept at default 0, 0, 0 origin and 1, 1, 1 direction)
Silly question, but have you tried RaycastHit.normal? Does it give you the wrong normal?
Unfortunately, I am not using Unity raycasting. I have the bounds and a ray on the GPU ins$$anonymous$$d of on the Unity processing threads. I am trying to figure this out using only the bounds $$anonymous$$ and max points and the ray origin and direction since I don't have access to the physic space of the scene, and these are just bounds of meshes ins$$anonymous$$d of their actual physics shapes. I should note that the example is so that it can be visualized but will actually be running in a compute shader on the GPU.
Okay I see what you mean. I've not tried something like this myself, but I might be able to help with the math.
You could use the mesh's normal array to sort out which normal it is you need.
If your contact point is on a face then a vector between the contact point and the face's normal vector's origin will always be perpendicular to the normal vector of that face, and not to any other. (This gets a little tricky around the edges and when the contact point is close to the normal's origin, but should be okay with floats)
So you could just go through the normals array in the mesh, draw vectors and check angles. You can also compare angles and get the one closest to perpendicular to eli$$anonymous$$ate unlikely results. (If your contact point isn't precisely on the face, or if the geometry is complex enough you could get more than one likely result).