Raytracing object matrix transformations with bounding volume hierarchies - graphics

I've run into an interesting issue in my ray tracer that I am developing. The objects in my scene are stored in a bounding volume hierarchy. Each individual object is encapsulated in a bounding box at a leaf node of the hierarchy and has a matrix transformation associated with it.
Now, the way I have been taught to do matrix transformations of objects in raytracing is to transform each ray by the inverse of the object's matrix and then see if there is an intersection. In pseudocode (and without a bvh tree) it would look like this:
float minimum_distance = FLOAT_MAX;
Intersection closestHit = null;
for(each object in scene)
{
Matrix transform = object.transform();
Matrix inverse = transform.inverse();
Ray transRay = transformRay(eyeRay, inverse);
Intersection hit = CollisionTest(transRay, object);
if(intersectionFound)
{
if(hit.distance() < minimum_distance)
{
closestHit = hit;
}
}
}
Shade(closestHit);
Since there is no bounding structure to the scene objects, you can loop through each one and transform the ray by the matrix of each object to test. But now imagine the following scenario with a BVH tree:
ROOT
Left Box Right Box
| |
V V
object A object B
Now lets say we have an eyeRay that intersections only the right box. The ray will only check for intersections objects that are in the right box and completely ignore any objects in the left box (which is the main advantage of putting your objects in a hierarchy like this...as to avoid unnecessary checking).
However, object A in the left box has a scaling transform associated with it that when applied would stretch object A such that it crosses over into right box territory. If the ray were allowed to check itself against every object in the scene, you would find that when the inverse of the transform of object A is applied to the ray, an intersection would be found. However, the ray will never be allowed to do that transformation check because the untransformed object A sits squarely in the left box. Thus, the intersection will be missed and the result would be a partially rendered object A.
So my question is how would I resolve this issue? I don't want to give up using a bounding volume hierarchy but I don't see how it is compatible with the above algorithm of inverting the ray. Any thoughts?

First note, there are a number of BVH Tree specializations. The more common ones exclude intersection of bounding volumes from neighbor nodes (i.e. Left Box cannot intersect Right Box).
Now the whole point of a bounding volume is that it bounds the underlying object. So if that object has a transform that scales it past the bounding volume, then that means the bounding volume is NOT a proper bounding volume (BV).
Two ways to fix this, if the bounding volume was computed on untrasformed geometry, then when checking ray-boundingVolume intersection, first transform the BV to the same coordinate system as the object.
Depending on what you're doing, a more efficient way may be to compute the BV directly on the transformed object (Scaled, translated, etc) object. This way you don't need to transform the box (or the ray) when doing initial ray-BV check.
Hope that helps.

Related

coco2d getContentSize vs getBoundingBox

I am new to cocos2d-x and I want to get the sprite size. But here are tow functions that do the work: getContentSize and getBoundingBox. What is the difference of this functions? Should I always usegetBoundingBox`?
contentSize refers to the size of the content (ie the texture size) whereas boundingBox also takes into account that the node may be rotated, scaled, skewed.
The bounding box is axis-aligned, which means it forms the rectangle that passes through all 4 corners of the node even when rotated, scaled, skewed, etc. and thus it may be larger than contentSize if any one of these properties has been modified.
However for collision detection of rotated, scaled, skewed, etc nodes the bounding box only provides an "early out" test where not intersecting the bounding box rectangle means there can not be any collision on a more accurate level anyway. If the axis-aligned bounding box intersection test passes you usually go on to perform, for example, an oriented bounding box rectangle intersection test or one where you do a collision mask or polygon intersection test.

BoundingBox Shape

In my Android mapping activity, I have a parallelogram shaped area that I want to tell if points (ie:LatLng) are inside. I've tried using the:
bounds = new LatLngBounds.Builder()
.include(latlngNW)
.include(latlngNE)
.include(latlngSW)
.include(latlngSE)
.build();
and later
if (bounds.contains(currentLatLng) {
.....
}
but it is not that accurate. Do I need to create equations for lines connecting the four corners?
Thanks in advance.
The LatLngBounds appears to create a box from the points included. Given the shape that I'm trying to monitor is a parallelogram, you do need to create equations for each of the edges of the shape and use if statements to determine which side of the line a point lies.
Not an easy solution!
If you wish to build a parallelogram-shaped bounding "box" from a collection of points, and you know the desired angles of the parallelogram's sides, your best bet is to probably define a 2d linear shear transform which will one of those angles to horizontal, and the other to vertical. One may then feed the transformed points into normal "bounding box" routines, and feed the corners of the resulting box through the inverse of the above transform to get a bounding parallelogram.
Note that this approach is generally only suitable for parallelograms, not trapezoids. There are a few special cases where it could be used to find bounding trapezoids [e.g. if the top and bottom were horizontal, and the sides were supposed to converge at a known point (x0-y0), one could map x' = (x-x0)/(y-y0)] but for many kinds of trapezoids, the trapezoid formed by inverse mapping the corners of a horizontal/vertical bounding rectangle may not properly bound the points that are supposed to be within it.

Binary space partition tree for 3D map

I have a project which takes a picture of topographic map and makes it a 3D object.
When I draw the 3D rectangles of the object, it works very slowly. I read about BSP trees and I didn't really understand it. Can someone please explain how to use BSP in 3D (maybe give an example)? and how to use it in my case, when some mountains in the map cover other parts so I need to organize the rectangles in order to draw them well?
In n-D a BSP tree is a spatial partitioning data structure that recursively splits the space into cells using splitting n-D hyperplanes (or even n-D hypersurfaces).
In 2D, the whole space is recursively split with 2D lines (into (possibly infinite) convex polygons).
In 3D, the whole space is recursively split with 3D planes (into (possibly infinite) convex polytopes).
How to build a BSP tree in 3D (from a model)
The model is made of a list of primitives (triangles or quads which is I believe what you call rectangles).
Start with an initial root node in the BSP tree that represents a cell covering the whole 3D space and initially holding all the primitives of your model.
Compute an optimal splitting plane for the considered primitives.
The goal of this step is to find a plane that will split the primitives into two groups of primitives of approximately the same size (either the same spatial extents or the same count of primitives).
A simple splitting strategy could be to chose a direction at random (which will be the normal of your plane) for the splitting. Then sort all the primitives spatially along this axis. And traverse the sorted list of primitives to find the position that will split the primitives into two groups of roughly equal size (i.e. this simply finds the median position from the primitives along this axis). With this direction and this position, the splitting plane is defined.
One typically used splitting strategy is however:
Compute the centroid of all the considered primitives.
Compute the covariance matrix of all the considered primitives.
The centroid gives the position of the splitting plane.
The eigenvector for the largest eigenvalue of the covariance matrix gives the normal of the splitting plane, which is the direction where the primitives are the most spread (and where the current cell should be split).
Split the current node, create two child nodes and assign primitives to each of them or to the current node.
Having found a suitable splitting plane in 1., the 3D space can be now be divided into two half-spaces: one positive, pointed to by the plane normal, and one negative (on the other side of the splitting plane). The goal of this step is to cut in half the considered primitives by assigning the primitives to the half-space where they belong.
Test each primitive of the current node against the splitting plane and assign it to either the left or right child node depending on whether it in the positive half-space or in the negative half-space.
Some primitives may intersect the splitting plane. They can be clipped by the plane into smaller primitives (and maybe also triangulated) so that these smaller primitives are fully inside one of the half-spaces and only belong to one of the cells corresponding to the child nodes. Another option is to simply attach the overlapping primitives to the current node.
Apply recursively this splitting strategy to the created child nodes (and their respective child nodes), until some criterion to stop splitting is met (typically not having enough primitives in the current node).
How to use a BSP tree in 3D
In all use cases, the hierarchical structure of the BSP tree is used to discard irrelevant part of the model for the query.
Locating a point
Traverse the BSP tree with your query point. At each node, go left or right depending on where the query point is located w.r.t. to the splitting plane of the node.
Compute a ray / model intersection
To find all the triangles of your model intersecting a ray (you may need this for picking your map), do something similar to 1.. Traverse the BSP tree with your query ray. At each node, compute the intersection of the ray with the splitting plane. Also check the primitives stored at the node (if any) and report the ones that intersect the ray. Continue traversing the children of this node that whose cell intersect your ray.
Discarding invisible data
Another possible use is to discard pieces of your model that lie outside the view frustum of your camera (that's probably what you are interested in here). The view frustum is exactly bounded by six planes and has 6 quad faces. Like in 1. and 2., you can traverse the BSP tree, check recursively which cell overlaps with the view frustum and completely discard the ones (and the corresponding pieces of your model) that don't. For the plane / view frustum intersection test, you could check whether any of the 6 quads of the view frustum intersect the plane, or you could conservatively approximate the view frustum with a bounding volume (sphere / axis-aligned bounding box / oriented bounding box) or even do a combination of both.
That being said, the solution to your slow rendering problem might be elsewhere (you may not be able to discard a lot of data with a 3D BSP tree for your model):
62K squares is not that big: if you're using OpenGL, you should however not draw these squares individually or continously stream the geometry to the GPU. You can put all the vertices in a single static vertex buffer and draw the quads by preparing a static index buffer containing the list of indices for the squares with either triangles or (better) triangle strips primitives to draw the corresponding squares in a single draw call.
Your data is highly structured (a regular grid with elevation). If you happen to have much larger data sets (that don't even fit in memory anymore), then you need not only spatial partitioning (that exploits the 2.5D structure of your data and its regularity, like a quadtree) but perhaps LOD techniques as well (to replace pieces of your data by a cheaper representation instead of simply discarding the data). You should then investigate LOD techniques for terrain rendering. This page lists a few resources (papers + implementations). A simplified Chunked LOD could be used as a starting point.

Ray Box intersection

I was wondering if anyone knew of a good resource for ray-box intersection algorithms. I am writing a ray-tracing program and want to include a box-primitive object. To be specific, I need an algorithm that can return a "t value" for a ray parameterized as R = E + t*D where E is the starting point and D is the direction vector. I have already implemented a ray-box intersection that is useful for bounding boxes, but it only returns a boolean value for whether or not the box was hit. This is no good however since I need to be able to calculate the exact point in 3D space that the box was hit in order to be able to render it.
I assume you are interested only in axes aligned boxes. The code is available in LuxRays sources: https://bitbucket.org/luxrender/luxrays/src/ceb10f7963250be95af709f98633907c13da7830/src/luxrays/core/geometry/bbox.cpp?at=default&fileviewer=file-view-default#bbox.cpp-148

Which stage of pipeline should I do culling and clipping and How should I reconstruct triangles after clipping

I'm trying to implement graphic pipeline in software level. I have some problems with clipping and culling now.
Basically, there are two main concerns:
When should back-face culling take place? Eye coordinate, clipping coordinate or window coordinate? I initially made culling process in eye coordinate, thinking this way could relieve the burden of clipping process since many back-facing vertices have already been discarded. But later I realized that in this way vertices need to take 2 matrix multiplications , namely left multiply model-view matrix --> culling --> left multiply perspective matrix, which increases the overhead to some extent.
How do I do clipping and reconstruct triangle? As far as I know, clipping happens in clipping coordinate(after perspective transformation), in another word homogeneous coordinate in which every vertex is being determined whether no not it should be discarded by comparing its x, y, z components with w component. So far so good, right? But after that I need to reconstruct those triangles which have one or two vertices been discarded. I googled that Liang-Barsky algorithm would be helpful in this case, but in clipping coordinate what clipping plane should I use? Should I just record clipped triangles and reconstruct them in NDC?
Any idea will be helpful. Thanks.
(1)
Back-face culling can occur wherever you want.
On the 3dfx hardware, and probably the other cards that rasterised only, it was implemented in window coordinates. As you say that leaves you processing some vertices you don't ever use but you need to weigh that up against your other costs.
You can also cull in world coordinates; you know the location of the camera so you know a vector from the camera to the face — just go to any of the edge vertices. So you can test the dot product of that against the normal.
When I was implementing a software rasteriser for a z80-based micro I went a step beyond that and transformed the camera into model space. So you get the inverse of the model matrix (which was cheap in this case because they were guaranteed to be orthonormal, so the transpose would do), apply that to the camera and then cull from there. It's still a vector difference and a dot product but if you're using the surface normals only for culling then it saves having to transform each and every one of them for the benefit of the camera. For that particular renderer I was then able to work forward from which faces are visible to determine which vertices are visible and transform only those to window coordinates.
(2)
A variant on Sutherland-Cohen is the thing I remember seeing most often. You'd do a forward scan around the outside of the polygon checking each edge in turn and adjusting appropriately.
So e.g. you start with the convex polygon between points (V1, V2, V3). For each clipping plane in turn you'd do something like:
for(Vn in input vertices)
{
if(Vn is on the good side of the plane)
add Vn to output vertices
if(edge from Vn to Vn+1 intersects plane) // or from Vn to 0 if this is the last edge
{
find point of intersection, I
add I to output vertices
}
}
And repeat for each plane. If you're worried about repeated costs then you either need to adopt a structure with an extra level of indirection between faces and edges or just keep a cache. You'd probably do something like dash round the vertices once marking them as in or out, then cache the point of intersection per edge, looked up via the key (v1, v2). If you've set yourself up with the extra level of indirection then store the result in the edge object.

Resources