To implement refraction in ray-tracing rendering, we can simply use the refraction equation:
sin(theta1)/sin(theta2) = n2/n1
To compute sin(theta1), simply:
cos(theta1)= v1.dot(N);
sin(theta1) = sqrt(1-cos(theta1) * cos(theta1));
where v1 is the incomming ray direction, and N is the surface normal.
However, there may be trouble when this implementation runs in computer. In C++, since we can't avoid floating point error, when v1 is almost perpendicular to the surface, that is, v1 and N almost the same (or they just be the same), cos(theta1) could slightly greater than 1 (something like 1.00001). Then, this result would cause:
1-cos(theta1) * cos(theta1) < 0
And sqrt(a) would return invalid result if a<0. So that sin(theta1) becomes a invalid value. Eventually, all the computation mess up. The pixel we want to shade becomes an error pixel.
To avoid this, simply put a check routine like:
cos(theta1) = min(1.0f, v1.dot(N))
Never trust floating point number!!