Background
I think I have the basics down for creating my OpenGL environment. I can render simple objects, can handle lighting, etc. I am trying to create a test environment that I can move around in similar to the FPS game style. I want to be able to "walk" forward and backwards in the direction I am looking at.
How my other methods are done
I have developed a walk forward and backwards function but am having a bit of difficulty with rotating my camera. I have a few variables that seem to work. I have cam.lookat which is the point to which my camera looks at, cam.position which is the point my camera is at, and distance which is a calculation between the two (which in my case I keep constant).
So I may be going at this completely wrong but here is what I have done so far:
My setupviewport sub that is called on initial load:
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
Here is my Paint event that is called per frame change:
Private Sub GlControl1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs)
GL.Clear(ClearBufferMask.ColorBufferBit)
GL.Clear(ClearBufferMask.DepthBufferBit)
GL.DepthMask(True)
GL.Enable(EnableCap.DepthTest)
GL.ClearDepth(1.0F)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadIdentity()
Dim lightColor0 As Single() = {light_intensity, light_intensity, light_intensity, 1.0F}
Dim lightPos0 As Single() = {cam.Position.X, cam.Position.Y, cam.Position.Z, 1.0F}
GL.Light(LightName.Light0, LightParameter.Diffuse, lightColor0)
GL.Light(LightName.Light0, LightParameter.Position, lightPos0)
GL.Enable(EnableCap.Light0)
Dim mat_specular As Single() = {1.0F, 1.0F, 1.0F, 1.0F}
Dim mat_shininess As Single() = {50.0F}
GL.Material(MaterialFace.Front, MaterialParameter.Specular, mat_specular)
GL.Material(MaterialFace.Front, MaterialParameter.Shininess, mat_shininess)
draw_extras()
GL.Flush()
GLcontrol1.SwapBuffers()
End Sub
The way I have constructed my "move forward and backwards" sub is as follows:
Private Sub move_forward_and_back(ByVal forward As Boolean, ByVal delta As Single)
'change the distance between camera and lookat
'distance = old distance
Dim curdistance As Single = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
'curdistance = distance
Dim deltadistance As Single = 0.1
Dim newdistance As Single = deltadistance
'The formula to use for the new point is Xnew = +/-((X2-X1)/Dold)*Dnew+X2
'This formula results with two possible points because of the +/-
'Both points are calculated and then evaluated
Dim newcamx As Single = 0
Dim newcamy As Single = 0
Dim newcamz As Single = 0
Dim newlookatx As Single = 0
Dim newlookaty As Single = 0
Dim newlookatz As Single = 0
Dim newx1 As Single = (((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
Dim newy1 As Single = (((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
Dim newz1 As Single = (((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
Dim newdistance1 As Single = Math.Sqrt((newx1 - cam.lookat.X) ^ 2 + (newy1 - cam.lookat.Y) ^ 2 + (newz1 - cam.lookat.Z) ^ 2)
Dim newx2 As Single = (-((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
Dim newy2 As Single = (-((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
Dim newz2 As Single = (-((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
Dim newdistance2 As Single = Math.Sqrt((newx2 - cam.lookat.X) ^ 2 + (newy2 - cam.lookat.Y) ^ 2 + (newz2 - cam.lookat.Z) ^ 2)
'The one with the greater distance is considered the one to use for "moving away"
'The one with the less distance is considered the one to use for "moving towards"
If forward = True Then 'use one with less distance
If newdistance1 > newdistance2 Then
newcamx = newx2
newcamy = newy2
newcamz = newz2
Else
newcamx = newx1
newcamy = newy1
newcamz = newz1
End If
Else 'use one with greater distance
If newdistance1 < newdistance2 Then
newcamx = newx2
newcamy = newy2
newcamz = newz2
Else
newcamx = newx1
newcamy = newy1
newcamz = newz1
End If
End If
'newcamx, newcamy, and newcamz are calculated for where the camera needs to move to
'need to move lookat the same distance in the same direction
If forward = True Then
newdistance = curdistance + delta
Else
newdistance = curdistance - delta
End If
newx1 = (((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
newy1 = (((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
newz1 = (((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
newdistance1 = Math.Sqrt((newx1 - cam.lookat.X) ^ 2 + (newy1 - cam.lookat.Y) ^ 2 + (newz1 - cam.lookat.Z) ^ 2)
newx2 = (-((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
newy2 = (-((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
newz2 = (-((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
newdistance2 = Math.Sqrt((newx2 - cam.lookat.X) ^ 2 + (newy2 - cam.lookat.Y) ^ 2 + (newz2 - cam.lookat.Z) ^ 2)
If forward = True Then 'we want the one that is smaller
If newdistance1 < newdistance2 Then
newlookatx = newx1
newlookaty = newy1
newlookatz = newz1
Else
newlookatx = newx2
newlookaty = newy2
newlookatz = newz2
End If
Else
If newdistance1 < newdistance2 Then
newlookatx = newx1
newlookaty = newy1
newlookatz = newz1
Else
newlookatx = newx2
newlookaty = newy2
newlookatz = newz2
End If
End If
'now simply assign values
cam.Position.X = newcamx
cam.Position.Y = newcamy
cam.Position.Z = newcamz
cam.lookat.X = newlookatx
cam.lookat.Y = newlookaty
cam.lookat.Z = newlookatz
newdistance = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
End Sub
I am just calculating the camera position points based on a constant distance and knowing the camera lookat. This is done as a calculation instead of matrices which hopefully is good practice.
Now all of this works fine so far but am providing it as background for how I am going about my environment
Problem
I am trying to create a rotate camera function based on two angles. It should keep the distance and cam.position constant while only changing the cam.lookat point. What I have so far:
Private Sub rotate_camera(ByVal deltaangle1 As Single, ByVal deltaangle2 As Single)
Dim curdistance As Single = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
angle1 += deltaangle1
angle2 += deltaangle2
If angle1 >= 360 Then
angle1 = angle1 - 360
End If
If angle2 >= 360 Then
angle2 = angle2 - 360
End If
If angle1 < 0 Then
angle1 = angle1 + 360
End If
If angle2 < 0 Then
angle2 = angle2 + 360
End If
deltax = distance * Sin(deltaangle1 * (PI / 180)) * Cos(deltaangle2 * (PI / 180))
deltay = distance * Sin(deltaangle1 * (PI / 180)) * Sin(deltaangle2 * (PI / 180))
deltaz = distance * Cos(deltaangle1 * (PI / 180))
deltaz = Sqrt((distance ^ 2) - (deltax ^ 2) - (deltay) ^ 2) * zsign
'now simply assign values
cam.lookat.X = cam.lookat.X + deltax
cam.lookat.Y = cam.lookat.Y + deltay
cam.lookat.Z = cam.lookat.Z + deltaz
'distance = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
End Sub
So I have three problems:
- My deltay is always positive. My formulas do not have direction How do I include in my formulas whether or not the delta should be positive or negative?
- On similar lines, there appears to be a reflection point at 180 degrees where the direction of rotation reverses. How should I take into account this reflection?
- On a slightly different note, my camera appears to have its positive X axis in line with the negative world X axis. How do I fix this? How can I have maintain my cam.position and cam.lookat, while rotating the camera?
I know there is a lot to read here but I do hope the questions are basic. I provided enough detail as I am open to adjusting other methods if I am totally off in left field.
Update per questions and answer below
So thank you for all of the help so far! I am mostly there I think. I still have this line which uses the final matrix:
Dim perspective1 As Matrix4 = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3F, GLcontrol1.Width / CSng(GLcontrol1.Height), 0.1F, 2000.0F)
but I have changed the cam.getviewmatrix function using advice to this:
Public Function GetViewMatrix() As Matrix4
Dim myforwardvector As Vector3
Dim rotational_matrix_y As Matrix3
Dim rotational_matrix_x As Matrix3
Dim rotational_matrix_z As Matrix3
Dim upvector As Vector3
If invert_z = False Then
upvector = Vector3.UnitZ
Else
upvector = -Vector3.UnitZ
End If
rotational_matrix_x.M11 = 1
rotational_matrix_x.M12 = 0
rotational_matrix_x.M13 = 0
rotational_matrix_x.M21 = 0
rotational_matrix_x.M22 = Cos(theida * (PI / 180))
rotational_matrix_x.M23 = -Sin(theida * (PI / 180))
rotational_matrix_x.M31 = 0
rotational_matrix_x.M32 = Sin(theida * (PI / 180))
rotational_matrix_x.M33 = Cos(theida * (PI / 180))
rotational_matrix_y.M11 = Cos(theida * (PI / 180))
rotational_matrix_y.M12 = 0
rotational_matrix_y.M13 = Sin(theida * (PI / 180))
rotational_matrix_y.M21 = 0
rotational_matrix_y.M22 = 1
rotational_matrix_y.M23 = 0
rotational_matrix_y.M31 = -Sin(theida * (PI / 180))
rotational_matrix_y.M32 = 0
rotational_matrix_y.M33 = Cos(theida * (PI / 180))
rotational_matrix_z.M11 = Cos(theida * (PI / 180))
rotational_matrix_z.M12 = -Sin(theida * (PI / 180))
rotational_matrix_z.M13 = 0
rotational_matrix_z.M21 = Sin(theida * (PI / 180))
rotational_matrix_z.M22 = Cos(theida * (PI / 180))
rotational_matrix_z.M23 = 0
rotational_matrix_z.M31 = 0
rotational_matrix_z.M32 = 0
rotational_matrix_z.M33 = 1
Dim rotational_matrix As Matrix3
rotational_matrix = Matrix3.Mult(Matrix3.Mult(rotational_matrix_x, rotational_matrix_y), rotational_matrix_z)
myforwardvector = multiply_matrix3_by_vector3(rotational_matrix, myforwardvector)
lookat = multiply_vector3_by_scalar(myforwardvector, distance)
Return Matrix4.LookAt(Position, lookat, upvector)
End Function
The loading of the environment works but nothing changes in my environment when I change theida. I do call my SetupViewPort to refresh the matrix and repaint it like normal. Am I missing something in my matrix creation?
This is what happens currently when holding down the button that increases my yaw only (just changing the X matrix). Keep in mind that one sphere is located at (0,0,0):
Improvement based on second answer
My new ViewMatrix function is as follows:
Dim view_matrix As Matrix4 = Matrix4.LookAt(Position, lookat, up_vector)
'transform to x axis first
view_matrix = view_matrix * Get_Transform_Matrix(-Position.X, 0, 0)
'rotate around x axis
view_matrix = view_matrix * Get_Rotational_Matrix("x", yaw)
'trnsform back
view_matrix = view_matrix * Get_Transform_Matrix(Position.X, 0, 0)
'transform to y axis first
view_matrix = view_matrix * Get_Transform_Matrix(0, -Position.Y, 0)
'rotate around y axis
view_matrix = view_matrix * Get_Rotational_Matrix("y", pitch)
'trnsform back
view_matrix = view_matrix * Get_Transform_Matrix(Position.Y, 0, 0)
'transform to z axis first
view_matrix = view_matrix * Get_Transform_Matrix(0, -Position.Z, 0)
'rotate around z axis
view_matrix = view_matrix * Get_Rotational_Matrix("z", roll)
'trnsform back
view_matrix = view_matrix * Get_Transform_Matrix(Position.Z, 0, 0)
Return view_matrix
My Get_Rotational_Matrix function that retrieves the correct matrix to use for the given rotation:
Public Function Get_Rotational_Matrix(ByVal matrix_name As String, ByVal angle As Single) As OpenTK.Matrix4
'yaw = x, pitch = y, z = roll
Dim rotational_matrix_ As Matrix4
Select Case matrix_name
Case "x"
rotational_matrix_.M11 = 1
rotational_matrix_.M12 = 0
rotational_matrix_.M13 = 0
rotational_matrix_.M14 = 0
rotational_matrix_.M21 = 0
rotational_matrix_.M22 = Cos(angle * (PI / 180))
rotational_matrix_.M23 = -Sin(angle * (PI / 180))
rotational_matrix_.M24 = 0
rotational_matrix_.M31 = 0
rotational_matrix_.M32 = Sin(angle * (PI / 180))
rotational_matrix_.M33 = Cos(angle * (PI / 180))
rotational_matrix_.M34 = 0
rotational_matrix_.M41 = 0
rotational_matrix_.M42 = 0
rotational_matrix_.M43 = 0
rotational_matrix_.M44 = 1
Case "y"
rotational_matrix_.M11 = Cos(angle * (PI / 180))
rotational_matrix_.M12 = 0
rotational_matrix_.M13 = Sin(angle * (PI / 180))
rotational_matrix_.M14 = 0
rotational_matrix_.M21 = 0
rotational_matrix_.M22 = 1
rotational_matrix_.M23 = 0
rotational_matrix_.M24 = 0
rotational_matrix_.M31 = -Sin(angle * (PI / 180))
rotational_matrix_.M32 = 0
rotational_matrix_.M33 = Cos(angle * (PI / 180))
rotational_matrix_.M34 = 0
rotational_matrix_.M41 = 0
rotational_matrix_.M42 = 0
rotational_matrix_.M43 = 0
rotational_matrix_.M44 = 1
Case "z"
rotational_matrix_.M11 = Cos(angle * (PI / 180))
rotational_matrix_.M12 = -Sin(angle * (PI / 180))
rotational_matrix_.M13 = 0
rotational_matrix_.M14 = 0
rotational_matrix_.M21 = Sin(angle * (PI / 180))
rotational_matrix_.M22 = Cos(angle * (PI / 180))
rotational_matrix_.M23 = 0
rotational_matrix_.M24 = 0
rotational_matrix_.M31 = 0
rotational_matrix_.M32 = 0
rotational_matrix_.M33 = 1
rotational_matrix_.M34 = 0
rotational_matrix_.M41 = 0
rotational_matrix_.M42 = 0
rotational_matrix_.M43 = 0
rotational_matrix_.M44 = 1
End Select
'
Return rotational_matrix_
End Function
This seems to work great! Last question, how do I apply the rotations to the up_vector so I can keep track of it? Notice how my new method never changes that.