In this post we will look at how to manipulate an object by rotating and moving it around.
This is part 6 of my series on OpenGL4 with OpenTK.
For other posts in this series:
OpenGL 4 with OpenTK in C# Part 1: Initialize the GameWindow
OpenGL 4 with OpenTK in C# Part 2: Compiling shaders and linking them
OpenGL 4 with OpenTK in C# Part 3: Passing data to shaders
OpenGL 4 with OpenTK in C# Part 4: Refactoring and adding error handling
OpenGL 4 with OpenTK in C# Part 5: Buffers and Triangle
OpenGL 4 with OpenTK in C# Part 6: Rotations and Movement of objects
OpenGL 4 with OpenTK in C# Part 7: Vectors and Matrices
OpenGL 4 with OpenTK in C# Part 8: Drawing multiple objects
OpenGL 4 with OpenTK in C# Part 9: Texturing
OpenGL 4 with OpenTK in C# Part 10: Asteroid Invaders
Basic bullet movement patterns in Asteroid Invaders
OpenGL 4 with OpenTK in C# Part 11: Mipmap
OpenGL 4 with OpenTK in C# Part 12: Basic Moveable Camera
OpenGL 4 with OpenTK in C# Part 13: IcoSphere
OpenGL 4 with OpenTK in C# Part 14: Basic Text
OpenGL 4 with OpenTK in C# Part 15: Object picking by mouse
If you think that the progress is slow, then know that I am a slow learner :P
This part will build upon the game window and shaders from the previous post.
3D Cube
Ah, the old cube that is used in all tutorials is back.Lets create a helper class, ObjectFactory, that emits the vertices for a cube (single color)
public class ObjectFactory { public static Vertex[] CreateSolidCube(float side, Color4 color) { side = side/2f; // half side - and other half Vertex[] vertices = { new Vertex(new Vector4(-side, -side, -side, 1.0f), color), new Vertex(new Vector4(-side, -side, side, 1.0f), color), new Vertex(new Vector4(-side, side, -side, 1.0f), color), new Vertex(new Vector4(-side, side, -side, 1.0f), color), new Vertex(new Vector4(-side, -side, side, 1.0f), color), new Vertex(new Vector4(-side, side, side, 1.0f), color), new Vertex(new Vector4(side, -side, -side, 1.0f), color), new Vertex(new Vector4(side, side, -side, 1.0f), color), new Vertex(new Vector4(side, -side, side, 1.0f), color), new Vertex(new Vector4(side, -side, side, 1.0f), color), new Vertex(new Vector4(side, side, -side, 1.0f), color), new Vertex(new Vector4(side, side, side, 1.0f), color), new Vertex(new Vector4(-side, -side, -side, 1.0f), color), new Vertex(new Vector4(side, -side, -side, 1.0f), color), new Vertex(new Vector4(-side, -side, side, 1.0f), color), new Vertex(new Vector4(-side, -side, side, 1.0f), color), new Vertex(new Vector4(side, -side, -side, 1.0f), color), new Vertex(new Vector4(side, -side, side, 1.0f), color), new Vertex(new Vector4(-side, side, -side, 1.0f), color), new Vertex(new Vector4(-side, side, side, 1.0f), color), new Vertex(new Vector4(side, side, -side, 1.0f), color), new Vertex(new Vector4(side, side, -side, 1.0f), color), new Vertex(new Vector4(-side, side, side, 1.0f), color), new Vertex(new Vector4(side, side, side, 1.0f), color), new Vertex(new Vector4(-side, -side, -side, 1.0f), color), new Vertex(new Vector4(-side, side, -side, 1.0f), color), new Vertex(new Vector4(side, -side, -side, 1.0f), color), new Vertex(new Vector4(side, -side, -side, 1.0f), color), new Vertex(new Vector4(-side, side, -side, 1.0f), color), new Vertex(new Vector4(side, side, -side, 1.0f), color), new Vertex(new Vector4(-side, -side, side, 1.0f), color), new Vertex(new Vector4(side, -side, side, 1.0f), color), new Vertex(new Vector4(-side, side, side, 1.0f), color), new Vertex(new Vector4(-side, side, side, 1.0f), color), new Vertex(new Vector4(side, -side, side, 1.0f), color), new Vertex(new Vector4(side, side, side, 1.0f), color), }; return vertices; } }And replace the triangle initialization with a call to it in the OnLoad method in the GameWindow
protected override void OnLoad(EventArgs e) { Vertex[] vertices = ObjectFactory.CreateSolidCube(0.2f, Color4.HotPink); _renderObjects.Add(new RenderObject(vertices)); CursorVisible = true; _program = CreateProgram(); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); GL.PatchParameter(PatchParameterInt.PatchVertices, 3); GL.Enable(EnableCap.DepthTest); Closed += OnClosed; }
This should result in something like the following;
So nothing fancy at all. As you can see the lines are drawn thanks to the following line in the OnLoad, it was PolygonMode.Fill before and now it says PolygonMode.Line.
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);and each side of the cube is drawn as 2 triangles.
So lets get this cube moving
Rotating
I am really bad at math. Thankfully a lot of this is provided by OpenTK.Matrices
We will use matrices to rotate and move (translate) objects in 3D.Our first goal is to create a model=>view matrix. The purpose of this matrix is to transform the coordinates of each vertex from model space (i.e. what we create above for the cube) to coordinates in the view space.
Vertex Shader
First lets update our vertex shader so that it can take a matrix input and then transform the vertex position with it.
#version 450 core layout (location = 0) in vec4 position; layout(location = 1) in vec4 color; out vec4 vs_color; layout (location = 20) uniform mat4 modelView; void main(void) { gl_Position = modelView * position; vs_color = color; }Note the uniform keyword here, what it says is that this is no ordinary attribute that could differ between vertices but constant for this batch.
Having done so, lets add some rotations to the cube so that we can see all sides of it.
In our OnUpdateFrame, lets add a little more then just the keyboard handler
private Matrix4 _modelView; protected override void OnUpdateFrame(FrameEventArgs e) { _time += e.Time; var k = (float)_time*0.05f; var r1 = Matrix4.CreateRotationX(k * 13.0f); var r2 = Matrix4.CreateRotationY(k * 13.0f); var r3 = Matrix4.CreateRotationZ(k * 3.0f); _modelView = r1 * r2 * r3; HandleKeyboard(); }And update our OnRenderFrame to provide the new matrix to our shader with the GL.UniformMatrix4 command.
protected override void OnRenderFrame(FrameEventArgs e) { Title = $"{_title}: (Vsync: {VSync}) FPS: {1f / e.Time:0}"; GL.ClearColor(_backColor); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.UseProgram(_program); GL.UniformMatrix4(20, // match the layout location in the shader false, // transpose ref _modelView); // our matrix foreach (var renderObject in _renderObjects) { renderObject.Render(); } SwapBuffers(); }
Output should be something like this:
Translating (moving)
Moving the object around is the next step, this we will be doing with the help of Translations.Add the following to the OnUpdateFrame method to make the object circle around the center of the screen at a leisurely pace.
var t1 = Matrix4.CreateTranslation( (float) (Math.Sin(k * 5f) * 0.5f), (float) (Math.Cos(k * 5f) * 0.5f), 0f); _modelView = r1*r2*r3*t1;
Should give the following result
Be aware that the order of the multiplications is really important,
_modelView = t1*r1*r2*r3;Yields in a totally different experience
For the full source at the end of part 6, go to: https://github.com/eowind/dreamstatecoding
Hope this helps someone out there :)
I must sadly inform that there is no cat video for this part, hopefully they will do something fun in front of the camera until next post.
Until next time: Work to Live, Don’t Live to Work
Turns out I had the vertex order wrong in the CreateSolidCube method.
ReplyDeleteIf you turned on the face culling (telling opengl to not render backsides of triangles, things looked shady). The code in the article is updated with correct vertex order.
I'm trying to use a rotation command to rotate the object. Using a obj called Bulldozer and I want to rotate it but every time I rotate it again it set to that position instead of increment more and more. Any help?
DeleteBulldozer.Rotation = new Vector3(-0.1f, 0f, 0f);
Hi there!
DeleteTry to increment the vector
Bulldozer.Rotation = new Vector3(-0.1f, 0f, 0f) + Bulldozer.Rotation;
And my cat video ???
ReplyDeleteexcellent tutorials...
Hehe, didn't have one for each post. Sorry about that! :)
Delete