Apr 23, 2008

Java Refraction method, using quaternion.

This is the refraction method I have made. For somebody who will need this kind of program I'm posting the source code.
import javax.vecmath.*;

public static Vector3f refract( Vector3f rayDir, Vector3f n, float indexRatio ) {
//assert (isNormalized( rayDir ));
//assert (isNormalized( n ));
assert (0 != indexRatio);
// Ni * Sin(I) = Nr * Sin(O); Snell's Law
// sinO = sinI / degree ; where indexRatio = Nr / Ni
// we need to know the angle R; where R = I - O
//
// rotate rayDir degree by the vector calculated by crossProduct(n,rayDir)
// q = cos(R/2), cross.x * sin(R/2), cross.y * sin(R/2), cross.z * sin(R/2)
// P' = qPq' ; Using Quaternion
final float cosI = n.dot( rayDir );
// assert (cosI <= 0f && cosI >= -1f);
if( cosI >= 1f || cosI <= -1f ) return new Vector3f( rayDir ); // straight final float sinI = (float) Math.sqrt( 1 - cosI * cosI ); final float sinO = sinI / indexRatio; if( sinO > 1f ) return null; // over angle, this should be treat as reflection
final float R = (float) (Math.acos( sinI ) - Math.acos( sinO ));
assert (false == Float.isNaN( R ));

final Vector3f cross = new Vector3f();
cross.cross( rayDir, n );
assert (false == Float.isNaN( cross.x ));
assert (false == Float.isNaN( cross.y ));
assert (false == Float.isNaN( cross.z ));

final Quat4f q = new Quat4f();
final AxisAngle4f axis = new AxisAngle4f( cross.x, cross.y, cross.z, R );
q.set( axis );
final Quat4f q2 = new Quat4f( q );
q2.conjugate();
final Quat4f P = new Quat4f( rayDir.x, rayDir.y, rayDir.z, 0 );
q.mul( P );
q.mul( q2 );
final Vector3f rst = new Vector3f( q.x, q.y, q.z );
//assert (isNormalized( rst ));
return rst;
}
Hopefully someone who will use this likes to understand how it works, before use it. And anyone might be able to improve the acos and square root parts, because they are expensive calculation.

1 comment:

Jay said...

My professor gave me great way to improve this speed. Later I want to post this function again, using the way.