2

Using Monotouch and OpenTK I am trying to get the screen coordinate of one 3D point. I have my world view projection matrix set up, and OpenGL makes sense of it and projects my 3D model perfectly, but how to use the same matrix to project just one point from 2D to 3D?

I thought I could simply use:

Vector3.Transform(ref input3Dpos, ref matWorldViewProjection, out projected2Dpos);

Then have the projected screen coordinate in projected2DPos. But the resulting Vector4 does not seem to represent the proper projected screen coordinate. And I do not know how to calculate it from there on.


I found I need to divide by Vector4.w, however I am still getting the wrong values. Using this method now:

private static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 matWorldViewProjection, int[] viewport, out OpenTK.Vector3 screenPos)
{
    OpenTK.Vector4 _in;

    _in.X = objPos.X;
    _in.Y = objPos.Y;
    _in.Z = objPos.Z;
    _in.W = 1f;

    Vector4 _out = OpenTK.Vector4.Transform(_in, matWorldViewProjection);

    if (_out.W <= 0.0)
    {
        screenPos = OpenTK.Vector3.Zero;
        return false;
    }

    _out.X /= _out.W;
    _out.Y /= _out.W;
    _out.Z /= _out.W;
    /* Map x, y and z to range 0-1 */
    _out.X = _out.X * 0.5f + 0.5f;
    _out.Y = -_out.Y * 0.5f + 0.5f;
    _out.Z = _out.Z * 0.5f + 0.5f;

    /* Map x,y to viewport */
    _out.X = _out.X * viewport[2] + viewport[0];
    _out.Y = _out.Y * viewport[3] + viewport[1];

    screenPos.X = _out.X;
    screenPos.Y = _out.Y;
    screenPos.Z = _out.Z;

    return true;
}

I cannot see any errors though... :S

Taryn
  • 242,637
  • 56
  • 362
  • 405
sinsro
  • 905
  • 7
  • 25

2 Answers2

1

You have two options. You can calculate it yourself, or use the glProject function. I prefer the first.

Number 1:

private Vector2 Convert(
  Vector3 pos, 
  Matrix4 viewMatrix, 
  Matrix4 projectionMatrix, 
  int screenWidth, 
  int screenHeight)
{
    pos = Vector3.Transform(pos, viewMatrix);
    pos = Vector3.Transform(pos, projectionMatrix);
    pos.X /= pos.Z;
    pos.Y /= pos.Z;
    pos.X = (pos.X + 1) * screenWidth / 2;
    pos.Y = (pos.Y + 1) * screenHeight / 2;

    return new Vector2(pos.X, pos.Y);
}

Number 2:

public Vector2 form3Dto2D(Vector3 our3DPoint)
{
    Vector3 our2DPoint;

    float[] modelviewMatrix =  new float[16];
    float[] projectionMatrix = new float[16];
    int[] viewport = new int[4];

    GL.GetFloat(GetPName.ModelviewMatrix, modelviewMatrix);
    GL.GetFloat(GetPName.ProjectionMatrix, projectionMatrix);
    GL.GetInteger(GetPName.Viewport, viewport);

    OpenTK.Graphics.Glu.Project(our3DPoint, convertFloatsToDoubles(modelviewMatrix),
        convertFloatsToDoubles(projectionMatrix), viewport, out our2DPoint);

    return new Vector2(our2DPoint.X, our2DPoint.Y)
}   

public static double[] convertFloatsToDoubles(float[] input)
{
    if (input == null)
    {
        return null; // Or throw an exception - your choice
    }

    double[] output = new double[input.Length];
    for (int i = 0; i < input.Length; i++)
    {
        output[i] = input[i];
    }
    return output;
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
skolko
  • 89
  • 1
  • 4
  • In Number 1, why the hell are we dividing by `pos.Z`?! Shouldn't the value be divided by the `w` component of the homogeneous coordinate system? – Maghoumi Jun 26 '16 at 06:55
1

In the first question you're missing the last step: Mapping from NDC (Normalized Device Coordinates) to viewport coordinates. That's what the lines

/* Map x,y to viewport */
_out.X = _out.X * viewport[2] + viewport[0];
_out.Y = _out.Y * viewport[3] + viewport[1];

in your GluProject do,

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • I just realized I was giving in the world matrix info twice, once as the incoming point (objPos) and again as part of the given matrix. That caused strange results. It works after inputting only the combined view and projection matrix. :) – sinsro Oct 08 '11 at 16:24