Issues with calculating normals of a triangle - java

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.

Related

Ray-tracing triangles

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 rotation of a circle to make the edge cross two points

I am trying to make a circle (actually a flat cylinder) rotate so that the edge crosses two points in world position. These two points can be anywhere on a sphere. The sphere has the same radius and position as the cylinder. the origin of both is [0,0,0].
It's a little bit hard to explain, so I included three pictures that I hope illustrates what I am trying to accomplish.
Here you see what I am trying to accomplish. The yellow circle represents one of the points along the sphere, while the red circle represents the other point. The blue line is actually a flat cylinder going through the sphere, and is rotated so that it goes through both points.
Here is another similar picture, but with the points at different locations.
In this picture one can see the cylinder in full, as the sphere has been hidden.
Now, I am really terrible at math, so I would really love an answer made up of pseudo code or a programming language. And if I should be so lucky, java.
The circles rotation can be represented with either a quaternion or a matrix
So far, what I have tried, is rotating the cylinder with an up vector towards one of the points, and a forward vector towards the other point. But I can't seem to make it work. I have also tried other solutions, most of them involving two rotations (one for each point), but I end up having trouble when combining the rotations.
Here is my current non-working code:
This code makes the circle go through the first point, and then rotates it with an "up vector" towards the same point, this second rotation varies depending on the first point position, and is kind off all over the place.
//calculate direction vector between the two points
point1point2dir.set(point1Pos);
//subtract point two position
point1point2dir.sub(point2Pos);
//normalize
point1point2dir.nor();
//make two quaternions for rotation
Quaternion rot1=new Quaternion();
Quaternion rot2=new Quaternion();
//set first rotation two a rotation between X-axis and point1 position. Vector3.X = (1,0,0)
rot1.set(m.quatUtils.getRot(Vector3.X, point1Pos));
//crossmuliply direction vector between the two points with X-axis
point1point2dir.crs(Vector3.X);
//set the second rotation to a rotation between Z-Axis and the crossmultiplied direction vector
rot2.set(m.quatUtils.getRot(Vector3.Z, point1point2dir));
//multiply the two rotations
rot1.mul(rot2);
//apply the rotation to the cylinders matrix
cylinderMatrix.rotate(rot1);
//the function that gets the quaternion rotation between two vectors
Quaternion getRot(Vector3 pStart, Vector3 pDest) {
start.set(pStart);
dest.set(pDest);
start.nor();
dest.nor();
cosTheta = Vector3.dot(start.x, start.y, start.z, dest.x, dest.y,
dest.z);
rotationAxis.set(0.0f, 0.0f, 0.0f);
if (cosTheta < -1.0f + 0.001f) {
rotationAxis.set(Z_AXIS);
rotationAxis.crs(start);
if (rotationAxis.len2() < 0.01f) {
rotationAxis.set(X_AXIS);
rotationAxis.crs(start);
}
rotationAxis.nor();
resultQuat.set(rotationAxis, 180.0f);
return resultQuat;
}
rotationAxis.set(start);
rotationAxis.crs(dest);
s = (float) Math.sqrt((1 + cosTheta) * 2);
invs = 1.0f / s;
resultQuat.set(rotationAxis.x * invs, rotationAxis.y * invs,
rotationAxis.z * invs, s * 0.5f);
return resultQuat;
}
I would suggest this solution:
Calculate v1 and v2 as the vectors from the center of the sphere to each point that you want the cylinder to pass trough.
Cross product v1 and v2 to get the vector up of the cylinder, let's call it n.
Position the center of the cylinder in the center of the sphere.
Rotate the cylinder using n as vector up.
I figured out the solution! It was actually really simple. I don't know how I managed to bungle the math as much as I did earlier. I actually did spend alot of time on this >:)
Sorry if I wasted anybodys time!
The solution:
find direction vector from point1 (A) to point2 (B).
crossmultiply direction vector with point2 to get (C)
Find the quaternion which represents the rotation from Z-axis to the crossmultiplied direction vector (C), function for doing this included in the code attached to the question.
apply rotation.
Here is the working code (yay):
//the rotation
Quaternion rot=new Quaternion();
//the direction from point1 to point 2 (the point positions are in this case also the direction vectors from center)
point1point2dir.set(point1Pos);
point1point2dir.sub(point2Pos);
point1point2dir.nor();
//crossmultiplied with point2
point1point2dir.crs(point2Pos);
//set the rotation to the rotation between Z-axis and the crossmultiplied direction between point 1 and 2
rot.set(m.quatUtils.getRot(Vector3.Z, point1point2dir));
//apply rotation
ekvatorMatrix.rotate(rot);
And here is the code for the function that returns the quaternion between two vectors:
Quaternion getRot(Vector3 pStart, Vector3 pDest) {
start.set(pStart);
dest.set(pDest);
start.nor();
dest.nor();
cosTheta = Vector3.dot(start.x, start.y, start.z, dest.x, dest.y,
dest.z);
rotationAxis.set(0.0f, 0.0f, 0.0f);
if (cosTheta < -1.0f + 0.001f) {
rotationAxis.set(Z_AXIS);
rotationAxis.crs(start);
if (rotationAxis.len2() < 0.01f) {
rotationAxis.set(X_AXIS);
rotationAxis.crs(start);
}
rotationAxis.nor();
resultQuat.set(rotationAxis, 180.0f);
return resultQuat;
}
rotationAxis.set(start);
rotationAxis.crs(dest);
s = (float) Math.sqrt((1 + cosTheta) * 2);
invs = 1.0f / s;
resultQuat.set(rotationAxis.x * invs, rotationAxis.y * invs,
rotationAxis.z * invs, s * 0.5f);
return resultQuat;
}
Assuming that the initial cylinder is axis aligned with the "circle" ends in positive and negative X direction, and assuming cylinder and sphere is initially unit size (radius=1.0) I would do the following:
Convert the world coordinate representation of the Red and "Yellow" points (let's just for fun call them A and B shall we) to normalized vectors pointing from centre [0,0,0] (from now on called C)
Calculate the angle between CA and CB (which is really just between A and B). Let's call this angle W
Calculate the vector perpendicular to both A and B by doing a cross product. Lets call this new vector D.
Find the rotation matrix that rotates from [0,0,1] to B. Lets call this M1. This can be done in the same way as in point 3 (create a perpendicular vector and rotate identity matrix around it with the angle between the normalized vectors).
Find the rotation matrix that rotates W around D. Let's call this M2
Combine M1 + M2 into M3
You result is M3
This was not tested and so I don't know if it works.

Rotate object to face point

I would like to rotate an object to face a point which I'm have a bit of of trouble with.
So I'm starting with an object that has a base at zero and is aligned on the y axis.
I would like to rotate it so that the top of the object is facing the destination
My process so far is to:
Given axis A
find the distance between my position and my look position: D
create a direction vector: V = D.normalize()
find the right vector: R = A cross D
find the up vector: U = D cross R
find the angle between up and direction: ANGLE = acos((U dot D) / (U.length * D.length))
rotate by angle scaled by direction on each axis
here is the code representation of that. I'm not sure what exactly is wrong with this I've worked it out on paper and to my knowledge this approach should work but the results are completely incorrect when drawn. If anyone sees any flaws and could point me in the right direction it would be great.
Vector3 distance = new Vector3(from.x, from.y, from.z).sub(to.x, to.y, to.z);
final Vector3 axis = new Vector3(0, 1, 0);
final Vector3 direction = distance.clone().normalize();
final Vector3 right = (axis.clone().cross(direction));
final Vector3 up = (distance.clone().cross(right));
float angle = (float) Math.acos((up.dot(direction)/ (up.length() * direction.length())));
bondObject.rotateLocal(angle, direction.x , direction.y, direction.z);
The basic idea here is as follows.
Determine which way the object is facing: directionA
Determine which way the object should be facing: directionB
Determine the angle between those directions: rotationAngle
Determine the rotation axis: rotationAxis
Here's the modified code.
Vector3 distance = new Vector3(from.x, from.y, from.z).sub(to.x, to.y, to.z);
if (distance.length() < DISTANCE_EPSILON)
{
//exit - don't do any rotation
//distance is too small for rotation to be numerically stable
}
//Don't actually need to call normalize for directionA - just doing it to indicate
//that this vector must be normalized.
final Vector3 directionA = new Vector3(0, 1, 0).normalize();
final Vector3 directionB = distance.clone().normalize();
float rotationAngle = (float)Math.acos(directionA.dot(directionB));
if (Math.abs(rotationAngle) < ANGLE_EPSILON)
{
//exit - don't do any rotation
//angle is too small for rotation to be numerically stable
}
final Vector3 rotationAxis = directionA.clone().cross(directionB).normalize();
//rotate object about rotationAxis by rotationAngle

Trying to get position in barycentric , I think?

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.

"Is Triangle Touching" code not giving correct results

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.

Categories

Resources