Home  Obtaining surface normals is needed for camber calculations. 

WHAT IS A TRACK NORMAL?
The track normal is a vector that points right out of your track upwards, perpendicular to the track itself. On a flat surface, it points right up. Things get more complicated in corners.
You can compare it with surface normals on 3D models; normals can be interpolated across the surface (Phong shading), and if so, you get a clean, smooth change in lighting effects.
WHY YOU NEED THEM
For camber calculations, you'll need the surface normal. From this normal and the axis sticking out of the wheel (laterally), you can calculate the amount of camber with the formula: camberAngle=90 degrees  angle_between(side_axis,track_normal). The calculation of the side axis is another matter; you may start with the sideway vector from your car's orientation matrix, and apply steering rotation and static camber. But there are other ways of calculating this, depending on your situation (the information you have available).
CALCULATING A NORMAL WITHOUT SPLINES
If you don't have splines, or are in a track area where no spline surface is present (in Racer, this is mostly offtrack), you can use the triangles or geometry that the car is driving on. Mostly, these will be triangles.
You can select from 2 methods; the first and easiest will be to just take 2 sides of the triangle, and create a normal using the crossproduct. Suppose your triangle has 3 vertices called v0, v1 and v2, then the track normal will just be (v1v0) x (v2v0).
The second method is a bit more involved, and requires you to create vertex normals at every vertex of the triangle/track model. Apply a smoothing operation on the triangles, just like a regular 3D model (averaging the normals at a vertex which is shared among triangles will probably do nicely, although better methods seem available). Then, create an interpolated track normal by using the 3 vertex normals on your current triangle, and interpolate using barycentric coordinates. You will have the barycentric coordinates probably from the track intersection functions in your program (you'll need them with the splines later on, and it's probably used to determine the height if you don't use splines anyway). Average the 3 normals using the U and V barycentric coordinates to come up with an approximate normal at (U,V).
You can search for Phong shading to learn more about this type of normal interpolation.
CALCULATING A NORMAL FROM SPLINES
When you do have splines available, you should use them to come up with nicer track normals that relate more to the splines, instead of interpolating normals like explained above.
Racer has a very specific way of storing splines, but an explanation may be useful or inspiring for other simulations as well. Racer uses 6 1dimensional splines currently (valid to at least v0.5). You can view them as 2 3dimensional splines; 3 splines on each side representing X, Y and Z. Compare the multiple traces in Grand Prix Legends, which may have upto 16 traces in a track (laterally). Racer currently only uses 2 traces, and for every trace 3 1dimensional splines are used.
In the image to the right you can see how a normal is calculated from the given data available. Now what data is here exactly? The transparent yellow border cylinders represent the spline (the left and right trace). The road is built from triangles, and the current triangle under the tire has been found, including barycentric coordinates. U is used for lateral, V is longitudinal (see the article 'A surface spline method'). Notice how the surface normal is perpendicular to the direction of the track and the line going from the left to the right side of the track. These 2 vectors we will be searching for to get the surface normal. 
Using the V coordinate, the left and right vertices (the little red spheres in the image) are calculated. Racer v0.5 uses Hermite splines, although any type of spline can be used. Hermite splines use the following formula:
pt = (2*t^33*t^2+1)*p0 + (t^32*t^2+t)*m0 + (t^3t^2)*m1 + (2*t^3+3*t^2)*p1
Here, p0 is the starting point, p1 the ending point, m0 the tangent at p0, and m1 the tangent at p1. 't' is the V barycentric coordinate. Google for Hermite spline to know more. Also notice though that the above formula can be highly optimized by noticing that p0/p1/m0/p1 are constants and so a lot of constants can be (pre)combined when actually implemented (this is left as an exercise). Racer uses multiple points, so there are actually a lot of p0/p1 possibilities, but every segment is seen as a single p0p1 curve as above.
To get the spline direction, we can use the mathematical derivative of the spline. The derivative for a single dimension is just:
pt' = (6*t^26*t)*p0 + (3*t^24*t+1)*m0 + (3*t^22*t)*m1 + (6*t^2+6*t)*p1
Again, a lot of this can be optimized in advance. This will give you:
pt' = t * ( t*c2 + c1 ) + c0
where
c2=6*p0+3*m0+3*m16*p1
c1=6*p04*m02*m1+6*p1+m0
c0=m0
Notice that c0c2 are constants and can all be precalculated; yes, splines turn out not to be that costly after all.
Let's combine it all; you calculate the left and right vertices along the track using the 3 splines you have for each side (well, Racer does anyway; you may use a different spline system). This gets you the 2 red spheres from the images. Also calculate the derivatives of the splines, so you get the (3D) direction of the spline at the left and right side of the track. You can interpolate using 'U' to get the approximate direction at the specific lateral location of the road (if you had multiple traces you might make lateral splines as well for even better smoothness across the track).
Now we have 2 vectors; one going from the left to the right of the track, and one direction vector based on the spline derivatives. When we cross those we get the track normal (the yellow vector pointing up):
trackNormal = cross(rightleft, interpolatedDirection)
With this track normal you can calculate camber for example, but that's another article.
Disclaimer: the derivative and the optimization I did while writing this article. Haven't checked it thoroughly, so you may want to check my quick work.
(last updated November 13, 2012 )