Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
431 views
in Technique[技术] by (71.8m points)

c# - Projecting a 3D point to a 2D screen coordinate

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've created and succesfully tested a working method by using the 3DUtils Codeplex source library.

The real work is performed in the TryWorldToViewportTransform() method from 3DUtils. This method will not work without it (see the above link).

Very useful information was also found in the article by Eric Sink: Auto-Zoom.

NB. There may be more reliable/efficient approaches, if so please add them as an answer. In the meantime this is good enough for my needs.

    /// <summary>
    /// Takes a 3D point and returns the corresponding 2D point (X,Y) within the viewport.  
    /// Requires the 3DUtils project available at http://www.codeplex.com/Wiki/View.aspx?ProjectName=3DTools
    /// </summary>
    /// <param name="point3D">A point in 3D space</param>
    /// <param name="viewPort">An instance of Viewport3D</param>
    /// <returns>The corresponding 2D point or null if it could not be calculated</returns>
    public Point? Point3DToScreen2D(Point3D point3D, Viewport3D viewPort)
    {
        bool bOK = false;

        // We need a Viewport3DVisual but we only have a Viewport3D.
        Viewport3DVisual vpv =VisualTreeHelper.GetParent(viewPort.Children[0]) as Viewport3DVisual;

        // Get the world to viewport transform matrix
        Matrix3D m = MathUtils.TryWorldToViewportTransform(vpv, out bOK);

        if (bOK)
        {
            // Transform the 3D point to 2D
            Point3D transformedPoint = m.Transform(point3D);

            Point screen2DPoint = new Point(transformedPoint.X, transformedPoint.Y);

            return new Nullable<Point>(screen2DPoint);
        }
        else
        {
            return null; 
        }
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...