Project point on 2D triangle back into 3D? - math

I'm not sure what to search, so I haven't been able to find what I need.
Say I have a 3D triangle with points [0, 1, 1], [1, 0.5, 0.5], [0, 0, 0]. I discard the Z component to create a 2D triangle with points [0, 1], [1, 0.5], [0, 0]. (I think this is an orthographic projection?) Through an unimportant process I find some 2D point that lies within the 2D triangle, say [0.5, 0.5].
How do I take that 2D point and find what its Z value should be to have it lie on the plane formed by the original 3D triangle?
Answers (or dupe links!) that describe maths through code rather than mathematical symbols would be greatly appreciated; I struggle to read the types of answers you get on Math.SE.

You can use barycentric coordinates...
So you got 2D triangle q0,q1,q2 and corresponding 3D triangle p0,p1,p2 and want to convert 2D point q into 3D point p
compute barycentric coordinates u,v of q within q0,q1,q2
see how to compute barycentric coordinates
convert u,v to cartessian using triangle p0,p1,p2
So when put together:
| u | | (q1.x - q0.x) , (q2.x - q0.x) , q0.x | | q.x |
| v | = inverse | (q1.y - q0.y) , (q2.y - q0.y) , q0.y | * | q.y |
| 1 | | 0 , 0 , 1 | | 1 |
p.x = p0.x + (p1.x - p0.x) * u + (p2.x - p0.x) * v
p.y = p0.y + (p1.y - p0.y) * u + (p2.y - p0.y) * v
p.z = p0.z + (p1.z - p0.z) * u + (p2.z - p0.z) * v

Expanding on #Spektre's excellent answer, this was how I implemented a working solution. I'm working with Unity, so I used Ivan Kutskir's awesome lightweight C# matrix class to handle the matrix maths. There's probably faster/cleaner ways to do this but this was very easy and works correctly.
Obviously you have to ensure that when you discard the Z axis, you don't end up with a degenerate triangle.
// tri is a 3D triangle with points p0, p1 and p2
// point is a 2D point within that triangle, assuming the Z axis is discarded
/*
Equivalent to this part of #Spektre's answer:
| u | | (q1.x - q0.x) , (q2.x - q0.x) , q0.x | | q.x |
| v | = inverse | (q1.y - q0.y) , (q2.y - q0.y) , q0.y | * | q.y |
| 1 | | 0 , 0 , 1 | | 1 |
*/
Matrix m1 = new Matrix(3, 3);
Matrix m2 = new Matrix(3, 1);
m1[0, 0] = tri.p1.x - tri.p0.x;
m1[0, 1] = tri.p2.x - tri.p0.x;
m1[0, 2] = tri.p0.x;
m1[1, 0] = tri.p1.y - tri.p0.y;
m1[1, 1] = tri.p2.y - tri.p0.y;
m1[1, 2] = tri.p0.y;
m1[2, 0] = 0;
m1[2, 1] = 0;
m1[2, 2] = 1;
m2[0, 0] = point.x;
m2[1, 0] = point.y;
m2[2, 0] = 1;
Matrix mResult = m1.Invert() * m2;
float u = (float)mResult[0, 0];
float v = (float)mResult[1, 0];
/*
Equivalent to this part of #Spektre's answer:
p.x = p0.x + (p1.x - p0.x) * u + (p2.x - p0.x) * v
p.y = p0.y + (p1.y - p0.y) * u + (p2.y - p0.y) * v
p.z = p0.z + (p1.z - p0.z) * u + (p2.z - p0.z) * v
*/
float newX = tri.p0.x + (tri.p1.x - tri.p0.x) * u + (tri.p2.x - tri.p0.x) * v;
float newY = tri.p0.y + (tri.p1.y - tri.p0.y) * u + (tri.p2.y - tri.p0.y) * v;
float newZ = tri.p0.z + (tri.p1.z - tri.p0.z) * u + (tri.p2.z - tri.p0.z) * v;
Vector3 newPoint = new Vector3(newX, newY, newZ);
Alternatively, you can achieve the same result without the matrix (though this may be a less robust method, I'm not sure). To calculate the barycentric coordinates I used this implementation, but the accepted answer also works.
// tri is a 3D triangle with points p0, p1 and p2
// point is a 2D point within that triangle, assuming the Z axis is discarded
// Find the barycentric coords for the chosen 2D point...
float u, v, w = 0;
Barycentric2D(point, new Vector2(tri.p0.x, tri.p0.y), new Vector2(tri.p1.x, tri.p1.y), new Vector2(tri.p2.x, tri.p2.y), out u, out v, out w);
// ...and then find what the Z value would be for those barycentric coords in 3D
float newZ = tri.p0.z * u + tri.p1.z * v + tri.p2.z * w;
Vector3 newPoint = new Vector3(point.x, point.y, newZ);
// https://gamedev.stackexchange.com/a/63203/48697
void Barycentric2D(Vector2 p, Vector2 a, Vector2 b, Vector2 c, out float u, out float v, out float w)
{
Vector2 v0 = b - a;
Vector2 v1 = c - a;
Vector2 v2 = p - a;
float den = v0.x * v1.y - v1.x * v0.y;
v = (v2.x * v1.y - v1.x * v2.y) / den;
w = (v0.x * v2.y - v2.x * v0.y) / den;
u = 1.0f - v - w;
}

Related

Find maximum angle of box in slot

How would I find the maximum possible angle (a) which a rectangle of width (W) can be at within a slot of width (w) and depth (h) - see my crude drawing below
Considering w = hh + WW at the picture:
we can write equation
h * tan(a) + W / cos(a) = w
Then, using formulas for half-angles and t = tan(a/2) substitution
h * 2 * t / (1 - t^2) + W * (1 + t^2) / (1 - t^2) = w
h * 2 * t + W * (1 + t^2) = (1 - t^2) * w
t^2 * (W + w) + t * (2*h) + (W - w) = 0
We have quadratic equation, solve it for unknown t, then get critical angle as
a = 2 * atan(t)
Quick check: Python example for picture above gives correct angle value 18.3 degrees
import math
h = 2
W = 4.12
w = 5
t = (math.sqrt(h*h-W*W+w*w) - h) / (W + w)
a = math.degrees(2 * math.atan(t))
print(a)
Just to elaborate on the above answer as it is not necessarly obvious, this is why why you can write equation:
h * tan(a) + W / cos(a) = w
PS: I suppose that the justification for "why a is the maximum angle" is obvious

Simplex noise magic numbers explanation

I was reading The Book of Shader's chapter about simplex noise (click for full code), and had difficulty understanding a few magic numbers used here. This will not be a bug related thread, but should make sense under SO's community criteria.
See these lines:
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
// random numbers for gradient generation
// element wise: 0.5 - x ^ 4
// use max clamp element wise: if x < 0 then m = 0 (i.e. the gradient from the vertex is 0)
// x1, x2, x3: 3 verteces of triangle simplex
// dot product is the distance from v to simpelx verteces
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
vec3 x = 2.0 * fract(p * C.www) - 1.0; // gradient? 2 * fract(x / 41) - 1, in [-1, 1]
vec3 h = abs(x) - 0.5; // in [-0.5, 0.5]
vec3 ox = floor(x + 0.5); // in [-1, 1]
vec3 a0 = x - ox;
// (x - ox) ^ 2 + (abs(x) - .5) ^ 2
m *= 1.79284291400159 - 0.85373472095314 * ( a0 * a0 + h * h ); // ???
Meanwhile I understand in another java implementation they used a precomputed 2d array table and a random index to look up gradients, the above lines don't make much sense for me. I guess m stands for weights for each vertex's gradient contribution, and the rest remains a puzzle.
Hope there could be resources / comments help me out on understanding this snippet.

Rotation About an Arbitrary Axis in 3 Dimensions Using Matrix

I come accross a math problem about Interactive Computer Graphics.
I summarize and abstract this problem as follows:
I'm going to rotation a 3d coordinate P(x1,y1,z1) around a point O(x0,y0,z0)
and there are 2 vectors u and v which we already know.
u is the direction to O before transformation.
v is the direction to O after transformation.
I want to know how to conduct the calculation and get the coordinate of Q
Thanks a lot.
Solution:
Rotation About an Arbitrary Axis in 3 Dimensions using the following matrix:
rotation axis vector (normalized): (u,v,w)
position coordinate of the rotation center: (a,b,c)
rotation angel: theta
Reference:
https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxnbGVubm11cnJheXxneDoyMTJiZTZlNzVlMjFiZTFi
for just single point no rotations is needed ... so knowns are:
u,v,O,P
so we now the distance is not changing:
|P-O| = |Q-O|
and directions are parallel to u,v so:
Q = O + v*(|P-O|/|v|)
But I suspect you want to construct rotation (transform matrix) such that more points (mesh perhaps) are transformed. If that is true then you need at least one known to get this right. Because there is infinite possible rotations transforming P -> Q but the rest of the mesh will be different for each ... so you need to know at least 2 non trivial points pair P0,P1 -> Q0,Q1 or axis of rotation or plane parallel to rotation or any other data known ...
Anyway in current state you can use as rotation axis vector perpendicular to u,v and angle obtained from dot product:
axis = cross (u,v)
ang = +/-acos(dot(u,v))
You just need to find out the sign of angle so try both and use the one for which the resultinq Q is where it should be so dot(Q-O,v) is max. To rotate around arbitrary axis and point use:
Rodrigues_rotation_formula
Also this might be helpfull:
Understanding 4x4 homogenous transform matrices
By computing dot product between v and u get the angle l between the vectors. Do a cross product of v and u (normalized) to produce axis of rotation vector a. Let w be a vector along vector u from O to P. To rotate point P into Q apply the following actions (in pseudo code) having axis a and angle l computed above:
float4 Rotate(float4 w, float l, float4 a)
{
float4x4 Mr = IDENTITY;
quat_t quat = IDENTITY;
float4 t = ZERO;
float xx, yy, zz, xy, xz, yz, wx, wy, wz;
quat[X] = a[X] * sin((-l / 2.0f));
quat[Y] = a[Y] * sin((-l / 2.0f));
quat[Z] = a[Z] * sin((-l / 2.0f));
quat[W] = cos((-l / 2.0f));
xx = quat[X] * quat[X];
yy = quat[Y] * quat[Y];
zz = quat[Z] * quat[Z];
xy = quat[X] * quat[Y];
xz = quat[X] * quat[Z];
yz = quat[Y] * quat[Z];
wx = quat[W] * quat[X];
wy = quat[W] * quat[Y];
wz = quat[W] * quat[Z];
Mr[0][0] = 1.0f - 2.0f * (yy + zz);
Mr[0][1] = 2.0f * (xy + wz);
Mr[0][2] = 2.0f * (xz - wy);
Mr[0][3] = 0.0f;
Mr[1][0] = 2.0f * (xy - wz);
Mr[1][1] = 1.0f - 2.0f * (xx + zz);
Mr[1][2] = 2.0f * (yz + wx);
Mr[1][3] = 0.0f;
Mr[2][0] = 2.0f * (xz + wy);
Mr[2][1] = 2.0f * (yz - wx);
Mr[2][2] = 1.0f - 2.0f * (xx + yy);
Mr[2][3] = 0.0f;
Mr[3][0] = 0.0f;
Mr[3][1] = 0.0f;
Mr[3][2] = 0.0f;
Mr[3][3] = 1.0f;
w = Mr * w;
return w;
}
Point Q is at the end of the rotated vector w. Algorithm used in the pseudo code is quaternion rotation.
If you know u, v, P, and O then I would suggest that you compute |OP| which should be preserved under rotations. Then multiply this length by the unit vector -v (I assumed u, v are unit vectors: if not - normalize them) and translate the origin by this -|OP|v vector. The negative sign in front of v comes from the description given in your question:"v is the direction to O after transformation".
P and Q are at the same distance R to O
R = sqrt( (x1-x0)^2 + (y1-y0)^2 + (z1-z0)^2 )
and OQ is collinear to v, so OQ = v * R / ||v|| where ||v|| is the norm of v
||v|| = sqrt( xv^2 + yv^2 + zv^2 )
So the coordinates of Q(xq,yq,zq) are:
xq= xo + xv * R / ||v||
yq= yo + yv * R / ||v||
zq= zo + zv * R / ||v||

Sphere center point and radius from 3 points on the surface

Is it possible to find he center of sphere and its radius from 3 points on the surface ?
I'm building a model for a segmented brain structure were the three points would be within the structure; head, tail and middle.
Thank you,
Express that the center of the sphere is equidistant to the three given points and coplanar with them (assuming that the three given points are on a great circle).
(X - Xa)² + (Y - Ya)² + (Z - Za)² = R²
(X - Xb)² + (Y - Yb)² + (Z - Zb)² = R²
(X - Xc)² + (Y - Yc)² + (Z - Zc)² = R²
|X Y Z 1|
|Xa Ya Za 1|
|Xb Yb Zb 1| = 0
|Xc Yc Zc 1|
Subtracting the first equation from the second and the third, you get rid of the quadratic terms.
(2X - Xb - Xa)(Xb - Xa) + (2Y - Yb - Ya)(Yb - Ya) + (2Z - Zb - Za)(Zb - Za) = 0
(2X - Xc - Xa)(Xc - Xa) + (2Y - Yc - Ya)(Yc - Ya) + (2Z - Zc - Za)(Zc - Za) = 0
Now you have an easy linear system of 3 equations in 3 unknowns.
For conciseness you can translate the three points so that Xa=Ya=Za=0, and the equations simplify as
|X Y Z |
|Xb Yb Zb| = 0
|Xc Yc Zc|
(2X - Xb) Xb + (2Y - Yb) Yb + (2Z - Zb) Zb = 0
(2X - Xc) Xc + (2Y - Yc) Yc + (2Z - Zc) Zc = 0
or
(Yb Zc - Yc Zb) X + (Zb Xc - Zc Xb) Y + (Xb Yc - Xc Yb) Z = 0
2 Xb X + 2 Yb Y + 2 Zb Z = Xb² + Yb² + Zb²
2 Xc X + 2 Yc Y + 2 Zc Z = Xc² + Yc² + Zc²
Then, R² = X² + Y² + Z², and don't forget to translate back.
It is indeed possible, but it will always give you a sphere which the surface input points will be on its equator (meaning there are other, larger spheres that have these 3 points on it's surface if there's a fourth not coplanar point given).
But I think the smallest sphere is what you want. The following code written in C# gives you a Vector3 with the center of the sphere. The distance can then be obtained with Vector3.Distance (or a simple pithagorean distance) between any of the input points and the resulting center.
static public Vector3 BarycentricToWorld3D(Vector3 p1, Vector3 p2, Vector3 p3, float u, float v, float w) {
return (u * p1 + v * p2 + w * p3) / (u + v + w);
}
static public Vector3 CircleBariCenter3D(Vector3 p1, Vector3 p2, Vector3 p3) {
Vector3 a = p3 - p2;
Vector3 b = p1 - p3;
Vector3 c = p2 - p1;
float u = Vector3.Dot(a, a) * Vector3.Dot(c, b);
float v = Vector3.Dot(b, b) * Vector3.Dot(c, a);
float w = Vector3.Dot(c, c) * Vector3.Dot(b, a);
return BarycentricToWorld3D(p1, p2, p3, u, v, w);
}
Notice there's no checking for colinear points, which will make this fail. It should be trivial to add such checking by simply feeding this colinear points and watching it fail in the debugger.
Credits: I got a maxscript source for this 10+ years ago and lost the original author and location, and translated it into C# quite recently.

Computing the 3D coordinates on a unit sphere from a 2D point

I have a square bitmap of a circle and I want to compute the normals of all the pixels in that circle as if it were a sphere of radius 1:
The sphere/circle is centered in the bitmap.
What is the equation for this?
Don't know much about how people program 3D stuff, so I'll just give the pure math and hope it's useful.
Sphere of radius 1, centered on origin, is the set of points satisfying:
x2 + y2 + z2 = 1
We want the 3D coordinates of a point on the sphere where x and y are known. So, just solve for z:
z = ±sqrt(1 - x2 - y2).
Now, let us consider a unit vector pointing outward from the sphere. It's a unit sphere, so we can just use the vector from the origin to (x, y, z), which is, of course, <x, y, z>.
Now we want the equation of a plane tangent to the sphere at (x, y, z), but this will be using its own x, y, and z variables, so instead I'll make it tangent to the sphere at (x0, y0, z0). This is simply:
x0x + y0y + z0z = 1
Hope this helps.
(OP):
you mean something like:
const int R = 31, SZ = power_of_two(R*2);
std::vector<vec4_t> p;
for(int y=0; y<SZ; y++) {
for(int x=0; x<SZ; x++) {
const float rx = (float)(x-R)/R, ry = (float)(y-R)/R;
if(rx*rx+ry*ry > 1) { // outside sphere
p.push_back(vec4_t(0,0,0,0));
} else {
vec3_t normal(rx,sqrt(1.-rx*rx-ry*ry),ry);
p.push_back(vec4_t(normal,1));
}
}
}
It does make a nice spherical shading-like shading if I treat the normals as colours and blit it; is it right?
(TZ)
Sorry, I'm not familiar with those aspects of C++. Haven't used the language very much, nor recently.
This formula is often used for "fake-envmapping" effect.
double x = 2.0 * pixel_x / bitmap_size - 1.0;
double y = 2.0 * pixel_y / bitmap_size - 1.0;
double r2 = x*x + y*y;
if (r2 < 1)
{
// Inside the circle
double z = sqrt(1 - r2);
.. here the normal is (x, y, z) ...
}
Obviously you're limited to assuming all the points are on one half of the sphere or similar, because of the missing dimension. Past that, it's pretty simple.
The middle of the circle has a normal facing precisely in or out, perpendicular to the plane the circle is drawn on.
Each point on the edge of the circle is facing away from the middle, and thus you can calculate the normal for that.
For any point between the middle and the edge, you use the distance from the middle, and some simple trig (which eludes me at the moment). A lerp is roughly accurate at some points, but not quite what you need, since it's a curve. Simple curve though, and you know the beginning and end values, so figuring them out should only take a simple equation.
I think I get what you're trying to do: generate a grid of depth data for an image. Sort of like ray-tracing a sphere.
In that case, you want a Ray-Sphere Intersection test:
http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter1.htm
Your rays will be simple perpendicular rays, based off your U/V coordinates (times two, since your sphere has a diameter of 2). This will give you the front-facing points on the sphere.
From there, calculate normals as below (point - origin, the radius is already 1 unit).
Ripped off from the link above:
You have to combine two equations:
Ray: R(t) = R0 + t * Rd , t > 0 with R0 = [X0, Y0, Z0] and Rd = [Xd, Yd, Zd]
Sphere: S = the set of points[xs, ys, zs], where (xs - xc)2 + (ys - yc)2 + (zs - zc)2 = Sr2
To do this, calculate your ray (x * pixel / width, y * pixel / width, z: 1), then:
A = Xd^2 + Yd^2 + Zd^2
B = 2 * (Xd * (X0 - Xc) + Yd * (Y0 - Yc) + Zd * (Z0 - Zc))
C = (X0 - Xc)^2 + (Y0 - Yc)^2 + (Z0 - Zc)^2 - Sr^2
Plug into quadratic equation:
t0, t1 = (- B + (B^2 - 4*C)^1/2) / 2
Check discriminant (B^2 - 4*C), and if real root, the intersection is:
Ri = [xi, yi, zi] = [x0 + xd * ti , y0 + yd * ti, z0 + zd * ti]
And the surface normal is:
SN = [(xi - xc)/Sr, (yi - yc)/Sr, (zi - zc)/Sr]
Boiling it all down:
So, since we're talking unit values, and rays that point straight at Z (no x or y component), we can boil down these equations greatly:
Ray:
X0 = 2 * pixelX / width
Y0 = 2 * pixelY / height
Z0 = 0
Xd = 0
Yd = 0
Zd = 1
Sphere:
Xc = 1
Yc = 1
Zc = 1
Factors:
A = 1 (unit ray)
B
= 2 * (0 + 0 + (0 - 1))
= -2 (no x/y component)
C
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2 + (0 - 1) ^ 2 - 1
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2
Discriminant
= (-2) ^ 2 - 4 * 1 * C
= 4 - 4 * C
From here:
If discriminant < 0:
Z = ?, Normal = ?
Else:
t = (2 + (discriminant) ^ 1 / 2) / 2
If t < 0 (hopefully never or always the case)
t = -t
Then:
Z: t
Nx: Xi - 1
Ny: Yi - 1
Nz: t - 1
Boiled farther still:
Intuitively it looks like C (X^2 + Y^2) and the square-root are the most prominent figures here. If I had a better recollection of my math (in particular, transformations on exponents of sums), then I'd bet I could derive this down to what Tom Zych gave you. Since I can't, I'll just leave it as above.

Resources