GL Project doesn't work properly.

Hello there,
with this problem I asked lots of people with no success so far.
My hope is that the people at the official opengl forums are able to help me out. So here’s my question/problem:

I’m using code which tries to work like Glu.Project() since OpenTK doesn’t support Glu.

        Vector4 pos = new Vector4(s.Position.X, 0.0f, s.Position.Y, 1.0f);
        Matrix4 mov = new Matrix4();
        Matrix4 prj = new Matrix4();
        Matrix4 mpj = new Matrix4();
        float[] vp = new float[4];

        GL.GetFloat(GetPName.ModelviewMatrix, out mov);
        GL.GetFloat(GetPName.ProjectionMatrix, out prj);
        GL.GetFloat(GetPName.Viewport, vp);

        Matrix4.Mult(ref prj, ref mov, out mpj);
        Vector4.Transform(ref pos, ref mpj, out pos);

        // Final mathematics as described in OpenGL 2.1 Glu specs
        s.set2DPos(new Vector2f( (vp[0] + (vp[2] * (pos.X + 1) / 2.0f)),
                                (vp[1] + (vp[3] * (pos.Y + 1) / 2.0f)) ));

        // Final mathematics as described in OpenGL 3 Vector specs
        s.set2DPos(new Vector2f( (view[2] / 2 * pos.X + view[0]),
                                (view[3] / 2 * pos.X + view[1]) ));

        // Neither of them work, but in relation OpenGL 3 vector specs work better.

s is a class which primary exists as a model in 3D space at s.Position.
But the values I’m getting from this are astronomically far beyond the window boundaries.

The ModelView matrix from a breakpoint:

{(1, 0, 0, 0)
(0, 0.7071068, 0.7071068, 0)
(0, -0.7071068, 0.7071068, 0)
(0, -141.4214, -141.4214, 1)}

The Projection matrix from a breakpoint:

{(1.931371, 0, 0, 0)
(0, 2.414213, 0, 0)
(0, 0, -1.0002, -1)
(0, 0, -2.0002, 0)}

Am I doing something wrong or did I get something wrong? Am I missing something?
( see previous suggestions at http://stackoverflow.com/questions/4316330/gl-project-doesnt-work-properly )

Your model view matrix looks as if it is printed transposed, have you checked that you consistently store your matrices in column major order (you could store them row major, but then need to transpose before passing on to OpenGL).


Vector4 pos = new Vector4(s.Position.X, 0.0f, s.Position.Y, 1.0f);

why do you ignore s.Position.Z and put Y into the third coordinate?

Since I’m only using the matrix4 class from OpenTK, which is designed for OpenGL, I suppose it to use the correct format for OpenGL.

I’m working on project in which 3d objects only can be placed at a zero height.

Ok. I just noticed that you are missing the perspective divide step though. After


Vector4.Transform(ref pos, ref mpj, out pos);

you need to divide pos.XYZ by pos.W - the viewport transformation takes normalized device coordinates as input, after applying the modelview and projection matrices you only have clip coordinates.

Thank you!
I implemented your that division to the Glu formula like this:

s.set2DPos(new Vector2f( (view[0] + (view[2] * (pos.X/pos.W + 1) / 2.0f)),
                         (view[1] + (view[3] * (pos.Y/pos.W + 1) / 2.0f)) ));

The resulting X coordinate is very close now (its just off a little too far but you can actually see it moving with the object when turning Y to something visible:) but Y ist still very far off target.

Cobra, what you are trying to do is trivial. Pick us some book on linear algebra, you won’t regret it. You can download plenty on the Internet.

Don’t think I didn’t do anything to solve this problem myself, don’t think I’d rather use other people than soluting problems myself.

I’ve read dozens of documents, forum topics, articles, and whatnot.
I tried various methods that work for most people in several scenarios.

All those methods are supposed to work, but in my case they don’t, that’s why I’m asking for the help of experts, hoping not to get bumped off as some lazy guy trying to have an easy time.
This problem is seriously eating me up.

So thanks to you, ugluk, for not helping in any aspect. (no offense intended, just stating the facts)

Ok, I show you some code, I used a long time ago, but, seriously, this is easy.

The idea was to detect clicking on a 2D gui element. Please don’t be offended by my statement. Reading an LA book is different than reading [censored] people write in the forums. Here it goes:


  Vector<GLfloat, 4> tmp_origin;
  std::copy(origin.begin(), origin.end(), tmp_origin.begin());

  Vector<GLfloat, 4> position(tmp_origin + Vector<GLfloat, 4>(x, 0, z, 0));
  position(1) = 0;
  position(3) = 1;

  Vector<GLfloat, 4> proj_position(projection_matrix * modelview_matrix *
    position);
  proj_position /= proj_position(3);

  aabb.a(0) = std::floor(.5 * width * (proj_position(0) + 1) + .5);
  aabb.a(1) = std::floor(height - 1 -
    .5 * (proj_position(1) + 1) * height + .5);

  aabb.b = Vector<GLfloat, 2>(GLfloat(texture.get_width()),
    GLfloat(texture.get_height()));

I hope this helps you somehow. The idea was to detect a click inside a texture, displayed as a 2D GUI element :slight_smile: Be sure to round better than I have. If you need more code, I have plenty. You probably forgot to take the origin into account or something. There are easy ways to test your projection formula for errors.


  Vector<GLfloat, 4> tmpa;
  std::copy(get_player_frog().fr_ptr->get_position().begin(),
    get_player_frog().fr_ptr->get_position().end(), tmpa.begin());
  tmpa(3) = 1;

  Vector<GLfloat, 4> tmpb(projection_matrix * modelview_matrix * tmpa);
  tmpb /= tmpb(3);

  frog_screen(0) = std::floor(.5 * (tmpb(0) + 1) * width + .5);
  frog_screen(1) = std::floor(height - 1 - .5 * (tmpb(1) + 1) * height + .5);

I don’t like C#. Why don’t you try your hand at C++? You won’t need any C# magic and maths is a lot faster.

BTW: The above code works on untransposed matrices, your’s look transposed.

Thanks, I tried with transposing the matrices, what got me to the current state:

Vector4 pos = new Vector4(s.Position.X, 0.0f, s.Position.Y, 1.0f);
Vector3 pos3 = new Vector3(s.Position.X, 0.0f, s.Position.Y);
Matrix4 model = new Matrix4();
Matrix4 proj = new Matrix4();
Matrix4 mpj = new Matrix4();
float[] view = new float[4];

GL.GetFloat(GetPName.ModelviewMatrix, out model);


> [/QUOTE]
> GL.GetFloat(GetPName.ProjectionMatrix, out proj);
> GL.GetFloat(GetPName.Viewport, view);
> 
> Matrix4.Transpose(ref model, out model);
> Matrix4.Transpose(ref proj, out proj);
> Matrix4.Mult(ref proj, ref model, out mpj);
> Vector4.Transform(ref pos, ref mpj, out pos);
> pos.Div(pos.W);
> 
> s.set2DPos(new Vector2f((view[0] + (view[2] * (pos.X + 1) / 2.0f)),
>                         (view[1] + (view[3] * (pos.Y) / 2.0f)) ));
> ```


> Which get's me a lot closer to the behaviour I intended. You can actually see the the point, but it only moves some pixels when the original object is moving across the screen.
> Additionaly, if i move the camera around both axis seem inverted and false scaled.

> 
[quote=""ugluk""]
but, seriously, this is easy.


To you this might be the easiest of all tasks but to me this one is actually pretty challanging, so I highly appreciate any constructive help which gets me understanding and solving my problem I got here.

[quote="ugluk"]I don't like C#. Why don't you try your hand at C++?
[/quote]

I do like C# and I dont see any sense in digging into a new language and then rewriting all my code to eventually get this issue of the project resolved.

Hmm, why don’t you add 1 to pos.Y in the s.set2DPos call? Well, even if you do that you’ll probably get a wrong Y coordinate. Use my view transform from the code, I’ve posted and it will work.

Read up the glViewport() specs.

http://www.opengl.org/sdk/docs/man/

GL was not meant to be used from C only, but C# is a sore in my eye, yet with modern GL programming almost everything runs on the GPU, so you’ll be forced to write C-like code even though you don’t like it.

Alright, I tried your code in two ways with moderate success.
I’m continuing with the one that gave me best results:

Matrix4.Transpose(ref model, out model);
Matrix4.Transpose(ref proj, out proj);

Matrix4 ProjPos = Matrix4.Mult(Matrix4.Mult(proj, model), new Matrix4(pos, pos, pos, pos));
Vector2f posout = new Vector2f(
   (0.5f * this.glc.Width * (ProjPos.Column0.X/ProjPos.Column0.W + 1.0f) + 0.5f),
   (0.5f * this.glc.Height * (ProjPos.Column0.Y/ProjPos.Column0.W + 1.0f) + 0.5f)
);

This one works best of all so far. But the point I see is static, it doesn’t move with the object although the coordinates in ‘pos’ are changing to the objects position.
The point moves very believable in relation to the camea but it appears shiftet Y- (it appears below the Y level the origin object moves along) in 3d space additionaly to its staticness.

Taking the coordinates from Column2 and Column3 results in the exact same behaviour. (Column1 is all 0)

It might be interesting to know that (0|0) is the bottom left corner of the screen in my situation (as I know that some people set top left as their (0|0) point).

While that is interesting, I don’t see how this is applicable here. I’m trying to translate a point into an actual viewport and not creating a new viewport?!?

It is applicable to your situation, because you only need to implement the viewing transform it describes in your code.

Well, using the formula from the documentation didn’t bring any change at all (which makes sense if you think about it).
So I’m still stuck on a static point that seems not to be bound to the 3d object at all.

Vector2f posout = new Vector2f(
   ((ProjPos.Column0.X / ProjPos.Column0.W + 1.0f) * (this.glc.Width / 2) + 0),
   ((ProjPos.Column0.Y / ProjPos.Column0.W + 1.0f) * (this.glc.Height / 2) + 0)
);

Well, I am not familiar with C#. I think you can write out text into the console somehow though. Try writing posout into the console or keep checking it in the debugger. If it is 0 all the time, even though po is changing, then there must be something still wrong somewhere, try to experiment a little, it is very likely a bug in your program.

Thanks A LOT ugluk, you’ve lead me on the right track!
I now solved the problem by using a very custom screen resolution translation and some workarounds.
In case you are interested here is all my code:

GL.GetFloat(GetPName.ModelviewMatrix, out model);
GL.GetFloat(GetPName.ProjectionMatrix, out proj);
GL.GetFloat(GetPName.Viewport, view);

Matrix4.Transpose(ref model, out model);
Matrix4.Transpose(ref proj, out proj);

Vector4 posa = new Vector4(0.0f, s.Position.Y, 1.0f, s.Position.X);
Vector4 posb = new Vector4(s.Position.Y, 1.0f, s.Position.X, 0.0f);
Vector4 posc = new Vector4(1.0f, s.Position.X, 0.0f, s.Position.Y);

Vector4 one = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
Matrix4 posv = new Matrix4(pos, posa, posb, posc);

Matrix4 ProjPos = Matrix4.Mult(Matrix4.Mult(proj, model), posv);
Matrix4.Transpose(ref ProjPos, out ProjPos);

Vector2f posout = new Vector2f(
   (0 + (this.glc.Width * (ProjPos.Column0.X / ProjPos.Column0.W + 1.0f)) - (this.glc.Width / 2.0f)),
   (0 + (this.glc.Height * (ProjPos.Column0.Y / ProjPos.Column0.W + 1.0f)) - (this.glc.Height / 2.0f))
);

Lots of thanks to everyone helping me with this!!