Background
I currently have an environment I have created to which I load some shapes (for reference to where I am) and am trying to get the controls to move forward, backwards, and rotate where I am looking.
I have the forward and backwards worked out using my lookat point and camera position point. I am trying to rotate 360 degrees but appear to get some strange limits based on what I have so far. I am outputting my xangle to see where I am at always..
In order to rotate, I change the lookat variable (x,y, and z) while keeping the position (x,y,z) the same.
Problem
When continuously increasing the angle, I hit two reflection points where the appeared rotation changes direction. For whatever reason these seem to happen at 60 degrees and 300 degrees as shown below:
Obviously these angles are not correct. I have evaluated the behavior that should happen as the rotation happens and the calculated Cartesian coordinates seem to be correct but the display of the angle is what is off.
My setupviewport sub:
Private Sub SetupViewport()
Dim w As Integer = GLcontrol1.Width
Dim h As Integer = GLcontrol1.Height
Dim perspective1 As Matrix4 = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3F, GLcontrol1.Width / CSng(GLcontrol1.Height), 0.1F, 2000.0F)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadIdentity()
GL.Ortho(0, w, h, 0, -1, 1)
GL.LoadMatrix(perspective1)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadIdentity()
GL.Viewport(0, 0, w, h)
GL.Enable(EnableCap.DepthTest)
GL.DepthFunc(DepthFunction.Less)
End Sub
My camera class:
Class Camera
Public Position As Vector3 = Vector3.Zero
Public Orientation As New Vector3(0.0F, 0.0F, 0.0F)
Public MoveSpeed As Single = 0.2F
Public MouseSensitivity As Single = 0.01F
Public lookat As New Vector3()
Public manual_lookat As Boolean = False
Public invert_y As Boolean = False
Public Function aim_at_origin()
Position.X = 0
Position.Y = 0
Position.Z = 2
If invert_y = False Then
Return Matrix4.LookAt(Position, Position + lookat, Vector3.UnitY)
Else
Return Matrix4.LookAt(Position, Position + lookat, -Vector3.UnitY)
End If
End Function
Public Function GetViewMatrix() As Matrix4
If invert_y = False Then
Return Matrix4.LookAt(Position, lookat, Vector3.UnitY)
Else
Return Matrix4.LookAt(Position, lookat, -Vector3.UnitY)
End If
End Function
End Class
The camera class establishes the matrix to multiply against the current one. The multiplication happens every frame when setupviewport is called.
I can't figure out why it has the reflection points at 300 and 60 degrees. To me 180 degrees or 360 would make sense. It appears like the area of rotation is a total of 45 degrees from visually looking.
I am tagging this is MATH, C#, and VB .NET as answers can be acceptable in most programming languages.
In order to rotate, I call this class:
Private Sub rotate_view(ByVal delta_camanglex As Single, ByVal delta_camangley As Single)
Dim curdistance As Single = 1
curdistance = Math.Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
Dim invertx As Boolean = False
Dim inverty As Boolean = False
camanglex = camanglex + delta_camanglex
camangley = camangley + delta_camangley
If camanglex >= 360 Then
camanglex = camanglex - 360
End If
If camangley >= 360 Then
camangley = camangley - 360
End If
If camanglex < 0 Then
camanglex = camanglex + 360
End If
If camangley < 0 Then
camangley = camangley + 360
End If
cam.manual_lookat = True
Dim sigma As Single = camanglex
Dim theda As Single = camangley
lookatx = curdistance * Sin(sigma * (PI / 180)) * Cos((theda) * (PI / 180))
lookaty = curdistance * Sin((sigma) * (PI / 180)) * Sin((theda) * (PI / 180))
lookatz = curdistance * Cos((sigma) * (PI / 180))
cam.lookat.X = lookatx
cam.lookat.Y = lookaty
cam.lookat.Z = lookatz
End Sub
Do not use Euler angles for this as they have many issues like the one you got. Instead use cumulative transform matrices. It looks like this question is asked again and again... for some time now. So I decided to make an example how to do it with pure OpenGL 1.0 no GLM or funny stuff.
Definitions
Lets have Player control-able object called
obj
and cameraeye
. Each of them should be represented by separate4x4
transform matrix. The OpenGL stores them as 1D arrays. For more info seeWe want to control
obj
in its local coordinate system independent on camera view. If you are used to have both camera and object matrices multiplied together inGL_MODELVIEW
to avoidGL_PROJECTION
matrix abuse then you quickly realize that this is not solvable by simplyglRotate/glTranslate
calls in usual manner.Because of that many people switch to Euler angles which can handle this easily but brings up a whole bunch of other problems (many nowadays games are still using them where they should not and there are a tons of bugs and issues because of it).
So add this to your project:
Using GL for our matrices
This is simple just do this:
With this we can use GL calls for our matrices outside the rendering loop. (for example in keyboard handler or in some timer).
Rendering loop matrices
Now if we want to render our object with our matrices then we need to set the GL matrices properly. Let assume Projection matrix is set then just Modelview is in question. The modelview matrix should be:
But OpenGL does not have any matrix inverse function. So this is the only thing we need to code. As the matrix is always
4x4
then it is not that hard.I put all this together into this simple GL/C++/VCL example:
It is simple single form VCL application with single 20ms timer in it. So port the events to your environment style code. You can ignore the VCL pragmas and includes. This example is driven by arrows. If shift is pressed then the arrows are turning camera otherwise the object. Space is moving object forward.
Here compiled Win32 standalone demo:
This approach has one drawback
With cumulating the transforms you are loosing precision. To remedy that you can exploit vector multiplication (cross product). Simply count the number of operations performed on such matrix and if threshold is reached normalize the matrix and reset the counter.
By normalization I mean ensuring all the axises are unit and perpendicular to each other Leaving the direction of main axis (usually forward of view or object) as is. Cross product of 2 vectors returns perpendicular vector to each. So for example if you extract the
X,Y,Z
axises (locations are described in the link in #1) andZ
is the main axis then:Where:
In case your coordinate system is not derived from unit matrix then you need to negate some axis or change the order of cross product operands to ensure the direction of your axises stays as should.
For more info take a look at: