Pseudocode for mapping a pixel position onto a virtual unit sphere:
project_pixel_onto_ball(x_pixels, y_pixels) do v_pixels = vec4(x_pixels, y_pixel, 0, 1) // The pixel is in [0, width] x [0, height]. We want to overlay a // virtual unit sphere in this window, whose coordinates are // in [-1, 1] x [-1, 1]. We first scale the pixel location into // [0, 2] x [0, 2]. Then we shift it into [-1, 1] x [-1, 1]. v_ball = translate by (-1, -1, 0) * scale by (2 / width, 2 / height, 1) * v_pixels // v_ball.x and v_ball.x tell us the xy-components on this // virtual sphere, but we don't yet know the z-component. // However, we do know 1 = x^2 + y^2 + z^2. Let's solve for // z^2. z_squared = 1 - v_ball.x ^ 2 - v_ball.y ^ 2 // If xy isn't on the unit sphere, but perhaps in a corner, we // end up with z_squared < 0. Let's clamp our projection to // the sphere. if z_squared < 0 z_squared = 0 end v_ball.z = z_squared ^ 0.5 normalize v_ball return v_ball end
Now, to implement the trackball:
Matrix4class for help:
Matrix4 rotation = Matrix4::GetRotate(theta * 180.0f / 3.14159265358979323846264f, axis);
Note that this rotation represents just the span between the last mouse event and this one. We want to accumulate this rotation with all the previous rotations. We can do so combining this matrix with the earlier transformations. Suppose
xform is our accumulated transformation, then:
xform = rotation * xform;
The order is important. The older rotations in
xform get applied first, with the new rotation applied last. Upload
xform to the vertex shader. Also note that before any mouse events happen,
xform should be the identity matrix.
You might notice the rotation is flipped. Try inverting the near and far parameters on your orthographic projection. The convention is to look down the negative z-axis.