faster aabox ray intersection and pre-computed inverse direction

This commit is contained in:
SamGondelman 2018-08-27 15:40:33 -07:00
parent 3d048c77ba
commit aae06e8f49
15 changed files with 69 additions and 91 deletions

View file

@ -180,7 +180,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
float distance;
BoxFace face;
glm::vec3 normal;
boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal);
boundingBox.findRayIntersection(cameraPosition, direction, 1.0f / direction, distance, face, normal);
float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE;
if (event.getID() == 1) { // "1" is left hand
offsetAngle *= -1.0f;

View file

@ -88,7 +88,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
// we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame
// and testing intersection there.
bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal);
bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, 1.0f / overlayFrameDirection, distance, face, surfaceNormal);
if (hit) {
surfaceNormal = transform.getRotation() * surfaceNormal;

View file

@ -596,8 +596,8 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
voxelBox += result3 - Vectors::HALF;
voxelBox += result3 + Vectors::HALF;
glm::vec4 directionInVoxel = wtvMatrix * glm::vec4(direction, 0.0f);
return voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel),
glm::vec3 directionInVoxel = vec3(wtvMatrix * glm::vec4(direction, 0.0f));
return voxelBox.findRayIntersection(glm::vec3(originInVoxel), directionInVoxel, 1.0f / directionInVoxel,
distance, face, surfaceNormal);
}

View file

@ -48,6 +48,7 @@ public:
// Inputs
glm::vec3 origin;
glm::vec3 direction;
glm::vec3 invDirection;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
bool visibleOnly;
@ -847,7 +848,7 @@ float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* ex
float boundDistance = FLT_MAX;
BoxFace face;
glm::vec3 surfaceNormal;
if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, boundDistance, face, surfaceNormal)) {
if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, args->invDirection, boundDistance, face, surfaceNormal)) {
// Don't add this cell if it's already farther than our best distance so far
if (boundDistance < args->distance) {
distance = boundDistance;
@ -863,7 +864,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm:
OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
Octree::lockType lockType, bool* accurateResult) {
RayArgs args = { origin, direction, entityIdsToInclude, entityIdsToDiscard,
RayArgs args = { origin, direction, 1.0f / direction, entityIdsToInclude, entityIdsToDiscard,
visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() };
distance = FLT_MAX;

View file

@ -215,7 +215,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
float localDistance;
BoxFace localFace;
glm::vec3 localSurfaceNormal;
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance,
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, 1.0f / entityFrameDirection, localDistance,
localFace, localSurfaceNormal)) {
if (entityFrameBox.contains(entityFrameOrigin) || localDistance < distance) {
// now ask the entity if we actually intersect

View file

@ -376,7 +376,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
// we can use the AABox's intersection by mapping our origin and direction into the model frame
// and testing intersection there.
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) {
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, 1.0f / modelFrameDirection, distance, face, surfaceNormal)) {
QMutexLocker locker(&_mutex);
float bestDistance = FLT_MAX;
@ -400,6 +400,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
glm::vec3 meshFrameInvDirection = 1.0f / meshFrameDirection;
int shapeID = 0;
int subMeshIndex = 0;
@ -415,8 +416,8 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
float partBoundDistance = FLT_MAX;
BoxFace partBoundFace;
glm::vec3 partBoundNormal;
if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, partBoundDistance,
partBoundFace, partBoundNormal)) {
if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection,
partBoundDistance, partBoundFace, partBoundNormal)) {
priority = partBoundDistance;
}
}
@ -444,7 +445,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
float triangleSetDistance = FLT_MAX;
BoxFace triangleSetFace;
Triangle triangleSetTriangle;
if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace,
if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, triangleSetDistance, triangleSetFace,
triangleSetTriangle, pickAgainstTriangles, allowBackface)) {
if (triangleSetDistance < bestDistance) {
bestDistance = triangleSetDistance;

View file

@ -33,6 +33,7 @@ void PickItemsJob::run(const render::RenderContextPointer& renderContext, const
render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const {
const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition();
const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection();
const glm::vec3 rayInvDirection = 1.0f / rayDirection;
BoxFace face;
glm::vec3 normal;
float isectDistance;
@ -42,7 +43,7 @@ render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPoint
render::ItemKey itemKey;
for (const auto& itemBound : inputs) {
if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) {
if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, rayInvDirection, isectDistance, face, normal)) {
auto& item = renderContext->_scene->getItem(itemBound.id);
itemKey = item.getKey();
if (itemKey.isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistance<maxDistance

View file

@ -192,9 +192,9 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x));
}
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) const {
return findRayAABoxIntersection(origin, direction, _corner, _scale, distance, face, surfaceNormal);
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
float& distance, BoxFace& face, glm::vec3& surfaceNormal) const {
return findRayAABoxIntersection(origin, direction, invDirection, _corner, _scale, distance, face, surfaceNormal);
}
bool AABox::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,

View file

@ -69,7 +69,7 @@ public:
bool expandedContains(const glm::vec3& point, float expansion) const;
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) const;
bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const;

View file

@ -187,9 +187,9 @@ bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3&
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x));
}
bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) const {
return findRayAABoxIntersection(origin, direction, _corner, glm::vec3(_scale), distance, face, surfaceNormal);
bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
float& distance, BoxFace& face, glm::vec3& surfaceNormal) const {
return findRayAABoxIntersection(origin, direction, invDirection, _corner, glm::vec3(_scale), distance, face, surfaceNormal);
}
bool AACube::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,

View file

@ -56,10 +56,10 @@ public:
bool touches(const AABox& otherBox) const;
bool expandedContains(const glm::vec3& point, float expansion) const;
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
float& distance, BoxFace& face, glm::vec3& surfaceNormal) const;
bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const;
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const;
bool touchesSphere(const glm::vec3& center, float radius) const;
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;

View file

@ -214,65 +214,40 @@ bool findInsideOutIntersection(float origin, float direction, float corner, floa
return false;
}
bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& corner, const glm::vec3& scale, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) {
// handle the trivial case where the box contains the origin
if (aaBoxContains(origin, corner, scale)) {
// We still want to calculate the distance from the origin to the inside out plane
float axisDistance;
if ((findInsideOutIntersection(origin.x, direction.x, corner.x, scale.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? 1.0f : -1.0f, 0.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? 1.0f : -1.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) {
distance = axisDistance;
face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? 1.0f : -1.0f);
return true;
}
// This case is unexpected, but mimics the previous behavior for inside out intersections
distance = 0;
return true;
// https://tavianator.com/fast-branchless-raybounding-box-intersections/
bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
float t1, t2, newTmin, newTmax, tmin = -INFINITY, tmax = INFINITY;
int minAxis = -1, maxAxis = -1;
for (int i = 0; i < 3; ++i) {
t1 = (corner[i] - origin[i]) * invDirection[i];
t2 = (corner[i] + scale[i] - origin[i]) * invDirection[i];
newTmin = glm::min(t1, t2);
newTmax = glm::max(t1, t2);
minAxis = newTmin > tmin ? i : minAxis;
tmin = glm::max(tmin, newTmin);
maxAxis = newTmax < tmax ? i : maxAxis;
tmax = glm::min(tmax, newTmax);
}
// check each axis
float axisDistance;
if ((findIntersection(origin.x, direction.x, corner.x, scale.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? -1.0f : 1.0f, 0.0f, 0.0f);
return true;
}
if ((findIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? -1.0f : 1.0f, 0.0f);
return true;
}
if ((findIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) {
distance = axisDistance;
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? -1.0f : 1.0f);
bool inside = tmin < 0.0f;
if (tmax >= glm::max(tmin, 0.0f)) {
if (inside) {
distance = tmax;
bool positiveDirection = direction[maxAxis] > 0.0f;
surfaceNormal = glm::vec3(0.0f);
surfaceNormal[maxAxis] = positiveDirection ? -1.0f : 1.0f;
face = positiveDirection ? BoxFace(2 * maxAxis + 1) : BoxFace(2 * maxAxis);
} else {
distance = tmin;
bool positiveDirection = direction[minAxis] > 0.0f;
surfaceNormal = glm::vec3(0.0f);
surfaceNormal[minAxis] = positiveDirection ? -1.0f : 1.0f;
face = positiveDirection ? BoxFace(2 * minAxis) : BoxFace(2 * minAxis + 1);
}
return true;
}
return false;

View file

@ -76,8 +76,8 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3&
bool findIntersection(float origin, float direction, float corner, float size, float& distance);
bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance);
bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& corner, const glm::vec3& scale, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);
bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal);
bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& center, float radius, float& distance);

View file

@ -176,7 +176,7 @@ void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) {
_triangleIndices.push_back(triangleIndex);
}
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance,
BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) {
if (!_isBalanced) {
balanceTree();
@ -184,7 +184,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3&
float localDistance = distance;
int trianglesTouched = 0;
bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface);
bool hit = _triangleTree.findRayIntersection(origin, direction, invDirection, localDistance, face, triangle, precision, trianglesTouched, allowBackface);
if (hit) {
distance = localDistance;
}
@ -232,9 +232,9 @@ bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3&
return intersectedSomething;
}
bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched,
bool allowBackface) {
bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched,
bool allowBackface) {
if (_population < 1) {
return false; // no triangles below here, so we can't intersect
}
@ -270,7 +270,7 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin,
float childBoundDistance = FLT_MAX;
BoxFace childBoundFace;
glm::vec3 childBoundNormal;
if (child->getBounds().findRayIntersection(origin, direction, childBoundDistance, childBoundFace, childBoundNormal)) {
if (child->getBounds().findRayIntersection(origin, direction, invDirection, childBoundDistance, childBoundFace, childBoundNormal)) {
// We only need to add this cell if it's closer than the local triangle set intersection (if there was one)
if (childBoundDistance < bestLocalDistance) {
priority = childBoundDistance;
@ -301,11 +301,11 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin,
if (!precision && childDistance < EPSILON) {
BoxFace childBoundFace;
glm::vec3 childBoundNormal;
sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, childDistance, childBoundFace, childBoundNormal);
sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, invDirection, childDistance, childBoundFace, childBoundNormal);
}
BoxFace childFace;
Triangle childTriangle;
if (sortedTriangleCell.second->findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) {
if (sortedTriangleCell.second->findRayIntersection(origin, direction, invDirection, childDistance, childFace, childTriangle, precision, trianglesTouched)) {
if (childDistance < bestLocalDistance) {
bestLocalDistance = childDistance;
bestLocalFace = childFace;

View file

@ -28,7 +28,7 @@ class TriangleSet {
void reset(const AABox& bounds, int depth = 0);
void clear();
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched,
bool allowBackface = false);
bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
@ -69,7 +69,7 @@ public:
void insert(const Triangle& t);
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection,
float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false);
bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false);