Calculating normals in a spline

Started by
6 comments, last by iEat_Babies 12 years, 8 months ago
[font="Arial"]Hello![/font]
[font="Arial"]I have a spline with a bunch of points...... I am calculating tangent and normal data for each point / vertex. Right now the tangents seem fine, but I just can't quite get the normal data right.[/font]

[font="Arial"]Check out this picture:[/font]
[font="Arial"]normals1.PNG
[/font]

[font="Arial"]Red is geometry, green is tangent data and yellow is normal data. In the above image the tangents and the normal seem fine![/font]

[font="Arial"]But here:[/font]
[font="Arial"]normals2.PNG
[/font]

[font="Arial"]the normals should[/font][font="Arial"] be tilted back more so they are perpendicular to the slope, but they are not.[/font]

[font="Arial"]How I am calculating the normals right now is the cross product of the tangent and the geometric line for each vertex.[/font][font="Arial"]In pseudo-code:[/font]
[font="Arial"][source lang="csharp"][/font]
// I have a List of points
// and a List of tangents....

// This is in a for loop...
Vector3 currentLine = points.ElementAt(i + 1) - points.ElementAt(i);
currentLine.Normalize();

Vector3 tangent = tangents.ElementAt(i);
tangent.Normalize();

Vector3 normal = Vector3.Cross(currentLine, tangent);
[/source]
[font="Arial"]Some help would really be appreciated.[/font][font="Arial"]Thanks![/font]
Advertisement
could your tangent vector be backwards?


The code looks right, but the results are puzzling...

could your tangent vector be backwards?


The code looks right, but the results are puzzling...


I switched the tangent vectors with Vector3.Negate.... and it certainly switched them, but the results for the normals aren't any better....

Here is my code for the tangent generation, in case I'm doing something wrong there:

// I have a list of points.....
// And this would be in a for loop


Vector3 currentPos = interpolatedVertices.ElementAt(i).Position;
Vector3 nextPos = interpolatedVertices.ElementAt(i + 1).Position;

Vector3 tangent = nextPos - currentPos;
tangent.Normalize();
tangents.Add(Vector3.Negate(tangent));

You are calculating normals in the wrong way. The Normal can't be calculated as the cross product of the tangent and the line, it is in fact contained in the same plane of those vectors. You may want to look at this article on parallel frame transport.
A quick and dirty approach would be to use a world axis in your calculation. something like


a = line;
b = line[i+1];

tangent = normalize(b-a);

SOMEAXIS = x, y or z; // make sure its not parallel to tangent

bitangent = cross( tangent, SOMEAXIS );
normal = cross( tangent, bitangent );



You'll generally want somthing more complex than this though, that will orient the transform with the slope.

You are calculating normals in the wrong way. The Normal can't be calculated as the cross product of the tangent and the line, it is in fact contained in the same plane of those vectors. You may want to look at this article on parallel frame transport.


I don't see how the normal is in the same plane as the tangent..... shouldn't it be perpendicular to the tangent?

Also I tried reading the article, it seems really complicated and over my head....



A quick and dirty approach would be to use a world axis in your calculation. something like


a = line;
b = line[i+1];

tangent = normalize(b-a);

SOMEAXIS = x, y or z; // make sure its not parallel to tangent

bitangent = cross( tangent, SOMEAXIS );
normal = cross( tangent, bitangent );



You'll generally want somthing more complex than this though, that will orient the transform with the slope.


I just tried that and it seems to work great..... but I think the real problem is before the normals would often be upside down, so I did "normal.Y = Math.Abs(normal.Y)", when I should have done "Vector3.Negate(normal)"......

But it is now working well with tangents, bitangents, and normals!
Thanks for the help!

But now one more question:
Which is the "right" way to calculate the tangents?

Option 1:
Vector3 tangent = Vector3.Normalize(points - points[i - 1]);

Option 2:
Vector3 tangent = Vector3.Normalize(points[i + 1] - points);

Option 3:

Vector3 tangent1 = Vector3.Normalize(points - points[i - 1]);
Vector3 tangent2 = Vector3.Normalize(points[i + 1] - points);
Vector3 tangent = Vector3.Lerp(tangent1, tangent2, 0,5f);


I am currently using "option 1" and it seems fine, but I am asking if it is correct.
Thanks!


Normals are perpendicular to the tangent and still be in the same plane as the tangent vector and curve for planar curves. For non-planar curves, the normal is contained in the same plane as the tangent vector and the derivative of the tangent vector. The vector you have calculated in your first post is perpendicular to the tangent vector, but it isn't the normal vector, i.e. it does not converge to the normal vector of the curve when the discrete curve converges to the continuous curve. The same is true for the code posted by maya18222. Using the definitions of tangent, normal and binormal vectors it's possible to define the following frenet frame method:

a = curve
b = curve[i+1]

tangent = normalize(b-a)

binormal = normalize(cross(tangent[i-1], tangent))
normal = normalize(cross(binormal, tangent))


The problem with that method is that the normal isn't defined when the binormal is not defined and that the frenet frame can change orientation in the points where the binormal is not defined. A better method in practice is the parallel frame transport algorithm which is defined in the following way:

a = curve
b = curve[i+1]

tangent = normalize(b-a)

binormal = cross(tangent[i-1], tangent)
if length(binormal) == 0 then
normal = normal[i-1]
else
binormal = normalize(binormal)
angle = acos(tangent[i-1], tangent)
normal = rotationMatrix(binormal, angle) * normal[i-1]


When the normal of the frenet frame is defined, this method gives a normal with the same direction of the frenet frame method, but without the orientation change problem. It is also defined for lines.

All the methods you listed for computing the tangent vectors are "correct". They all converge to the correct vector.


Normals are perpendicular to the tangent and still be in the same plane as the tangent vector and curve for planar curves. For non-planar curves, the normal is contained in the same plane as the tangent vector and the derivative of the tangent vector. The vector you have calculated in your first post is perpendicular to the tangent vector, but it isn't the normal vector, i.e. it does not converge to the normal vector of the curve when the discrete curve converges to the continuous curve. The same is true for the code posted by maya18222. Using the definitions of tangent, normal and binormal vectors it's possible to define the following frenet frame method:

a = curve
b = curve[i+1]

tangent = normalize(b-a)

binormal = normalize(cross(tangent[i-1], tangent))
normal = normalize(cross(binormal, tangent))


The problem with that method is that the normal isn't defined when the binormal is not defined and that the frenet frame can change orientation in the points where the binormal is not defined. A better method in practice is the parallel frame transport algorithm which is defined in the following way:

a = curve
b = curve[i+1]

tangent = normalize(b-a)

binormal = cross(tangent[i-1], tangent)
if length(binormal) == 0 then
normal = normal[i-1]
else
binormal = normalize(binormal)
angle = acos(tangent[i-1], tangent)
normal = rotationMatrix(binormal, angle) * normal[i-1]


When the normal of the frenet frame is defined, this method gives a normal with the same direction of the frenet frame method, but without the orientation change problem. It is also defined for lines.

All the methods you listed for computing the tangent vectors are "correct". They all converge to the correct vector.




Ok I see what your saying about Normals and tangents being in the same plane, I misunderstood your last post.
Thanks for that pseudo-code, it really helps out alot!


This topic is closed to new replies.

Advertisement