Based on information in Chapter 7 of 3D Programming For Windows (Charles Petzold), I’ve attempted to write as helper function that projects a Point3D to a standard 2D Point that contains the corresponding screen coordinates (x,y):
public Point Point3DToScreen2D(Point3D point3D,Viewport3D viewPort ) { double screenX = 0d, screenY = 0d; // Camera is defined in XAML as: // <Viewport3D.Camera> // <PerspectiveCamera Position='0,0,800' LookDirection='0,0,-1' /> // </Viewport3D.Camera> PerspectiveCamera cam = viewPort.Camera as PerspectiveCamera; // Translate input point using camera position double inputX = point3D.X - cam.Position.X; double inputY = point3D.Y - cam.Position.Y; double inputZ = point3D.Z - cam.Position.Z; double aspectRatio = viewPort.ActualWidth / viewPort.ActualHeight; // Apply projection to X and Y screenX = inputX / (-inputZ * Math.Tan(cam.FieldOfView / 2)); screenY = (inputY * aspectRatio) / (-inputZ * Math.Tan(cam.FieldOfView / 2)); // Convert to screen coordinates screenX = screenX * viewPort.ActualWidth; screenY = screenY * viewPort.ActualHeight; // Additional, currently unused, projection scaling factors /* double xScale = 1 / Math.Tan(Math.PI * cam.FieldOfView / 360); double yScale = aspectRatio * xScale; double zFar = cam.FarPlaneDistance; double zNear = cam.NearPlaneDistance; double zScale = zFar == Double.PositiveInfinity ? -1 : zFar / (zNear - zFar); double zOffset = zNear * zScale; */ return new Point(screenX, screenY); }
On testing however this function returns incorrect screen coordinates (checked by comparing 2D mouse coordinates against a simple 3D shape). Due to my lack of 3D programming experience I am confused as to why.
The block commented section contains scaling calculations that may be essential, however I am not sure how, and the book continues with the MatrixCamera using XAML. Initially I just want to get a basic calculation working regardless of how inefficient it may be compared to Matrices.
Can anyone advise what needs to be added or changed?
Since Windows coordinates are z into the screen (x cross y), I would use something like
instead of
to correct screenY to accomodate Windows.
Alternately, you could use OpenGL. When you set the viewport x/y/z range, you could leave it in ‘native’ units, and let OpenGL convert to screen coordinates.
Edit: Since your origin is the center. I would try
The screen + 1.0 converts from [-1.0, 1.0] to [0.0, 2.0]. At which point, you divide by 2.0 to get [0.0, 1.0] for the multiply. To account for Windows y being flipped from Cartesian y, you convert from [1.0, 0.0] (upper left to lower left), to [0.0, 1.0] (upper to lower) by subtracting the previous screen from 1.0. Then, you can scale to the ActualHeight.