Demx's picture

3D Box + Movement

I've written a small program that draws a cube and then rotates it 45 degrees on both x and y axes. I've also implemented movement of the cube without looking at any sort of example code. I have a number of problems at the moment.

  • Artifacts when moving the cube, the surfaces disappear when I get close to the edge of the screen. It alsmost looks like it is clipping against something, and if I move it right, I can make the whole cube disappear into a triangle when it is still in view. Also, the top of the cube never shows up. I've attached a few screenshots to demonstrate the artifacts.
  • My translating of the cube works perfectly without rotation, but with rotation, It moves right when it is supposed to move left, and visa-versa. Also, my method that intends to move it vertically actually moves it diagonally when rotated.
  • My aspect ratio seems off, it seems like it is set to 6:4 even though I have the resolution at 1920 x 1080. (This should be easy to fix).
  • In short, everything goes well until I try to rotate my cube so that I can see the edges, otherwise it goes perfectly.
    and of course, here is my code:

    using System;
    using System.Drawing;
    using OpenTK;
    using OpenTK.Graphics;
    using OpenTK.Graphics.OpenGL;
    using OpenTK.Input;
     
    namespace OpenTKRendering
    {
        class MainClass
        {
            private static void DrawCube() 
            {
                GL.Begin(PrimitiveType.Quads);
     
                GL.Color3(Color.Blue);
     
                GL.Vertex3(-0.25f, -0.25f, -0.25f);
                GL.Vertex3(-0.25f, 0.25f, -0.25f);
                GL.Vertex3(0.25f, 0.25f, -0.25f);
                GL.Vertex3(0.25f, -0.25f, -0.25f);
     
                GL.Color3(Color.Green);
     
                GL.Vertex3(-0.25f, -0.25f, -0.25f);
                GL.Vertex3(0.25f, -0.25f, -0.25f);
                GL.Vertex3(0.25f, -0.25f, 0.25f);
                GL.Vertex3(-0.25f, -0.25f, 0.25f);
     
                GL.Color3(Color.Yellow);
     
                GL.Vertex3(-0.25f, -0.25f, -0.25f);
                GL.Vertex3(-0.25f, -0.25f, 0.25f);
                GL.Vertex3(-0.25f, 0.25f, 0.25f); 
                GL.Vertex3(-0.25f, 0.25f, -0.25f);
     
                GL.Color3(Color.Red);
     
                GL.Vertex3(-0.25f, -0.25f, 0.25f);
                GL.Vertex3(0.25f, -0.25f, 0.25f);
                GL.Vertex3(0.25f, 0.25f, 0.25f);
                GL.Vertex3(-0.25f, 0.25f, 0.25f);
     
                GL.Color3(Color.Orange);
     
                GL.Vertex3(-0.25f, 0.25f, -0.25f);
                GL.Vertex3(-0.25f, 0.25f, 0.25f);
                GL.Vertex3(0.25f, 0.25f, 0.25f);
                GL.Vertex3(0.25f, 0.25f, -0.25f);
     
                GL.Color3(Color.White);
     
                GL.Vertex3(0.25f, -0.25f, -0.25f);
                GL.Vertex3(0.25f, 0.25f, -0.25f);
                GL.Vertex3(0.25f, 0.25f, 0.25f);
                GL.Vertex3(0.25f, -0.25f, 0.25f);
     
                GL.End();
            }
     
            public static void Main()
            {
                Matrix4 modelview = new Matrix4();
                float x = 0;
                float y = 0;
                float z = 0;
     
                using (var game = new GameWindow())
                {
                    game.Load += (sender, e) =>
                    {
                        game.VSync = VSyncMode.On;
                        DisplayDevice.GetDisplay(DisplayIndex.Default).ChangeResolution(1920, 1080, 32, 60);
                        game.WindowState = WindowState.Fullscreen;
                    };
     
                    game.Resize += (sender, e) =>
                    {
                        GL.Viewport(0, 0, game.Width, game.Height);
                    };
     
                    game.UpdateFrame += (sender, e) =>
                    {
                        /*Exit game*/
                        if (game.Keyboard[Key.Escape]){
                            game.Exit();
                        }
     
                        /*Movement*/
                        if (game.Keyboard[Key.Up] || game.Keyboard[Key.W]){
                            y+= 0.01f;
                        }
                        if (game.Keyboard[Key.Down] || game.Keyboard[Key.S]){
                            y-= 0.01f;
                        }
                        if (game.Keyboard[Key.Right] || game.Keyboard[Key.D]) {
                            x += 0.01f;
                        }
                        if (game.Keyboard[Key.Left] || game.Keyboard[Key.A]){
                            x -= 0.01f;
                        }
     
                        /*Export translation*/
                        modelview = Matrix4.CreateTranslation(x, y, z) * Matrix4.CreateRotationX(40) * Matrix4.CreateRotationY(40);
                    };
     
                    game.RenderFrame += (sender, e) =>
                    { 
                        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
                        GL.MatrixMode(MatrixMode.Modelview);
                        GL.LoadMatrix(ref modelview);
     
                        DrawCube();
     
                        game.SwapBuffers();
                    };
     
                    /*Run the game at 60 updates per second*/
                    game.Run(60.0);
                }
            }
        }
    }
    Inline Images
    Moving down and to the left, (which is actually pressing up and to the right on my keyboard).
    Now just moving to the left. Clipping and stuff. Getting smaller...
    Disappears to nothing at the tip of triangle.

    Comments

    Comment viewing options

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

    You need to setup a projection matrix. See here for an example:

                OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, (float)aspect_ratio, 1, 64);
                GL.MatrixMode(MatrixMode.Projection);
                GL.LoadMatrix(ref perspective);

    Note that Matrix4.CreateRotation works in *radians* not degrees.

    Demx's picture

    Thankyou; one more question: If I wanted to move the rotating cube with my modelview matrix in the example, how would I go about it? Multiplying lookat by modelview renders the sceen blank.

    winterhell's picture

    modelMatrix=modelRotMatrix*modelTranslationMatrix;
    Usually the order of the matrix multiplications is (model * view) * projection. A*B does not equal B*A, so try switching them around

    Demx's picture

    Whoops. I guess I had it right, it just took me some time to figure it out. My movement was too slow and I had to write it to console to see if it was moving. Thanks for your help though. I'm sure I'll be back for more.

    flopoloco's picture

    Hi I have made some changes to the example to test some stuff.
    I also kept some comments on, just if there's any interest to anyone reading this to learn.

    using System;
    using System.Drawing;
    using OpenTK;
    using OpenTK.Graphics;
    using OpenTK.Graphics.OpenGL;
    using OpenTK.Input;
     
    namespace OpenTKRendering
    {
    	class MainClass
    	{
    		private static void DrawCube() 
    		{
    			GL.Begin(PrimitiveType.Quads);
     
    			GL.Color3(Color.Blue);
    			GL.Vertex3(-0.25f, -0.25f, -0.25f);
    			GL.Vertex3(-0.25f, 0.25f, -0.25f);
    			GL.Vertex3(0.25f, 0.25f, -0.25f);
    			GL.Vertex3(0.25f, -0.25f, -0.25f);
     
    			GL.Color3(Color.Green);
    			GL.Vertex3(-0.25f, -0.25f, -0.25f);
    			GL.Vertex3(0.25f, -0.25f, -0.25f);
    			GL.Vertex3(0.25f, -0.25f, 0.25f);
    			GL.Vertex3(-0.25f, -0.25f, 0.25f);
     
    			GL.Color3(Color.Yellow);
    			GL.Vertex3(-0.25f, -0.25f, -0.25f);
    			GL.Vertex3(-0.25f, -0.25f, 0.25f);
    			GL.Vertex3(-0.25f, 0.25f, 0.25f); 
    			GL.Vertex3(-0.25f, 0.25f, -0.25f);
     
    			GL.Color3(Color.Red);
    			GL.Vertex3(-0.25f, -0.25f, 0.25f);
    			GL.Vertex3(0.25f, -0.25f, 0.25f);
    			GL.Vertex3(0.25f, 0.25f, 0.25f);
    			GL.Vertex3(-0.25f, 0.25f, 0.25f);
     
    			GL.Color3(Color.Orange);
    			GL.Vertex3(-0.25f, 0.25f, -0.25f);
    			GL.Vertex3(-0.25f, 0.25f, 0.25f);
    			GL.Vertex3(0.25f, 0.25f, 0.25f);
    			GL.Vertex3(0.25f, 0.25f, -0.25f);
     
    			GL.Color3(Color.White);
    			GL.Vertex3(0.25f, -0.25f, -0.25f);
    			GL.Vertex3(0.25f, 0.25f, -0.25f);
    			GL.Vertex3(0.25f, 0.25f, 0.25f);
    			GL.Vertex3(0.25f, -0.25f, 0.25f);
     
    			GL.End();
    		}
     
    		public static void Main()
    		{
    			// matrixCube: The transformation matrix of the cube
    			Matrix4 matrixCube = Matrix4.Identity;
     
    			// matrixModelview: Combination of the "model" (world matrix) and the view (camera matrix).
    			// 					These matrices are combined for convinience now, but if more control
    			//					is needed they can be used separately.
    			// matrixProjection: A special matrix only for calculating the projection of the "camera".
    			//					Let's say that each time the window is resized the aspect ration is
    			//					changed so that means that the projection as well must be recalculated.
    			// matrixMVP: A matrix that combines modelview and projection, this is simply declared
    			//					as a helper variable, just to avoid allocating it in each frame again and again.
    			Matrix4 matrixModelview, matrixProjection, matrixMVP;
    			matrixModelview = matrixProjection = matrixMVP = Matrix4.Identity;
     
    			var window = new GameWindow ();
     
    			var rotationY = 0f;
     
    			window.Load += (object sender, EventArgs e) => 
    			{
    				GL.Enable(EnableCap.DepthTest);
    			};
     
    			window.UpdateFrame += (object sender, FrameEventArgs e) => 
    			{
    				rotationY += 50f * (float)e.Time;
    				if (rotationY >= 360f) rotationY -= 360f;
     
    				matrixCube = Matrix4.CreateRotationY(MathHelper.DegreesToRadians(rotationY));
    			};
     
    			window.Resize += (object sender, EventArgs e) => 
    			{
    				// Instead of distorting the image: GL.Viewport(0, 0, window.Width, window.Height);
    				// Resize it based on aspect ration lock:
    				var aspect = 16f / 10f;
    				var viewWidth = (float)window.Width;
    				var viewHeight = (float)window.Width / aspect;
    				if (viewHeight > window.Height)
    				{
    					viewWidth = window.Height * aspect;
    					viewHeight = window.Height;
    				}
    				var viewX = (window.Width - viewWidth) / 2f;
    				var viewY = (window.Height - viewHeight) / 2f;
    				GL.Viewport((int)viewX, (int)viewY, (int)viewWidth, (int)viewHeight);
     
    				// Here we can calculate only the projection matrix.
    				// No need to set Matrix mode projection at all since
    				// everything is calculated right on the mvp variable.
    				matrixProjection = Matrix4.CreatePerspectiveFieldOfView(
    					MathHelper.PiOver4, (float)window.Width/window.Height, 0.1f, 1000f
    				);
    			};
     
    			window.RenderFrame += (object sender, FrameEventArgs e) =>
    			{
    				GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
     
    				// The transformation of the world = Identity
    				// The transformation of the camera = LookAt
    				matrixModelview = Matrix4.LookAt(new Vector3(0f, 0f, 5f), Vector3.Zero, Vector3.UnitY);
     
    				// The final transformation of the "camera"
    				// Combination of matrices: model * view * projection
    				matrixMVP = matrixModelview * matrixProjection;
     
    				// No need to call GL.MatrixMode(MatrixMode.Modelview);
    				// because we will never need to change the state to projection.
    				GL.LoadMatrix(ref matrixMVP);
     
    				// A little bit of hack to keep the mvp and cube matrices separated.
    				// Normally they could be combined but keeping them separated might
    				// make much more sense of how they are composed.
    				GL.PushMatrix();
    				GL.LoadMatrix(ref matrixCube);
    				DrawCube();
    				GL.PopMatrix();
     
    				window.SwapBuffers();
    			};
     
    			window.Run();
    		}
    	}
    }

    Do you know why the projection looks like it's very squeezed along the Y axis?

    flopoloco's picture

    Hi, I found the answer to my question. I won't delete the previous post.

    using System;
    using System.Drawing;
    using OpenTK;
    using OpenTK.Graphics;
    using OpenTK.Graphics.OpenGL;
    using OpenTK.Input;
     
    namespace OpenTKRendering
    {
    	class MainClass
    	{
    		public static void DrawAxis()
    		{
    			GL.Begin(PrimitiveType.Lines);
    			GL.Vertex3(-1f, 0f, 0f);
    			GL.Vertex3( 1f, 0f, 0f);
    			GL.Vertex3(0f, 1f, 0f);
    			GL.Vertex3(0f, -1f, 0f);
    			GL.Vertex3(0f, 0f, 1f);
    			GL.Vertex3(0f, 0f, -1f);
    			GL.End();
    		}
     
    		private static void DrawCube() 
    		{
    			GL.Begin(PrimitiveType.Quads);
     
    			GL.Color3(Color.Blue);
    			GL.Vertex3(-0.25f, -0.25f, -0.25f);
    			GL.Vertex3(-0.25f, 0.25f, -0.25f);
    			GL.Vertex3(0.25f, 0.25f, -0.25f);
    			GL.Vertex3(0.25f, -0.25f, -0.25f);
     
    			GL.Color3(Color.Green);
    			GL.Vertex3(-0.25f, -0.25f, -0.25f);
    			GL.Vertex3(0.25f, -0.25f, -0.25f);
    			GL.Vertex3(0.25f, -0.25f, 0.25f);
    			GL.Vertex3(-0.25f, -0.25f, 0.25f);
     
    			GL.Color3(Color.Yellow);
    			GL.Vertex3(-0.25f, -0.25f, -0.25f);
    			GL.Vertex3(-0.25f, -0.25f, 0.25f);
    			GL.Vertex3(-0.25f, 0.25f, 0.25f); 
    			GL.Vertex3(-0.25f, 0.25f, -0.25f);
     
    			GL.Color3(Color.Red);
    			GL.Vertex3(-0.25f, -0.25f, 0.25f);
    			GL.Vertex3(0.25f, -0.25f, 0.25f);
    			GL.Vertex3(0.25f, 0.25f, 0.25f);
    			GL.Vertex3(-0.25f, 0.25f, 0.25f);
     
    			GL.Color3(Color.Orange);
    			GL.Vertex3(-0.25f, 0.25f, -0.25f);
    			GL.Vertex3(-0.25f, 0.25f, 0.25f);
    			GL.Vertex3(0.25f, 0.25f, 0.25f);
    			GL.Vertex3(0.25f, 0.25f, -0.25f);
     
    			GL.Color3(Color.White);
    			GL.Vertex3(0.25f, -0.25f, -0.25f);
    			GL.Vertex3(0.25f, 0.25f, -0.25f);
    			GL.Vertex3(0.25f, 0.25f, 0.25f);
    			GL.Vertex3(0.25f, -0.25f, 0.25f);
     
    			GL.End();
    		}
     
    		public static void Main()
    		{
    			var window = new GameWindow();
     
    			Matrix4 matrixCube = Matrix4.Identity;
    			var rotationY = 0f;
     
    			Matrix4 matrixModelview, matrixProjection, matrixMVP;
    			matrixModelview = matrixProjection = matrixMVP = Matrix4.Identity;
     
    			window.Load += (object sender, EventArgs e) => 
    			{
    				GL.Enable(EnableCap.DepthTest);
    			};
     
    			window.UpdateFrame += (object sender, FrameEventArgs e) => 
    			{
    				rotationY += 50f * (float)e.Time;
    				if (rotationY >= 360f) rotationY -= 360f;
     
    				matrixCube = Matrix4.CreateRotationY(MathHelper.DegreesToRadians(rotationY));
    			};
     
    			window.Resize += (object sender, EventArgs e) => 
    			{
    				var aspect = 4f/3f;
    				var viewWidth = (float)window.Width;
    				var viewHeight = (float)window.Width / aspect;
    				if (viewHeight > window.Height)
    				{
    					viewWidth = window.Height * aspect;
    					viewHeight = window.Height;
    				}
    				var viewX = (window.Width - viewWidth) / 2f;
    				var viewY = (window.Height - viewHeight) / 2f;
    				GL.Viewport((int)viewX, (int)viewY, (int)viewWidth, (int)viewHeight);
     
    				matrixProjection = Matrix4.CreatePerspectiveFieldOfView(
    					MathHelper.PiOver4, (float)window.Width/window.Height, 0.1f, 1000f);
    				//This is disabled because it causes error to me
    				//GL.MatrixMode(MatrixMode.Projection);
    				//GL.LoadMatrix(ref matrixProjection);
    			};
     
    			window.RenderFrame += (object sender, FrameEventArgs e) =>
    			{
    				GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
     
    				// First drawing operation
    				matrixModelview = Matrix4.LookAt(new Vector3(5f, 5f, 5f), Vector3.Zero, Vector3.UnitY);
    				matrixMVP = matrixModelview * matrixProjection;
    				//Matrix mode is disabled because it causes error
    				//when switching between Modelview and Projection
    				//I can't see anything on screen.
    				//GL.MatrixMode(MatrixMode.Modelview);
    				GL.LoadMatrix(ref matrixMVP);
    				DrawAxis();
     
    				// Second drawing operation
     
    				// I am trying to do a pseudo push and pop matrix here.
    				// normally I would try
    				// GL.PushMatrix();
    				// GL.LoadMatrix(ref matrixCube);
    				// GL.PopMatrix();
     
    				// since there is a math api in opentk it can be done like this.
    				var matrixMVPBefore = matrixMVP;
    				matrixMVP = matrixCube * matrixMVP;
     
    				// long version is: matrixMVP = matrixCube * matrixModelview * matrixProjection;
    				// but since modelview and projection are calculated already no need to do it again.
     
    				//ERROR: flipping the multiplication order causes funny $h1+ :D
    				//matrixMVP *= matrixCube;
     
    				GL.LoadMatrix(ref matrixMVP);
    				DrawCube();
     
    				// Restore the matrix back to normal for drawing the rest of the objects
    				matrixMVP = matrixMVPBefore;
     
    				window.SwapBuffers();
    			};
     
    			window.Run();
    		}
    	}
    }