## Thursday 12 April 2012

After writing yesterday's post I wondered how easy it would be to use the mathematics in a shader. This would be a natural way to extend my cross product shader, replacing the simple product in that with a more general geometric product.

In practice in three dimensions this meant it would use a quaternion product and rotation. The code is rather more extensive than the cross product shader but still very compact.

float4 col = sampleNearest(src, outCoord());
float2 diff = center - outCoord();

float angle = length(diff);
float rec = 1.0 / angle;
diff = diff * rec;
angle = angle * 0.002 * magnitude;

float myCos = cos(angle);
float mySin = sin(angle);
float3 imag = float3(mySin * diff.x, mySin * diff.y, 0);

float3 ortho = col.xyz;
ortho = ortho * 2.0 - float3(1.0, 1.0, 1.0);

float3 myCross = cross(ortho, imag);
float myDot = dot(ortho, imag);
float qReal = -myDot;
float3 qImag = myCos * ortho + myCross;
ortho = qReal * imag + myCos * qImag - cross(imag, qImag);

ortho = (ortho + float3(1.0, 1.0, 1.0)) * 0.5;
dst = float4(ortho.x, ortho.y, ortho.z, 1.0);

It calculates the rotation through an angle, which increases based on the distance from the centre of the effect, scaled by the input magnitude. Because it's calculated from an angle trigonometry is needed to generate the quaternion. The displacement from the centre also acts as the axis, so the effect varies based on the bearing from the centre.

The quaternion in stored as two parts, myCos (the 'real' part) and the float3 valued imag. This avoids a four-vector for performance reasons, so the code can use the shader language's vector functions and as the input and result 'quaternions' have no real parts. The actual product RxR-1 takes place in the block of five lines above the last two. qReal and qImag are the parts of the intermediate quaternion after the first product is done.

The result is here and embedded below. As before I've copied the whole shader source into a comment at the end.