Alright, so I'm trying to achieve whats in this image:
I believe this would be a barycentric coord system, but where the X always equals 1? Basically I need it to only increase/decrease when I move towards/away from the highest point in my triangle. This is the code I got for it so far (Note I'm using the LWJGL library in java).
public float getDist( Vector3f p, Vector3f a, Vector3f b, Vector3f c )
{
Vector3f v0 = new Vector3f(0,0,0);
Vector3f.sub( c, a, v0 );
Vector3f v1 = new Vector3f(0,0,0);
Vector3f.sub( b, a, v1);
Vector3f v2 = new Vector3f(0,0,0);
Vector3f.sub( p, a, v2 );
float dot00 = Vector3f.dot(v0, v0);
float dot01 = Vector3f.dot(v0, v1);
float dot02 = Vector3f.dot(v0, v2);
float dot11 = Vector3f.dot(v1, v1);
float dot12 = Vector3f.dot(v1, v2);
float inverse = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * inverse;
float v = (dot00 * dot12 - dot01 * dot02) * inverse;
if((u >= 0) && (v >= 0) && (u + v <= 1)) return (float) (Math.sin(u) * Math.cos(v));
else return 0;
}
edit: I guess what I'm asking is: Is there a way to get the distance that a point inside a triangle has travailed from the lowest point the triangle has in space where 1 would be the highest point on the triangle (mosty far away from the lowist) without taking it's deviating vector into account? I.E. notice that the two red dots on the image have the same coords even though they have different dists from the top's x?
Edit2:
If I understand what you're saying: you have a triangle (abc) in 3D coordinates, and you want to evaluate the elevation of a fourth point (p) above one edge, proportionate to its opposite point:
b --------------- 1.0
|\
| \
| p\ ------------- (result)
| \
a----c ---------- 0.0
You are correct in that this result is one of three barycentric coordinates for a point on the triangle.
I recommend something like the following:
find the normal of the plane of the triangle: vABC = cross(c-a, b-a)
find a normal perpendicular to vABC and ac: vPerpAC = cross(c-a, vABC)
evaluate the vector ab with respect to it: sAB = dot(vPerpAC, b-a)
evaluate your target point: sAP = dot(vPerpAC, p-a)
your final result is the ratio of these last two evaluations: return sAP / sAB
This should be robust against points that aren't actually on the triangle: they are effectively projected perpendicularly onto the abc plane.
Note that, if you know abc beforehand, you can precalculate the following values:
the scaled normal: vScaled = vPerpAC / sAB
the scaled offset: sScaled = dot(vScaled, a)
and compute the result of a series of points p more efficiently:
return dot(vScaled, p) - sScaled
This effectively precomputes an oriented plane, pre-scaled to directly provide the desired result.
Related
I'm rendering some basic triangles, and I'm generating the normals in code. This is my "calculate normals" method. It gets the face normal of a triangle from its three vertices:
public static Vector3f calculateNormal(Vector3f v0, Vector3f v1, Vector3f v2) {
Vector3f u = Vector3f.sub(v2, v0, null);
Vector3f w = Vector3f.sub(v1, v0, null);
Vector3f n = new Vector3f();
Vector3f.cross(u, w, n);
n.normalise(n);
if (Float.isNaN(n.x) || Float.isNaN(n.y) || Float.isNaN(n.z)) {
System.out.println("It's NaN!");
return new Vector3f(0,1,0);
}
return n;
}
Except it outputs "NaN" for about half the triangles. I tried switching the order of the vertices, and that doesn't do anything.
Your calculateNormal function looks okay to me :)
But, the triangles with NaN normals, probably have co-linear vertices (i.e., they are degenerated triangles). Try checking if n.length is zero (or almost zero) to detect this edge case.
Without knowing which math library you are using, I cannot answer specifically. However, to find the cross product with 3 points, you need to find the vector from one point to the other two.
Vector3f toPoint2 = point2 - point1;
Vector3f toPoint3 = point3 - point1;
Once you have the two vectors, you need to find their cross product, which is orthogonal to both toPoint2 and toPoint3. In order to find the cross product, find the determinate of the following matrix:
[i j k ]
[toPoint2.x toPoint2.y toPoint2.z]
[toPoint3.x toPoint3.y toPoint3.z]
The cross product is:
Vector3f vec = new Vector3f(toPoint2.y * toPoint3.z - toPoint2.z * toPoint3.y,
toPoint2.z * toPoint3.x - toPoint2.x * toPoint3.z,
toPoint2.x * toPoint3.y - toPoint2.y * toPoint3.x)
This cross product will result in the normal vector of point1 with respect to point2 and point3.
I am writing a raytracer in java, and I was able to get tracing of spheres working, but I believe I have something wrong with how I am tracing triangles.
Here is the basic algorithm, as I understand it:
First determine if the ray even intersects the plane that the triangle is on.
Clip all points so they are on the same plane as the triangle (so to the xy plane, as an example).
Determine if the potential intersection point falls inside or out of the triangle, based on the number of polygon edges you cross when sending out a ray in an arbitrary direction along the new plane.
Now, here is my implementation of that (specifically the first point):
public Vector getIntersectionVector(Ray ray)
{
Vector planeIntersectionVector = getPlaneIntersectionVector(ray, getPlaneNormal());
if (planeIntersectionVector != null)
{
if (isIntersectionVectorInsideTriangle(planeIntersectionVector))
{
return planeIntersectionVector;
}
else
{
return null;
}
}
else
{
return null;
}
}
Where getPlaceIntersectionVector() is:
private Vector getPlaneIntersectionVector(Ray ray, Vector planeNormal)
{
double vd = planeNormal.dotProduct(ray.getDirection());
//(p_n \dot r_d) == 0, parallel. (p_n \dot r_d) > 0 Plane normal pointing away from ray.
if (vd >= 0)
{
return null;
}
double distance = planeNormal.distance(0d, 0d, 0d);
double vo = -(planeNormal.dotProduct(ray.getOrigin()) + distance);
double intersectionDistance = vo / vd;
//intersectionDistance <= 0 means the "intersection" is behind the ray, so not a real intersection
return (intersectionDistance <= 0) ? null : ray.getLocation(intersectionDistance);
}
Which basically tries to mimic this:
And this:
Where t is the distance along the ray that the point hits, ro is the origin of the ray, rd is the direction of the ray, pn refers to the plane normal of the triangle/plane, and d is the distance from the plane that the triangle is on to the origin (0,0,0)
Am I doing that wrong? When I send out the ray from the first pixel in the image (0,0), I am seeing that the intersectionDistance (or t) is almost 1100, which intuitively seems wrong to me. I would think that the intersection point would be much closer.
Here is the relevant data:
Ray origin (0,0,1), Ray Direction is roughly (0.000917, -0.4689, -0.8833).
Triangle has vertices as (-0.2, 0.1, 0.1), (-0.2, -0.5, 0.2), (-0.2, 0.1, -0.3), which makes the plane normal (-1, 0, 0).
According to my code, the Ray intersects the plane 1090 distance away, which as I mentioned before, seems wrong to me. The scene is only -1.0 to 1.0 in every direction, which means the intersection is very very far in the distance.
Am I doing the plane intersection wrong?
Please let me know where to clarify points, and if you need any more information.
The problem is this line:
double distance = planeNormal.distance(0d, 0d, 0d);
A plane is defined by a normal and a distance from the plane to the origin. The distance of a vector from the origin is the length of a vector, so what you are calculating there is just the length of the plane normal (which will always be 1.0 if it has been normalized).
The distance of the plane from the origin is an extra piece of information that needs to be passed into your function, you can't just calculate it from the normal because it's independent of it (you can can have lots of planes with normals pointing in the same direction at different distances from the origin).
So define your function something like this:
private Vector getPlaneIntersectionVector(Ray ray, Vector planeNormal, double planeDistance)
and call it something like this:
Vector planeIntersectionVector = getPlaneIntersectionVector(ray, getPlaneNormal(), getPlaneDistance());
Then you can calculate vo like this:
double vo = -(planeNormal.dotProduct(ray.getOrigin()) + planeDistance);
Slightly different approach:
Let's triangle vertices are V0, V1, V2
Edge vectors are
A = V1-V0
B = V2 - V0
Ray has parametric equation (as you wrote)
P = R0 + t * Rd
From the other side, intersection point has parametric coordinates u, v in the triangle plane
P = V0 + u * A + v * B
So you can write system of three linear equation for x, y, z coordinates and solve it for t, u, v. If determinant ius non-zero (ray is not parallel to the plane), and t >=0, and u, v, u+v lie in range 0..1, then P is inside triangle.
R0.X + t * Rd.X = V0.X + u * A.X + v * B.X
R0.Y + t * Rd.Y = V0.Y + u * A.Y + v * B.Y
R0.Z + t * Rd.Z = V0.Z + u * A.Z + v * B.Z
3d terrain.
I have 3 vertices that define a plane. (the 3 nearest pixels in a height map)
I have an x,z on that plane. (my location in the world)
How do you find the y-intercept? (so that I stand on the surface of that plane)
The equation of a plane is:
Ax + By + Cz = D, where D = Ax0 + By0 + Cz0,
If you have three vertices, find two vectors from the vertices. For example, for three vertices T, U, V, there would be, for example, a vector TU, and a a vector UV.
Find the cross product of the two vectors. That's your normal vector, n, which has three components n1, n2, and n3.
A = n1
B = n2
C = n3
Take one of the points. The coordinates of that point are x0, y0, and z0.
Input this into the equation to calculate D.
Then substitute your x and z for x and z and solve for y!
So in the end y is:
y = (A*x0 + B*y0 + C*z0 - A*x - C*z)/B
Somebody correct me if my algebra was wrong.
You can calculate the cross product like this:
For two vectors a and b, with components a1, a2, a3 and b1, b2, b3, respectively, the cross product is :
which goes to:
A = the coefficient of i-hat (the bolded i)
B = the coefficient of j-hat (the bolded j)
C = the coefficient of k-hat (the bolded k)
You say that you are looking at the three nearest pixels in the height map, which makes me assume that you have a regular grid from which you extract your vertices. In this case, you can use image interpolation methods to perform linear being similar to the answer from eboix or bicubic interpolation. Your height value is then equivalent to the brightness value in the image processing domain.
The math is much easier in the linear case, and the grid structure makes it possible to use a simple form. Let c by your cell size, and p, q, r the height values of your 3 vertices, like this
p q
+--.
| /
|/
r
and x, and y the distances along the legs of your right angled triangle. Where the triangle is of course the projection of your 3 vertices on the x, y plane. Then your interpolated height value is
z = (q-p)/c * x + (r-q)/c * y
I've been trying for the past few days to make a working implementation of a virtual trackball for the user interface for a 3D graphing-like program. But I'm having trouble.
Looking at the numbers and many tests the problems seems to be the actual concatenation of my quaternions but I don't know or think so. I've never worked with quaternions or virtual trackballs before, this is all new to me. I'm using the Quaternion class supplied by JOGL. I tried making my own and it worked (or at least as far a I know) but it was a complete mess so I just went with JOGL's.
When I do not concatenate the quaternions the slight rotations I see seem to be what I want, but of course It's hard when it's only moving a little bit in any direction. This code is based off of the Trackball Tutorial on the OpenGL wiki.
When I use the Quaternion class's mult (Quaternion q) method the graph hardly moves (even less than not trying to concatenate the quaternions).
When I tried Quaternionclass'sadd (Quaternion q)` method for the fun of it I get something that at the very least rotates the graph but not in any coherent way. It spazzes out and rotates randomly as I move the mouse. Occasionally I'll get quaternions entirely filled with NaN.
In my code I will not show either of these, I'm lost with what to do with my quaternions. I know I want to multiply them because as far as I'm aware that's how they are concatenated. But like I said I've had no success, I'm assuming the screw up is somewhere else in my code.
Anyway, my setup has a Trackball class with a public Point3f projectMouse (int x, int y) method and a public void rotateFor (Point3f p1, Point3f p2), Where Point3f is a class I made. Another class called Camera has a public void transform (GLAutoDrawable g) method which will call OpenGL methods to rotate based on the trackball's quaternion.
Here's the code:
public Point3f projectMouse (int x, int y)
{
int off = Screen.WIDTH / 2; // Half the width of the GLCanvas
x = x - objx_ - off; // obj being the 2D center of the graph
y = off - objy_ - y;
float t = Util.sq(x) + Util.sq(y); // Util is a class I made with
float rsq = Util.sq(off); // simple some math stuff
// off is also the radius of the sphere
float z;
if (t >= rsq)
z = (rsq / 2.0F) / Util.sqrt(t);
else
z = Util.sqrt(rsq - t);
Point3f result = new Point3f (x, y, z);
return result;
}
Here's the rotation method:
public void rotateFor (Point3f p1, Point3f p2)
{
// Vector3f is a class I made, I already know it works
// all methods in Vector3f modify the object's numbers
// and return the new modify instance of itself
Vector3f v1 = new Vector3f(p1.x, p1.y, p1.z).normalize();
Vector3f v2 = new Vector3f(p2.x, p2.y, p2.z).normalize();
Vector3f n = v1.copy().cross(v2);
float theta = (float) Math.acos(v1.dot(v2));
float real = (float) Math.cos(theta / 2.0F);
n.multiply((float) Math.sin(theta / 2.0F));
Quaternion q = new Quaternion(real, n.x, n.y, n.z);
rotation = q; // A member that can be accessed by a getter
// Do magic on the quaternion
}
EDIT:
I'm getting closer, I found out a few simple mistakes.
1: The JOGL implementation treats W as the real number, not X, I was using X for real
2: I was not starting with the quaternion 1 + 0i + 0j + 0k
3: I was not converting the quaternion into an axis/angle for opengl
4: I was not converting the angle into degrees for opengl
Also as Markus pointed out I was not normalizing the normal, when I did I couldn't see much change, thought it's hard to tell, he's right though.
The problem now is when I do the whole thing the graph shakes with a fierceness like you would never believe. It (kinda) moves in the direction you want it to, but the seizures are too fierce to make anything out of it.
Here's my new code with a few name changes:
public void rotate (Vector3f v1, Vector3f v2)
{
Vector3f v1p = v1.copy().normalize();
Vector3f v2p = v2.copy().normalize();
Vector3f n = v1p.copy().cross(v2p);
if (n.length() == 0) return; // Sometimes v1p equals v2p
float w = (float) Math.acos(v1p.dot(v2p));
n.normalize().multiply((float) Math.sin(w / 2.0F));
w = (float) Math.cos(w / 2.0F);
Quaternion q = new Quaternion(n.x, n.y, n.z, w);
q.mult(rot);
rot_ = q;
}
Here's the OpenGL code:
Vector3f p1 = tb_.project(x1, y1); // projectMouse [changed name]
Vector3f p2 = tb_.project(x2, y2);
tb_.rotate (p1, p2);
float[] q = tb_.getRotation().toAxis(); // Converts to angle/axis
gl.glRotatef((float)Math.toDegrees(q[0]), q[1], q[2], q[3]);
The reason for the name changes is because I deleted everything in the Trackball class and started over. Probably not the greatest idea, but oh well.
EDIT2:
I can say with pretty good certainty that there is nothing wrong with projecting onto the sphere.
I can also say that as far as the whole thing goes it seems to be the VECTOR that is the problem. The angle looks just fine, but the vector seems to jump around.
EDIT3:
The problem is the multiplication of the two quaternions, I can confirm that everything else works as expected. Something goes whacky with the axis during multiplication!
The problem is the multiplication of the two quaternions, I can confirm that everything else works as expected. Something goes whacky with the axis during multiplication!
You are absolutely correct!! I just recently submitted a correct multiplication and Jogamp has accepted my change. They had incorrect multiplication on mult(quaternion).
I am sure if you get the latest jogl release, it'll have the correct mult(Quaternion)
I did it!
Thanks to this C++ implementation I was able to develop a working trackball/arcball interface. My goodness me, I'm still not certain what the problem was, but I rewrote everything and even wrote my own Quaternions class and suddenly the whole thing works. I also made a Vectors class for vectors. I had a Vector3f class before but the Quaternions and Vectors classes are full of static methods and take in arrays. To make it easy to do vector computations on quaternions and vice versa. I will link the code for those two classes below, but only the Trackball class will be show here.
I made those two classes pretty quickly this morning so if there are any mathematical errors, well, uh, oops. I only used what I needed to use and made sure they were correct. These classes are below:
Quaternions: http://pastebin.com/raxS4Ma9
Vectors: http://pastebin.com/fU3PKZB9
Here is my Trackball class:
public class Trackball
{
private static final float RADIUS_ = Screen.DFLT_WIDTH / 2.0F;
private static final int REFRESH_ = 50;
private static final float SQRT2_ = (float) Math.sqrt(2);
private static final float SQRT2_INVERSE_ = 1.0F / SQRT2_;
private int count_;
private int objx_, objy_;
private float[] v1_, v2_;
private float[] rot_;
public Trackball ()
{
v1_ = new float[4];
v2_ = new float[4];
rot_ = new float[] {0, 0, 0, 1};
}
public void click (int x, int y)
{
v1_ = project(x, y);
}
public void drag (int x, int y)
{
v2_ = project(x, y);
if (Arrays.equals(v1_, v2_)) return;
float[] n = Vectors.cross(v2_, v1_, null);
float[] o = Vectors.sub(v1_, v2_, null);
float dt = Vectors.len(o) / (2.0F * RADIUS_);
dt = dt > 1.0F ? 1.0F : dt < -1.0F ? -1.0F : dt;
float a = 2.0F * (float) Math.asin(dt);
Vectors.norm_r(n);
Vectors.mul_r(n, (float) Math.sin(a / 2.0F));
if (count_++ == REFRESH_) { count_ = 0; Quaternions.norm_r(rot_); }
float[] q = Arrays.copyOf(n, 4);
q[3] = (float) Math.cos(a / 2.0F);
rot_ = Quaternions.mul(q, rot_, rot_);
}
public float[] getAxis ()
{
return Quaternions.axis(rot_, null);
}
public float[] project (float x, float y)
{
x = RADIUS_ - objx_ - x;
y = y - objy_ - RADIUS_;
float[] v = new float[] {x, y, 0, 0};
float len = Vectors.len(v);
float tr = RADIUS_ * SQRT2_INVERSE_;
if (len < tr)
v[2] = (float) Math.sqrt(RADIUS_ * RADIUS_ - len * len);
else
v[2] = tr * tr / len;
return v;
}
}
You can see there's a lot of similarities from the C++ example. Also I'd like to note there is no method for setting the objx_ and objy_ values yet. Those are for setting the center of the graph which can be moved around. Just saying, so you don't scratch your head about those fields.
The cross-product of two normalized vectors is not normalized itself. It's length is sin(theta). Try this instead:
n = n.normalize().multiply((float) Math.sin(theta / 2.0F));
The code I'm using in my collision detection code is this:
(note: Vector3f is part of the LWJGL library.)
(Note2:Tri is a class composed of three of LWJGL's Vector3fs. v1, v2, and v3.)
public Vector<Tri> getTrisTouching(Vector3f pos, float radius){
Vector<Tri> tempVec = new Vector<Tri>();
for(int i = 0; i < tris.size(); i++){
Tri t = tris.get(i);
Vector3f one_to_point = new Vector3f(0,0,0);
Vector3f.sub(pos,t.v1,one_to_point); //Storing vector A->P
Vector3f one_to_two = new Vector3f(0,0,0);
Vector3f.sub(t.v2,t.v1, one_to_two); //Storing vector A->B
Vector3f one_to_three = new Vector3f(0,0,0);
Vector3f.sub(t.v3, t.v1, one_to_three); //Storing vector A->C
float q1 = Vector3f.dot(one_to_point, one_to_two) / one_to_two.lengthSquared(); // The normalized "distance" from a to
float q2 = Vector3f.dot(one_to_point, one_to_three) / one_to_three.lengthSquared(); // The normalized "distance" from a to
if (q1 > 0 && q2 > 0 && q1 + q2 < 1){
tempVec.add(t);
}
}
return tempVec;
}
My question is how do I correctly see if a point in space is touching one of my triangles?
To test if your point is inside the triangle, create a ray with its origin at the test point and extend it to infinity. A nice easy one would be a ray which is horizontal ( e.g. y constant, and x increases to infinity. Then count the number of times it intersects with one of your polygon edges. Zero or an even number of intersections means you are outside the triangle. The nice thing about this it works not just for triangles but any polygon.
http://erich.realtimerendering.com/ptinpoly/
The only way I can help you is by providing you with this link.
Unfortunately, I'm not very good with LWJGL Geometry so here you are. - Vector3f
Hope it helps! If it does, please tick the answer to accept.