Here's my code:

Function ProjectPoint(ByVal Pt As Point3D) As Point2D 'define a vector rising from the center of the planet to the camera Dim CenterPoint As New Point3D(0, 0, 0) Dim v = New Vector3D(Pt, Camera) 'now define a plane according to the camera position 'firstly, getting a vector from planet center to camera Dim pv = New Vector3D(CenterPoint, Camera) 'and a point some 50 pixels distant Dim Viewpoint As New Point3D Viewpoint.X = Camera.X + pv.UnitVector().X * 50 Viewpoint.Y = Camera.Y + pv.UnitVector().Y * 50 Viewpoint.Z = Camera.Z + pv.UnitVector().Z * 50 'that right there should define a plane... 'this is going to find the point along that line where it intersects the camera plane Dim t As Double t = pv.DotProduct(New Vector3D(Pt, Viewpoint)) t /= pv.DotProduct(v) 'which is vector (pt, camera) 'project to that point Pt.X += v.X * t Pt.Y += v.Y * t Pt.Z += v.Z * t 'move the point to center around 0,0,0 - trying to map it onto the screen Pt.TranslateBy(New Vector3D(Viewpoint, New Point3D(0, 0, 0))) 'now, if we find the line of intersection between the planes defined by: '-vector pv and point (0,0,0), and '-vector 0,0,1 and point (0,0,0) 'and preform a rotation along that axis, by the correct amount, 'that should bring all points into the viewing plane Dim CrossVector As New Vector3D(pv.Y, -pv.X, 0) Dim m As New Matrix3D 'a rotation matrix, rotating the point around axis CrossVector by angle PV < 0,0,1 m.SetToRotation(Cos(pv.AngleTo(New Vector3D(0, 0, 1))), CrossVector) Pt.TransformBy(m) 'assign the function's output Return New Point2D(Pt.X, Pt.Y) End Function