I need to interpolate a 3D surface given it's points and normal vectors.
Given a point on it's surface, I need to find where that point would be in space once the interpolation has been accounted for. I need to be able to do this for each triangle in isolation.
Here's what I'm trying to describe. I need the position of the point once the curve / surface has been interpolated.
If I was working in 2D:
3D:
I've come across this paper "Simple local interpolation of surfaces using normal vectors - Takashi Nagata" which I think demonstrates exactly what I'm looking for (section 2.2. Interpolation of a patch using normals), but the math is just beyond me.
What I'm trying to extract from it is a set of equations where the position and normals of the points comprising the triangle go in, as well as the point on the triangle, and the adjusted point comes out (like magic).
The paper looks like its trying to fit a quadratic surface so that it matches the points and normals you have. The resulting surface is given by
p(s,t) = c00 + c10 s + c01 t + c11 s t + c20 s^2 + c02 t^2
where s,t are the two variables, c00 etc are all vectors with three coordinates. s,t are chosen so at s=0,t=0 its your first point, s=1, t=0 is your second point and s=1,t=1 is your third point. Assuming we can find the various c00's you can pick some values of s,t in the triangle to give a middle point, s=2/3, t=1/3 might be a find candidate.
Find c00 etc will take some work. You probably need to implement eqn 15, which gives a curvature, as a function
vec3 c(vec3 D,vec3 n0,vec3 n1) {
vec3 v = (n0 + n1)/2; // 12a
vec3 dv = (n0 - n1)/2; // 12b
double d = D.dot(v); // 13a
double dd = D.dot(dv); // 13b
double c = n0.dot(n0 - 2*dv); // 14a
double dc = n0.dot(dv); // 14b
vec3 res;
if( c == -1 || c==1 )
res = vec3.zeroVector;
else
res = dd / (1-dc) * v + d / dc * dv;
return res;
}
assuming you have a vec3 class which can do basic vector operators.
With that defined, use 35, 36 to define the starting vectors and normals. Use 39 to define differences between pairs of points d1, d2, d3 and curvatures c1, c2, c3. The use eq 44
x(η, ζ ) = x00(1 − η) + x10(η − ζ ) + x11ζ
− c1(1 − η)(η − ζ ) − c2(η − ζ )ζ − c3(1 − η)ζ
and your done.
For the records, and because I wanted to have this information somewhere on the web.
This is the 2d interpolation using the paper posted by the OP.
Where 1a and 1b are the boundary conditions, and the equations 4a and 4b are the x and y components of the vector c needed for the interpolation.
Related
I have a position in space called X1. X1 has a velocity called V1. I need to construct an orthogonal plane perpendicular to the velocity vector. The origin of the plane is X1.
I need to turn the two edges from the plane into two vectors, E1 and E2. The edges connect at the origin. So the three vectors form an axis.
I'm using the GLM library for the vector mathematics.
One way to create a frame from a vector is to use Householder transformations. This may seem complicated but the code is in quite short, at least as efficient as using cross products, and less prone to rounding error. Moreover exactly the same idea works in any number of dimensions.
The ideas is, given a vector v, find a Householder transformation that maps v to a multiple of (1,0,0), and then apply the inverse of this to (0,1,0) and (0,0,1) to get the other frame vectors. Since a Householder transformation is it's own inverse, and since they are simple to apply, the resulting code is fairly efficient. Below is C code that I use:
static void make_frame( const double* v, double* f)
{
double lv = hypot( hypot( v[0], v[1]), v[2]); // length of v
double s = v[0] > 0.0 ? -1.0 : 1.0;
double h[3] = { v[0] - s*lv, v[1], v[2]}; // householder vector for Q
double a = 1.0/(lv*(lv + fabs( v[0]))); // == 2/(h'*h)
double b;
// first frame vector is v normalised
b = 1.0/lv;
f[3*0+0] = b*v[0]; f[3*0+1] = b*v[1]; f[3*0+2] = b*v[2];
// compute other frame vectors by applying Q to (0,1,0) and (0,0,1)
b = -v[1]*a;
f[3*1+0] = b*h[0]; f[3*1+1] = 1.0 + b*h[1]; f[3*1+2] = b*h[2];
b = -v[2]*a;
f[3*2+0] = h[0]*b; f[3*2+1] = b*h[1]; f[3*2+2] = 1.0 + b*h[2];
}
In general you can define a plane in 3D using four numbers, e.g., Ax+By+Cz=D. You can think of the triple of numbers (A,B,C) as a vector that sticks out perpendicularly to the plane (called the normal vector).
The normal vector n = (A,B,C) only defines the orientation of the plane, so depending on the choice of the constant D you get planes at different distance from the origin.
If I understand your question correctly, the plane you're looking for has normal vector (A,B,C) = V1 and the constant D is obtained using the dot product: D = (A,B,C) . X1, i.e., D = AX1.x + BX1.y + C*X1.z.
Note you can also obtain the same result using the geometric equation of a plane n . ((x,y,z) - p0) = 0, where p0 is some point on the plane, which in your case is V1 . ( (x,y,z) - X1) = 0.
I'd like to generate random points being located on the surface of an n-dimensional torus. I have found formulas for how to generate the points on the surface of a 3-dimensional torus:
x = (c + a * cos(v)) * cos(u)
y = (c + a * cos(v)) * sin(u)
z = a * sin(v)
u, v ∈ [0, 2 * pi); c, a > 0.
My question is now: how to extend this formulas to n dimensions. Any help on the matter would be much appreciated.
I guess that you can do this recursively. Start with a full orthonormal basis of your vector space, and let the current location be the origin. At each step, choose a point in the plane spanned by the first two coordinate vectors, i.e. take w1 = cos(t)*v1 + sin(t)*v2. Shift the other basis vectors, i.e. w2 = v3, w3 = v4, …. Also take a step from your current position in the direction w1, with the radius r1 chosen up front. When you only have a single basis vector remaining, then the current point is a point on the n-dimensional torus of the outermost recursive call.
Note that while the above may be used to choose points randomly, it won't choose them uniformly. That would likely be a much harder question, and you definitely should ask about the math of that on Math SE or perhaps on Cross Validated (Statistics SE) to get the math right before you worry about implementation.
An n-torus (n being the dimensionality of the surface of the torus; a bagel or doughnut is therefore a 2-torus, not a 3-torus) is a smooth mapping of an n-rectangle. One way to approach this is to generate points on the rectangle and then map them onto the torus. Aside from the problem of figuring out how to map a rectangle onto a torus (I don't know it off-hand), there is the problem that the resulting distribution of points on the torus is not uniform even if the distribution of points is uniform on the rectangle. But there must be a way to adjust the distribution on the rectangle to make it uniform on the torus.
Merely generating u and v uniformly will not necessarily sample uniformly from a torus surface. An additional step is needed.
J.F. Williamson, "Random selection of points distributed on curved surfaces", Physics in Medicine & Biology 32(10), 1987, describes a general method of choosing a uniformly random point on a parametric surface. It is an acceptance/rejection method that accepts or rejects each candidate point depending on its stretch factor (norm-of-gradient). To use this method for a parametric surface, several things have to be known about the surface, namely—
x(u, v), y(u, v) and z(u, v), which are functions that generate 3-dimensional coordinates from two dimensional coordinates u and v,
The ranges of u and v,
g(point), the norm of the gradient ("stretch factor") at each point on the surface, and
gmax, the maximum value of g for the entire surface.
For the 3-dimensional torus with the parameterization you give in your question, g and gmax are the following:
g(u, v) = a * (c + cos(v) * a).
gmax = a * (a + c).
The algorithm to generate a uniform random point on the surface of a 3-dimensional torus with torus radius c and tube radius a is then as follows (where RNDEXCRANGE(x,y) returns a number in [x,y) uniformly at random, and RNDRANGE(x,y) returns a number in [x,y] uniformly at random):
// Maximum stretch factor for torus
gmax = a * (a + c)
while true
u = RNDEXCRANGE(0, pi * 2)
v = RNDEXCRANGE(0, pi * 2)
x = cos(u)*(c+cos(v)*a)
y = sin(u)*(c+cos(v)*a)
z = sin(v)*a
// Norm of gradient (stretch factor)
g = a*abs(c+cos(v)*a)
if g >= RNDRANGE(0, gmax)
// Accept the point
return [x, y, z]
end
end
If you have n-dimensional torus generating formulas, a similar approach can be used to generate uniform random points on that torus (accept a candidate point if norm-of-gradient equals or exceeds a random number in [0, gmax), where gmax is the maximum norm-of-gradient).
Why this throws an compilation error: no matching function for call to ‘cross(glm::vec4&, glm::vec4&)’
glm::vec4 a;
glm::vec4 b;
glm::vec4 c = glm::cross(a, b);
but it works fine for vec3?
There is no such thing as a 4D vector cross-product; the operation is only defined for 3D vectors. Well, technically, there is a seven-dimensional vector cross-product, but somehow I don't think you're looking for that.
Since 4D vector cross-products aren't mathematically reasonable, GLM doesn't offer a function to compute it.
What do your vec4's represent? Like Nicol said, cross products are only for 3D vectors. The cross product operation is used to find a vector that is orthogonal to the two input vectors. So if your vec4's represent 3D homogeneous vectors in the form {x, y, z, w}, then the w-component doesn't matter to you; You could simply ignore it.
A workaround could go as follows:
vec4 crossVec4(vec4 _v1, vec4 _v2){
vec3 vec1 = vec3(_v1[0], _v1[1], _v1[2]);
vec3 vec2 = vec3(_v2[0], _v2[1], _v2[2]);
vec3 res = cross(vec1, vec2);
return vec4(res[0], res[1], res[2], 1);
}
Simply turn your vec4's into vec3's, perform the cross product, then add a w-component of 1 back into it.
The generalization of the cross product is the wedge product, and the wedge product of two vectors is a 2-form, also known as a bivector.
In 3-space, the 2-form kinda looks like a vector, but it behaves quite differently. Suppose we have two non-collinear vectors tangent to a surface (aka tangent vectors). By taking the cross product of these vectors, we have a 2-form that represents the tangent plane. We can also represent that tangent plane by the vector normal to that plane (aka the normal vector). But the tangent and normal vectors are transformed differently, i.e. the normal vector is transformed by the inverse transpose of the matrix used to transform the tangent vectors.
In 4-space, the 2-form resulting from the wedge product of two vectors also represents the plane that contains the two vectors (this is also true in N-space). Similarly to the case in 3-space, we can have an alternate interpretation of that plane, but in 4-space, the complement to a plane is not a 4-vector, but another plane, both of which are represented with 6 components, not 4.
c1 * e1^e2 + c2 * e1^e3 + c3 * e1^e4 + c4 * e2^e3 + c5 * e2^e4 + c6 * e3^e4
Since glm doesn't provide the API for wedge products, you will have to roll your own. You can easily work out the algebra for the wedge product with two simple rules:
(1) ei ^ ei = 0
(2) ei ^ ej = -ej ^ ei
where the ei and ej are the component vectors (bases) of the vector space, e.g.
[a b c d] --> a * e1 + b * e2 + c * e3 + d * e4
The 7-dimensional vector referred to in a previous post is the geometric product of two vectors, which uses ei^ei=1 instead of rule (1) above, and is like a meld of the dot and cross products (or complex multiplication), which is more than what you want.
For more information, https://en.wikipedia.org/wiki/Exterior_algebra or https://en.wikipedia.org/wiki/Geometric_algebra .
There is more shortcut way to calculate cross product using glm's GLM_SWIZZLE.
Just do #define GLM_SWIZZLE before inclusion of any glm file. It's also helpful for lots of other tricks.
glm::vec4 a;
glm::vec4 b;
glm::vec4 c = glm::vec4( glm::cross( glm::vec3( a.xyz ), glm::vec3( b.xyz ) ), 0 );
I have three vertices which make up a plane/polygon in 3D Space, v0, v1 & v2.
To calculate barycentric co-ordinates for a 3D point upon this plane I must first project both the plane and point into 2D space.
After trawling the web I have a good understanding of how to calculate barycentric co-ordinates in 2D space, but I am stuck at finding the best way to project my 3D points into a suitable 2D plane.
It was suggested to me that the best way to achieve this was to "drop the axis with the smallest projection". Without testing the area of the polygon formed when projected on each world axis (xy, yz, xz) how can I determine which projection is best (has the largest area), and therefore is most suitable for calculating the most accurate barycentric co-ordinate?
Example of computation of barycentric coordinates in 3D space as requested by the OP. Given:
3D points v0, v1, v2 that define the triangle
3D point p that lies on the plane defined by v0, v1 and v2 and inside the triangle spanned by the same points.
"x" denotes the cross product between two 3D vectors.
"len" denotes the length of a 3D vector.
"u", "v", "w" are the barycentric coordinates belonging to v0, v1 and v2 respectively.
triArea = len((v1 - v0) x (v2 - v0)) * 0.5
u = ( len((v1 - p ) x (v2 - p )) * 0.5 ) / triArea
v = ( len((v0 - p ) x (v2 - p )) * 0.5 ) / triArea
w = ( len((v0 - p ) x (v1 - p )) * 0.5 ) / triArea
=> p == u * v0 + v * v1 + w * v2
The cross product is defined like this:
v0 x v1 := { v0.y * v1.z - v0.z * v1.y,
v0.z * v1.x - v0.x * v1.z,
v0.x * v1.y - v0.y * v1.x }
WARNING - Almost every thing I know about using barycentric coordinates, and using matrices to solve linear equations, was learned last night because I found this question so interesting. So the following may be wrong, wrong, wrong - but some test values I have put in do seem to work.
Guys and girls, please feel free to rip this apart if I screwed up completely - but here goes.
Finding barycentric coords in 3D space (with a little help from Wikipedia)
Given:
v0 = (x0, y0, z0)
v1 = (x1, y1, z1)
v2 = (x2, y2, z2)
p = (xp, yp, zp)
Find the barycentric coordinates:
b0, b1, b2 of point p relative to the triangle defined by v0, v1 and v2
Knowing that:
xp = b0*x0 + b1*x1 + b2*x2
yp = b0*y0 + b1*y1 + b2*y2
zp = b0*z0 + b1*z1 + b2*z2
Which can be written as
[xp] [x0] [x1] [x2]
[yp] = b0*[y0] + b1*[y1] + b2*[y2]
[zp] [z0] [z1] [z2]
or
[xp] [x0 x1 x2] [b0]
[yp] = [y0 y1 y2] . [b1]
[zp] [z0 z1 z2] [b2]
re-arranged as
-1
[b0] [x0 x1 x2] [xp]
[b1] = [y0 y1 y2] . [yp]
[b2] [z0 z1 z2] [zp]
the determinant of the 3x3 matrix is:
det = x0(y1*z2 - y2*z1) + x1(y2*z0 - z2*y0) + x2(y0*z1 - y1*z0)
its adjoint is
[y1*z2-y2*z1 x2*z1-x1*z2 x1*y2-x2*y1]
[y2*z0-y0*z2 x0*z2-x2*z0 x2*y0-x0*y2]
[y0*z1-y1*z0 x1*z0-x0*z1 x0*y1-x1*y0]
giving:
[b0] [y1*z2-y2*z1 x2*z1-x1*z2 x1*y2-x2*y1] [xp]
[b1] = ( [y2*z0-y0*z2 x0*z2-x2*z0 x2*y0-x0*y2] . [yp] ) / det
[b2] [y0*z1-y1*z0 x1*z0-x0*z1 x0*y1-x1*y0] [zp]
If you need to test a number of points against the triangle, stop here. Calculate the above 3x3 matrix once for the triangle (dividing it by the determinant as well), and then dot product that result to each point to get the barycentric coords for each point.
If you are only doing it once per triangle, then here is the above multiplied out (courtesy of Maxima):
b0 = ((x1*y2-x2*y1)*zp+xp*(y1*z2-y2*z1)+yp*(x2*z1-x1*z2)) / det
b1 = ((x2*y0-x0*y2)*zp+xp*(y2*z0-y0*z2)+yp*(x0*z2-x2*z0)) / det
b2 = ((x0*y1-x1*y0)*zp+xp*(y0*z1-y1*z0)+yp*(x1*z0-x0*z1)) / det
That's quite a few additions, subtractions and multiplications - three divisions - but no sqrts or trig functions. It obviously does take longer than the pure 2D calcs, but depending on the complexity of your projection heuristics and calcs, this might end up being the fastest route.
As I mentioned - I have no idea what I'm talking about - but maybe this will work, or maybe someone else can come along and correct it.
Update: Disregard, this approach does not work in all cases
I think I have found a valid solution to this problem.
NB: I require a projection to 2D space rather than working with 3D Barycentric co-ordinates as I am challenged to make the most efficient algorithm possible. The additional overhead incurred by finding a suitable projection plane should still be smaller than the overhead incurred when using more complex operations such as sqrt or sin() cos() functions (I guess I could use lookup tables for sin/cos but this would increase the memory footprint and defeats the purpose of this assignment).
My first attempts found the delta between the min/max values on each axis of the polygon, then eliminated the axis with the smallest delta. However, as suggested by #PeterTaylor there are cases where dropping the axis with the smallest delta, can yeild a straight line rather than a triangle when projected into 2D space. THIS IS BAD.
Therefore my revised solution is as follows...
Find each sub delta on each axis for the polygon { abs(v1.x-v0.x), abs(v2.x-v1.x), abs(v0.x-v2.x) }, this results in 3 scalar values per axis.
Next, multiply these scaler values to compute a score. Repeat this, calculating a score for each axis. (This way any 0 deltas force the score to 0, automatically eliminating this axis, avoiding triangle degeneration)
Eliminate the axis with the lowest score to form the projection, e.g. If the lowest score is in the x-axis, project onto the y-z plane.
I have not had time to unit test this approach but after preliminary tests it seems to work rather well. I would be eager to know if this is in-fact the best approach?
After much discussion there is actually a pretty simple way to solve the original problem of knowing which axis to drop when projecting to 2D space. The answer is described in 3D Math Primer for Graphics and Game Development as follows...
"A solution to this dilemma is to
choose the plane of projection so as
to maximize the area of the projected
triangle. This can be done by
examining the plane normal; the
coordinate that has the largest
absolute value is the coordinate that
we will discard. For example, if the
normal is [–1, 0, 0], then we would
discard the x values of the vertices
and p, projecting onto the yz plane."
My original solution which involved computing a score per axis (using sub deltas) is flawed as it is possible to generate a zero score for all three axis, in which case the axis to drop remains undetermined.
Using the normal of the collision plane (which can be precomputed for efficiency) to determine which axis to drop when projecting into 2D is therefore the best approach.
To project a point p onto the plane defined by the vertices v0, v1 & v2 you must calculate a rotation matrix. Let us call the projected point pd
e1 = v1-v0
e2 = v2-v0
r = normalise(e1)
n = normalise(cross(e1,e2))
u = normalise(n X r)
temp = p-v0
pd.x = dot(temp, r)
pd.y = dot(temp, u)
pd.z = dot(temp, n)
Now pd can be projected onto the plane by setting pd.z=0
Also pd.z is the distance between the point and the plane defined by the 3 triangles. i.e. if the projected point lies within the triangle, pd.z is the distance to the triangle.
Another point to note above is that after rotation and projection onto this plane, the vertex v0 lies is at the origin and v1 lies along the x axis.
HTH
I'm not sure that the suggestion is actually the best one. It's not too hard to project to the plane containing the triangle. I assume here that p is actually in that plane.
Let d1 = sqrt((v1-v0).(v1-v0)) - i.e. the distance v0-v1.
Similarly let d2 = sqrt((v2-v0).(v2-v0))
v0 -> (0,0)
v1 -> (d1, 0)
What about v2? Well, you know the distance v0-v2 = d2. All you need is the angle v1-v0-v2. (v1-v0).(v2-v0) = d1 d2 cos(theta). Wlog you can take v2 as having positive y.
Then apply a similar process to p, with one exception: you can't necessarily take it as having positive y. Instead you can check whether it has the same sign of y as v2 by taking the sign of (v1-v0)x(v2-v0) . (v1-v0)x(p-v0).
As an alternative solution, you could use a linear algebra solver on the matrix equation for the tetrahedral case, taking as the fourth vertex of the tetrahedron v0 + (v1-v0)x(v2-v0) and normalising if necessary.
You shouldn't need to determine the optimal area to find a decent projection.
It's not strictly necessary to find the "best" projection at all, just one that's good enough, and that doesn't degenerate to a line when projected into 2D.
EDIT - algorithm deleted due to degenerate case I hadn't thought of
I have a 3D Plane defined by two 3D Vectors:
P = a Point which lies on the Plane
N = The Plane's surface Normal
And I want to calculate any vector that lies on the plane.
Take any vector, v, not parallel to N, its vector cross product with N ( w1 = v x N ) is a vector that is parallel to the plane.
You can also take w2 = v - N (v.N)/(N.N) which is the projection of v into plane.
A point in the plane can then be given by x = P + a w, In fact all points in the plane can be expressed as
x = P + a w2 + b ( w2 x N )
So long as the v from which w2 is "suitable".. cant remember the exact conditions and too lazy to work it out ;)
If you want to determine if a point lies in the plane rather than find a point in the plane, you can use
x.N = P.N
for all x in the plane.
If N = (xn, yn, zn) and P = (xp, yp, zp), then the plane's equation is given by:
(x-xp, y-yp, z-zp) * (xn, yn, zn) = 0
where (x, y, z) is any point of the plane and * denotes the inner product.
And I want to calculate any vector
that lies on the plane.
If I understand correctly You need to check if point belongs to the plane?
http://en.wikipedia.org/wiki/Plane_%28geometry%29
You mast check if this equation: nx(x − x0) + ny(y − y0) + nz(z − z0) = 0 is true for your point.
where: [nx,ny,nz] is normal vector,[x0,y0,z0] is given point, [x,y,z] is point you are checking.
//edit
Now I'm understand Your question. You need two linearly independent vectors that are the planes base. Sow You need to fallow Michael Anderson answerer but you must add second vector and use combination of that vectors. More: http://en.wikipedia.org/wiki/Basis_%28linear_algebra%29