Question about performance for raytracing algorithm intersection test - c

I'm currently building a basic raytracing algorithm and need to figure out which system of handling the intersections would be best performance-wise.
In the method I'm checking for a intersection of the ray and the object I'm returning a struct with the distance of the ray traveled to the hit, the position vector of the hit and the normal vector or -1 for the distance if there is no intersection.
For the next step I have to find the shortest distance of all intersections and exclude the ones with a negative distance.
I even thought about having 2 structs, one with only negative distances and one full struct to reduce the amount of space needed, but thought this wouldn't really make a difference.
My options so far:
first go over the array of the intersections and exclude the ones with negative distances, then find the shortest distance from the remainings via a sorting algorithm (probably insertion sort due to quick implementation).
Or put them together in one algorithm and test in each sort step if the distance is negative.
typedef Point3f float[3];
typedef struct {
float distance;
Point3f point;
Point3f normal;
} Intersection;
Intersection intersectObject (Ray-params, object) {
Intersection intersection;
//...
if (hit) {
intersection.distance = distance;
intersection.point = point;
intersection.normal = normal;
} else {
intersection.distance = -1.0f;
}
return intersection;
}
//loop over screen pixel
Intersection* intersections;
int amountIntersections;
//loop over all objects
//here I would handle the intersections
if (amountIntersections) {
//cast additional rays
}
I can't really figure out what would be the best way to handle this, since this would be called a lot of times. The intersection array will probably be a dynamic array with the amountIntersections as the length variable or an array with the most expected amount of intersections which then have intersections in it with negative distances.

Here is the approach I've succesfully used for a huge number of objects. (Especially for ball-and-stick atomic models; see my Wikipedia user page for the equations I used for those.)
First, transform the objects to a coordinate system where the eye is at origin, and the projected plane is parallel to the xy plane, with center on the positive z axis. This simplifies the equations needed a lot, as you can see from the above linked page.
As an example, if you have a unit ray n (so n·n = 1) and a sphere of radius r centered at c, the ray intersects the sphere if and only if h ≥ 0,
h = (n·c)2 + r2 - (c·c)
and if so, at distance d,
d = n·c ± sqrt(h)
If you work out the necessary code, and use sensible temprary variables, you'll see that you can reject non-intersecting spheres using eight multiplications and six additions or subtractions, and that this vectorizes across objects easily using SSE2/AVX intrinsics (#include <x86intrin.h>). (That is, do not try to use an XMM/YMM vector register for n or c, and instead use each register component for a different object, calculating h for 2/4/8 objects at a time.)
For each ray, sort/choose the objects to be tested according to their known minimum z coordinate (say, cz - r for spheres). This way, when you find an intersection at distance d, you can ignore all objects with minimum z coordinate larger than d, because the intersection point would necessarily be further out, behind the already known intersection.
Similarly, you should ignore all intersections where the distance is smaller than the distance to the projection plane (which is zd / nz, if the plane is at z = zd, and only needs to be computed once per ray), because those intersections are between the eye and the projection plane. (Technically, you've "crashed into" something then, if you think of the projection plane as a camera.)

Related

Check if geo location is within radius of other geolocation without using sin/cos/tan

I want to develop a simple geo-fencing algorithm in C, that works without using sin, cos and tan. I am working with a small microcontroller, hence the restriction. I have no space left for <math.h>. The radius will be around 20..100m. I am not expecting super accurate results this way.
My current solution takes two coordinate sets (decimal, .00001 accuracy, but passed as a value x10^5, in order to eliminate the decimal places) and a radius (in m). When multiplying the coordinates with 0.9, they can approximately be used for a Pythagorean equation which checks, if one coordinate lies within the radius of another:
static int32_t
geo_convert_coordinates(int32_t coordinate)
{
return (cordinate * 10) / 9;
}
bool
geo_check(int32_t lat_fixed,
int32_t lon_fixed,
int32_t lat_var,
int32_t lon_var,
uint16_t radius)
{
lat_fixed = geo_convert_distance(lat_fixed);
lon_fixed = geo_convert_distance(lon_fixed);
lat_var = geo_convert_distance(lat_var);
lon_var = geo_convert_distance(lon_var);
if (((lat_var - lat_fixed) * (lat_var - lat_fixed) + (lon_var - lon_fixed) * (lon_var - lon_fixed))
<= (radius * radius))
{
return true;
}
return false;
}
This solution works quite well for the equator, but when changing the latitude, this becomes increasingly inaccurate, at 70°N the deviation is around 50%. I could change the factor depending on the latitude, but I am not happy with this solution.
Is there a better way to do this calculation? Any help is very much appreciated. Best regards!
UPDATE
I used the input I got and managed to implement a decent solution. I used only signed ints, no floats.
The haversine formula could be simplified: due to the relevant radii (50-500m), the deltas of the latitude and longitude are very small (<0.02°). This means, that the sine can be simplified to sin(x) = x and also the arcsine to asin(x) = x. This approach is very accurate for angles <10° and even better for the small angles used here. This leaves the cosine, which I implemented according to #meaning-matters 's suggestion. The cosine will take an angle and return the actual result multiplied by 100, in order to be able to use ints. The square root was implemented with an iterative loop (I cannot find the so post anymore). The haversine calculation was done with the inputs multiplied by powers of 10 in order to achieve accuracy and afterwards divided by the necessary power of 10.
For my 8bit system, this caused a memory usage of around 2000-2500 Bytes.
Implement the Havesine function using your own trigonometric functions that use lookup tables and do interpolation.
Because you don't want very accurate results, small lookup tables, of perhaps twenty points, would be sufficient. And, simple linear interpolation would also be fine.
In case you don't have much memory space: Bear in mind that to implement sine and cosine, you only need one lookup table for 90 degrees of either function. All values can then be determined by mirroring and offsetting.

Computing intercept for an accelerated object in 2D

I'm making a C program in which I simulate a Patriot missile system. In this simulation my Patriot missile has to catch an incoming enemy target missile.
The information about the Patriot missile and the enemy target are stored in a structure like this:
typedef struct _stat {
float32_t x;
float32_t y;
float32_t v; // speed magnitude
float32_t v_theta; // speed angle in radians
float32_t a; // acceleration magnitude
float32_t a_theta; // acceleration angle in radians
} stat;
And I'm storing the informations in two globals variables like those:
stat t_stat; // target stats
stat p_stat; // patriot stats
Now, to simplify the problem the target is moving thanks to an initial speed and is affected only by gravity, so we can consider:
t_stat.x = TARGET_X0;
t_stat.y = TARGET_Y0;
t_stat.v = TARGET_V0;
t_stat.v_theta = TARGET_V_THETA0;
t_stat.a = G; // gravity acceleration
t_stat.a_theta = -(PI / 2);
Again, to simplify I'm also considering to compute the collision point when the Patriot has reached its top speed, so its own acceleration is only used to balance the gravity acceleration. In particular we have:
p_stat.x = PATRIOT_X0;
p_stat.y = PATRIOT_Y0;
p_stat.v = 1701,45; // Mach 5 speed in m/s
p_stat.v_theta = ???? // that's what I need to find
p_stat.a = G; // gravity acceleration
p_stat.a_theta = PI / 2;
In this way we can consider the Patriot as moving at constant speed because the sum of the accelerations by which is affected is equal to 0.
float32_t patr_ax = p_stat.a * cos(p_stat.a_theta); // = 0
float32_t patr_ay = p_stat.a * sin(p_stat.a_theta) - G; // = 0
Now, here comes the problem. I want to write a function which computes the right p_stat.v_theta in order to hit the target (if a collision is possible).
For example the function that I need could have a prototype like this:
uint8_t computeIntercept(stat t, stat p, float32_t *theta);
And it can be used in this way:
if(computeIntercept(t_stat, p_stat, &p_stat.v_theta)) {
printf("Target Hit with an angle of %.2f\n", p_stat.v_theta);
} else {
printf("Target Missed!\n");
}
For making it even more clear, here is the image which I want
Your target projectile is moving with constant acceleration hence the velocity can be described as
Now integrating this equation gives us the equation of the position.
Now by knowing the initial position and we can determine this constant vector is the initial position
Now the position of the target projectile is finally
This are two equations (for x and y coordinate). The equation for y is quadratic and the equation for x is linear since the acceleration (gravitational) is in the vertical direction.
You have
In general you should do something like this :
You can use https://en.wikipedia.org/wiki/Newton%27s_method
in order to solve the last equation for theta that you get.
In order to have a collision you need the coordinates of both objects to be identical at the same instant of time.
You could decompose the problem into two simpler ones, considering each axis, x and y, separately:
You need to calculate the equations of motion for the both objects, once for their horizontal components and once for their vertical components.
Check whether the solutions of both objects contain equal coordinates and if this happens at the same instant of time.
Target coordinates: T (xt, yt)
Patriot coordinates: P (xp, yp)
You could solve this numerically by varying the time, t, and observing whether: T == P.
In your case, one of the equations should contain a parameter accounting for the angle theta.
Simulate the event and find the time the 2 objects are closest.
The missile can only fly so long after launch (negating it going into orbit), let that be tf.
Using struct _stat for each object, write a function that report the x,y for a given t and object.
Simulate, at reasonable intervals (1s?), 0.0 to tf, the square of the distance between the two objects.
From the time t of the closest approach, use it 2 neighbors t-dt and t+dt to do the simulation over again. Could use time from t-dt to t+dt with 10x smaller dt or other methods.
Repeat the above until the distance is close enough or dt is sufficiently small.
If this distance is sufficiently small, evaluate struct _stat for the needed data now that the time is determined.
Note: the details of the complexity of pt2 compute_position(struct _stat st, time t) only need consideration for the the initial dt estimate.

How would you convert X,Y points to Rho,Theta for hough transform in C?

So I am trying to code Hough Transform on C. I have a binary image and have extracted the binary values from the image. Now to do hough transform I have to convert the [X,Y] values from the image into [rho,theta] to do a parametric transform of the form
rho=xcos(theta)+ysin(theta)
I don't quite understand how it's actually transformed, looking at other online codes. Any help explaining the algorithm and how the accumulator for [rho,theta] values should be done based on [X,Y] would be appreciated.Thanks in advance. :)
Your question hints at the fact that you think that you need to map each (X,Y) point of interest in the image to ONE (rho, theta) vector in the Hough space.
The fact of the matter is that each point in the image is mapped to a curve, i.e. SEVERAL vectors in the Hough space. The number of vectors for each input point depends on some "arbitrary" resolution that you decide upon. For example, for 1 degree resolution, you'd get 360 vectors in Hough space.
There are two possible conventions, for the (rho, theta) vectors: either you use [0, 359] degrees range for theta, and in that case rho is always positive, or you use [0,179] degrees for theta and allow rho to be either positive or negative. The latter is typically used in many implementation.
Once you understand this, the Accumulator is little more than a two dimension array, which covers the range of the (rho, theta) space, and where each cell is initialized with 0. It is used to count the number of vectors that are common to various curves for different points in the input.
The algorithm therefore compute all 360 vectors (assuming 1 degree resolution for theta) for each point of interest in the input image. For each of the these vectors, after rounding rho to the nearest integral value (depends on precision in the rho dimension, e.g. 0.5 if we have 2 points per unit) it finds the corresponding cell in the accumulator, and increment the value in this cell.
when this has been done for all points of interest, the algorithm searches for all cells in the accumulator which have a value above a chosen threshold. The (rho, theta) "address" of these cells are the polar coordinates values for the lines (in the input image) that the Hough algorithm has identified.
Now, note that this gives you line equations, one is typically left with figure out the segment of these lines that effectively belong in the input image.
A very rough pseudo-code "implementation" of the above
Accumulator_rho_size = Sqrt(2) * max(width_of_image, height_of_image)
* precision_factor // e.g. 2 if we want 0.5 precision
Accumulator_theta_size = 180 // going with rho positive or negative convention
Accumulator = newly allocated array of integers
with dimension [Accumulator_rho_size, Accumulator_theta_size]
Fill all cells of Accumulator with 0 value.
For each (x,y) point of interest in the input image
For theta = 0 to 179
rho = round(x * cos(theta) + y * sin(theta),
value_based_on_precision_factor)
Accumulator[rho, theta]++
Search in Accumulator the cells with the biggest counter value
(or with a value above a given threshold) // picking threshold can be tricky
The corresponding (rho, theta) "address" of these cells with a high values are
the polar coordinates of the lines discovered in the the original image, defined
by their angle relative to the x axis, and their distance to the origin.
Simple math can be used to compute various points on this line, in particular
the axis intercepts to produce a y = ax + b equation if so desired.
Overall this is a rather simple algorithm. The complexity lies mostly in being consistent with the units, for e.g. for the conversion between degrees and radians (most math libraries' trig functions are radian-based), and also regarding the coordinates system used for the input image.

plane equation from two 3D points and perpendicular distance between that plane and a point

I have three 3d points say A(x1,y1,z1), B(x2,y2,z2) and C(x3,y3,z3). How to get an equation of plane that passes through point A and B , then how to get perpendicular distance from point C to this plane. Perpendicular distance will be distance between plane passing through point C and parallel to plane b/w A and B. Is there any short way to implement these calculation, as I want these calculation to implement in code in C language with time of execution as major concern.
Main aim is to find perpendicular distance b/w C and plane containing A and B.
2 points define a line.
You can always choose a plane including both that line and point C, hence the distance is always zero.
The C you're looking for is something like:
struct Point {
double x;
double y;
double z;
};
double perpendicular_distance(struct Point *a, struct Point *b, struct Point *c)
{
return 0.0;
}
Seriously, understand the maths first.
Your problem is underconstrained as written. There are an infinte number of planes containing the two points A and B; the distance to any third point P will be in the range [0, X] where X is the distance between P and the line connecting points A and B. So, if all you're interested in doing is finding the distance between the point P and the line connecting A and B, there's an algorithm for that. This will also uniquely define the plane containing A and B which is most distant from P.

Simple circular gesture detection

I'm looking at a simple, programmatic way of detecting whether or not the user has drawn a circular shape. I'm working in C, but am happy to work from pseudo-code. A bit of Googling brings up a number of (hopefully) overly-complex methods.
I'm tracking the mouse coordinates as floats, and have created an array of vectors to track the mouse movement over time. Essentially I'm looking to detect when a circle has been drawn and then disgard all movement data not associated with that circle.
I have a basic idea of how this might be accomplished:
Track all movements using a polling function. Each time the function is polled the current mouse position is stored. Here, we loop through the historic position data and do a rough 'snap to position' to compare the two locations. If the new location is within a close enough distance to an old position, we remove all historic data before the old location.
While this works in theory, it's a mess in practice. Does anyone have any suggestions? Bonus points if the method suggested can detect whether it's been drawn clockwise or counter-clockwise.
Based on your tracking/polling function, which pushes float pairs on a stack. This must be done on a regular timing interval.
Do a threshold-based search for two equal entries in the list. Now you have two indexes in your stack; the first and the second equal entries. Consider this as a line.
Get the absolute difference in indices. Then divide by two and get the coordinates of this point. (Center of the line.)
You've got two points: thus you can get the radius of the circle, by getting the distance between the two points divided by two.
Divide the number of step 2 by 2, now you've got the quarters.
If the line at step 1 is vertical and the first point of the line is at the top: If the first quarter is left of the center-point, the circle was drawn counter-clockwise. If the first quarter is right of the center-point, the circle was drawn clockwise. If the first point of the line is at the bottom, reverse (i.e. ccw => cw and cw => ccw)
If the line at step 1 is horizontal and the first point of the list is at the left: If the first quarter is above the center-point, the circle was drawn counter-clockwise. If the first quarter is below of the center-point, the circle was drawn clockwise. If the first point of the line is at the right, reverse.
Check if it was a circle: iterate over all pairs of coordinates and calculate the distance to the center-point. Tweak the threshold of allowed distances from the calculated distance and the actual distance to the center-point.
In step 2 and 4 you can tweak this algorithm further by taking the average of several indices if the timing interval is very low (fast polling). For instance: there are 30 pairs in the array, then you average pairs at 0, 1 and 28, 29 to get the upper point. Do the same for all other points.
I hope this is easy enough.
You are definitely on the right track IMHO. Basically you need to compare each mouse point with the previous mouse point and calculate the angle between them (as envisioned on a unit circle where the first point is at the origin). For this you can use the formula:
double angle = atan2(y2 - y1, x2 - x1) * 180 / PI;
if (angle < 0)
angle += 360;
What you end up with is that for clockwise movement, the angle will cycle in a positive direction, whereas for counterclockwise movement the angle will cycle in a negative direction. You can figure out if the current angle is greater or less than the previous one with the following logic:
if (angle2 > 270 && angle1 < 90)
{
angle1 += 360
}
else if (angle1 > 270 && angle2 < 90)
{
angle2 += 360
}
bool isPositive = (angle2-angle1 > 0);
If you get a certain number of vectors all with angles that are increasing (isPositive is true, let's say, 10 times), you can assume a clockwise circle is being drawn; if the tendency is negative (isPositive is false 10 times) it's a counterclockwise circle. :)
Here's an algorithm to see if an array of points fits a circle:
calculate the centroid of the points (average of all the x and y coordinates)
calculate the distance of all points to the centroid
find the maximum and minimum distances
if maximum - minimum < tolerance, circular section detected
NB This will detect a section of a circle as well so you will need to determine that enough of an angle is swept through for it to be a full circle.
To do this:
calculate centroid as above
calculate angle between centroid and each point (use atan2 function)
map angles to segments (I find 12 30 degree segments works for me; just divide angle by 30 and round down to integer - assuming you are working in degrees here)
if all segments contain at least 1 point, then it is a circle (i.e. your mapped segments array contains all values between 0 and 11)
bonus: increasing angle is anti-clockwise; decreasing is clockwise
Haven't tried this, but the idea came to mind reading your question, so might as well share it with you:
I'm assuming the circle has to be drawn within a reasonable amount of time, given a steady "sample-rate" of the mouse that would leave a known-size array of 2D vectors (points). Add them all and divide by the count of 2D vectors to get an estimate of the "center" point in the array. Then form vectors from this center-point to the points in the array and do dot-products (normalizing by vector length), making sure the sign of the dot-products remain identical for a range of points means those points all move in the same direction, a positive sign will indicate counter-clockwise movement, negative is just the opposite. If the accumulated angle exceeds 2 PI, a circular movement was drawn..
Good luck.
1 - Pick any 3 of the points
2 - If the points are collinear +/- 'some buffer' then it isn't a circle.
3 - Use the method described on Wikipedia for finding the circumscribed circle for a triangle to find the midpoint and radius of your candidate circle
The circumcenter of a triangle can be constructed by drawing any two
of the three perpendicular bisectors. For three non-collinear points,
these two lines cannot be parallel, and the circumcenter is the point
where they cross. Any point on the bisector is equidistant from the
two points that it bisects, from which it follows that this point, on
both bisectors, is equidistant from all three triangle vertices. The
circumradius is the distance from it to any of the three vertices.
4 - Check the distance to the remaining points. If those points are within the 'candidate circle radius' +/- 'some buffer allowance' then it is a circle.
5 - To determine direction, simply calculate the angle between the first and 2nd points from the midpoint. A negative angle is right. A positive angle is left. (Could be reversed depending on the coordinate system you are using)

Resources