phoenix's picture

Perspective and Orthographic Problem

Hi everybody,
I have a problem in switching between orthographic and perspective projection. My code runs well with GL.Ortho but if I replace it with Glu.Perspective, it doesn't work. My Render() function is here:
public void Render()
{
thisTransformation.getRenamed(matrix);

GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
GL.LoadIdentity();

GL.Ortho(-width / 2, width / 2, -height / 2, height / 2, -1000, 1000); // works fine

// Glu.Perspective(45, width / height, 0.1, 100); // problem

GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();

GL.MultMatrixf(matrix);

GL.Enable(EnableCap.DepthTest);

#region draw something here

// draw something ....

#endregion

GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();

GL.MatrixMode(MatrixMode.Modelview);
GL.PopMatrix();

GL.Flush();
this.Invalidate();
}

Any suggestion ?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
JTalton's picture

I believe the problem is that the Window_Coor_To_World_Coor and Render functions do not setup the ModelView matrix exactly the same.

Render() calls GL.MultMatrixf(matrix) to apply your arcball transformation to the rendering.
Window_Coor_To_World_Coor() needs to also call GL.MultMatrixf(matrix).

GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();
GL.MultMatrixf(matrix).

A "safer" way to do this could be to save off the ModelView and Projection matrices after they have been setup in the Render(). Then in the Window_Coor_To_World_Coor() you could apply those matrices to guarantee that they are the same.

float[] projectionMatrix = new float[16];
float[] modelViewMatrix = new float[16];
int[] viewport = new int[4];
 
public void Render()
{
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadIdentity();
    GL.Ortho(-width / 2, width / 2, -height / 2, height / 2, -1000, 1000); // works fine
    // Glu.Perspective(45, width / height, 0.1, 100); // problem
    GL.GetFloat(GetPName.ProjectionMatrix, projectionMatrix);
 
    GL.MatrixMode(MatrixMode.Modelview);
    GL.LoadIdentity();
    GL.MultMatrixf(matrix);
    GL.GetFloat(GetPName.ModelviewMatrix, modelViewMatrix);
 
    GL.GetInteger(GetPName.Viewport, viewport);
 
    // Enable Caps
 
    // Draw Here
 
    GL.Flush();
    this.Invalidate();
}
 
public Vector3d ScreenToWorld(int x, int y)
{
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadMatrix(projectionMatrix);
 
    GL.MatrixMode(MatrixMode.Modelview);
    GL.LoadMatrix(modelViewMatrix);
 
    object boxedZ = (float)0.0;
    GL.ReadPixels(x, viewport[3] - y, 1, 1, PixelFormat.DepthComponent, PixelType.Float,boxedZ);
    float z = (float)boxedZ;
 
    Vector3d result;
    Glu.UnProject(new Vector3(x, viewport[3] - y, z), ModelViewMatrix, ProjectionMatrix, Viewport, out result);
    return result;
}
JTalton's picture

Doh! I just noticed after I made that last post that your UnProject is not using the z value from the depth buffer.

Also change
Glu.UnProject(new Vector3(x, viewport[3] - y, 0), ModelViewMatrix, ProjectionMatrix, Viewport, out result);
to
Glu.UnProject(new Vector3(x, viewport[3] - y, z), ModelViewMatrix, ProjectionMatrix, Viewport, out result);

phoenix's picture

I used your Render and ScreenToWorld functions. When I run the program, it gives me an error, kind of unhandled exception and can't launch the scene.

JTalton's picture

I'll get a working demo up in the next couple of days and upload the source.

phoenix's picture

It will be great.

JTalton's picture

Draws a rotating triangle and as you move the mouse it will convert the mouse screen coordinates to world coordinates.
I then stores those coordinates and draws them as points so you can see where it is calculating the value.
Works for both types of projection - Ortho & Perspective

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Math;
using OpenTK.Input;
using System.Collections.Generic;
 
public class UnprojectTest : OpenTK.GameWindow
{
	double[] projectionMatrix = new double[16];
	double[] modelViewMatrix = new double[16];
	int[] viewport = new int[4];
 
	int lastMouseX = 0;
	int lastMouseY = 0;
	public override void OnUpdateFrame(UpdateFrameEventArgs e)
	{
		if( Mouse.X != lastMouseX || Mouse.Y != lastMouseY )
		{
			Vector3 worldPosition = ScreenToWorld(Mouse.X, Mouse.Y);
			pointList.Add(worldPosition);
		}
 
		lastMouseX = Mouse.X;
		lastMouseY = Mouse.Y;
	}
 
	List<Vector3> pointList = new List<Vector3>();
 
	public override void OnRenderFrame(RenderFrameEventArgs e)
	{
		GL.ClearColor(1.0f,0.0f,0.0f,1.0f);
		GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
		GL.GetInteger(GetPName.Viewport, viewport);
 
		GL.MatrixMode(MatrixMode.Projection);
		GL.LoadIdentity();
		GL.Ortho(-viewport[2] / 2, viewport[2] / 2, -viewport[3] / 2, viewport[3] / 2, -1000, 1000); // works fine
		//Glu.Perspective(90, (float)viewport[2] / (float)viewport[3], 1, 1000); // problem
		GL.GetDouble(GetPName.ProjectionMatrix, projectionMatrix);
 
		GL.MatrixMode(MatrixMode.Modelview);
		GL.LoadIdentity();
		//GL.MultMatrix(matrix);
		GL.Translate(0,0,-300);
		GL.Rotate(System.Environment.TickCount/ 50.0f, 1, 2, 3 );
		GL.GetDouble(GetPName.ModelviewMatrix, modelViewMatrix);
 
		GL.Enable(EnableCap.DepthTest);
 
		GL.Color3(1.0, 1.0, 1.0);
		GL.Begin(BeginMode.Triangles);
		GL.Vertex3(-200,-200,0);
		GL.Vertex3(200,0,0);
		GL.Vertex3(0,200,0);
		GL.End();
 
		GL.Color3(0.0, 0.0, 1.0);
		GL.PointSize(9);
		GL.Begin(BeginMode.Points);
		foreach( Vector3 point in pointList)
		{
			GL.Vertex3(point);
		}
		GL.End();
 
		GL.Flush();
 
		SwapBuffers();
	}
 
	public Vector3 ScreenToWorld(int x, int y)
	{
		GL.MatrixMode(MatrixMode.Projection);
		GL.LoadMatrix(projectionMatrix);
 
		GL.MatrixMode(MatrixMode.Modelview);
		GL.LoadMatrix(modelViewMatrix);
 
		object boxedZ = (float)0.0;
		GL.ReadPixels(x, viewport[3] - y, 1, 1, PixelFormat.DepthComponent, PixelType.Float,boxedZ);
		float z = (float)boxedZ;
 
		Vector3 result;
		Glu.UnProject(new Vector3(x, viewport[3] - y, z), modelViewMatrix, projectionMatrix, viewport, out result);
		return result;
	}
 
	public static void Main()
	{
		UnprojectTest unprojectTest = new UnprojectTest();
		unprojectTest.Run(60,60);
	}
}
phoenix's picture

I works with GL.Ortho(-viewport[2] / 2, viewport[2] / 2, -viewport[3] / 2, viewport[3] / 2, -1000, 1000);
With Glu.Perspective(90, (float)viewport[2] / (float)viewport[3], 1, 1000); It doesn't work, i just can see a part of what i draw (I also use GL.MultMatrix(matrix);) and the scene is weird. I really dont know why.

phoenix's picture

With GL.Ortho(-viewport[2] / 2, viewport[2] / 2, -viewport[3] / 2, viewport[3] / 2, -1000, 1000); I can zoom in and out the whole scene with the little mouse button but I can not do it with Glu.Perspective(90, (float)viewport[2] / (float)viewport[3], 1, 1000);

JTalton's picture

If you attach your source project I'll take a look.

virabhadra's picture

I have the same problem.
I still don't understand it's bug of OpenTK or OpenGL, or something in my head?

void SetupViewport()
		{
			int w = glControl1.Width;
			int h = glControl1.Height;
			GL.Viewport(0, 0, w, h);
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadIdentity();
 
			Matrix4 m = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver3, (float) w / (float) h, 0.001f, 5000);
			GL.LoadMatrix(ref m);
			GL.MatrixMode(MatrixMode.Modelview);
			GL.LoadIdentity();
 
			GL.ClearColor(Color.White);
		}

I looks like a perspective view, but very polygons look like something wrong. Only when I do very small scale, it looks good.