1

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:

  1. 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?
  2. 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?
  3. 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):

enter image description here

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.

Eric F
  • 899
  • 2
  • 21
  • 45
  • A. that method you have there is **NOT** how you rotate a vector. B. the only points which cause such a "reflection" are the *poles* of spherical polar coordinates (+z and -z directions); typically the vertical rotation (pitch) is clamped to some range so as to never cross the poles. C. not sure why your movement function is so convoluted - just add a positive multiple of the direction to the position if moving forward, and negative if backwards; it should take far fewer lines of code than what you have, and even less if you use overloaded operator for vector types – meowgoesthedog Oct 02 '17 at 19:31
  • @meowgoesthedog How would I make a method for rotation then if this is not the way to go at it? From all I have seen I need another matrix to multiply my stack by right? Currently I have Dim perspective1 As Matrix4 = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3F, GLcontrol1.Width / CSng(GLcontrol1.Height), 0.1F, 2000.0F) so should I have another matrix multiplied here? – Eric F Oct 02 '17 at 19:45
  • Actually I found that my cam has a function.. cam.move(deltax,deltay,deltaz) seems to work fine but cam.addrotation(delta1, delta2) does nothing to the fram – Eric F Oct 02 '17 at 19:52
  • In your `delta` calculations you use `deltaangle` instead of `angle`. Is this right? Those trigonometric formulas use angles relative to axis, not "incremental" angles. – Ripi2 Oct 02 '17 at 20:07
  • I changed it to use true angles instead of increments but it still reflects across angles for some reason – Eric F Oct 02 '17 at 20:13
  • How do you get the `deltaangle` values? – Ripi2 Oct 02 '17 at 20:20
  • @meowgoesthedog How do I use matrices instead of trig? I am trying so hard to understand this – Eric F Oct 02 '17 at 20:20
  • @Ripi2 They are inputs so for example the key "A" will have -1 as a deltaangle and "D" as +1 to try and get direction to the rotation – Eric F Oct 02 '17 at 20:21
  • The `lookAt` matrix you pass to GPU not only contains `camera.lookat` vector, but also needs local `right`and `up`vectors which must be updated for each rotation. – Ripi2 Oct 02 '17 at 20:24
  • The rotation matrices page on Wikipedia has 3D rotation matrices for all three axes. Use these in the correct order to achieve what you want. – meowgoesthedog Oct 02 '17 at 20:51
  • read [Understanding 4x4 homogenous transform matrices](https://stackoverflow.com/a/28084380/2521214) especially the linked answers at the bottom where you can find C++ examples for camera and player control – Spektre Oct 03 '17 at 08:55
  • @meowgoesthedog Using the help below, I did implement the 3 rotational matrices but nothing seems to move. See the above updated question with my updated code. Do you see any errors in what I have done? – Eric F Oct 04 '17 at 14:44
  • @Ripi2 Wouldn't my up always be Z in my case? – Eric F Oct 04 '17 at 14:46
  • No. `camera.up` vector changes. Remember it must be perpendicular to new `camera.lookat`vector. Same for `camera.right`. – Ripi2 Oct 04 '17 at 14:56
  • @Ripi2 See the above update. I did try changing Return Matrix4.LookAt(Position, lookat, upvector) to Return Matrix4.LookAt(Position, lookat, myforwardvector) Isn't this where I would change my up vector? – Eric F Oct 04 '17 at 15:17

2 Answers2

1

I think you should have a private member where you store your forward vector. The direction pointing in front of your camera. At start you should look in the z direction so for example it could be myForwardvector = vec3(0,0,1).

You also should store the distance to the point you are aiming. So the lookAt position should become something like:

vec3 position = myForwardVector * radius;

Now when when you want to rotate your forward vector depending on your input, your mouse etc ...

you can use spherical coordinates

enter image description here

where theta is the angle when you rotate left/right (yaw) and Phi top/bottom (pitch). Roll is not used in a fps.

If you want to use matrice you can start by a litlle example and just implementing the yaw movement.

this is what thoose matrices look like:

enter image description here

Use the one from the middle and replace theta by your yaw angle.

Now each time you made a movement you change your forwardVector:

myForwardVector = Rotationmatrice * myForwardVector;
myLookAtMatrice = glm::lookat(myCameraPos, myFowardVector * radius, upVector)

Hope it helps,

note that you will update your upVector using the same method.

Paltoquet
  • 1,184
  • 1
  • 10
  • 18
  • Thank you so much for your help! This has helped me quite a bit but I am still not there 100% yet. Can you please take a look at my update to my question? I think I have my matrix function to what you are saying here.. do you see something I may be missing? – Eric F Oct 03 '17 at 17:03
  • So I got it to work so far... it rotates around the Y axis only when using the Ry matrix you have above. How would I get it so my camera can rotate by theida and phi? – Eric F Oct 03 '17 at 17:53
  • You can chain rotation by multiplying rotation matrice with each other. But the order is important you can do something like myForwardVector = Ry * Rz * myForwardVector. – Paltoquet Oct 03 '17 at 19:43
  • So I did try that.. I did the following but then nothing rotates or moves: rotational_matrix = Matrix3.Mult(Matrix3.Mult(rotational_matrix_x, rotational_matrix_y), rotational_matrix_z) myforwardvector = multiply_matrix3_by_vector3(rotational_matrix, upvector) lookat = multiply_vector3_by_scalar(myforwardvector, distance) – Eric F Oct 03 '17 at 19:59
  • myforwardvector = multiply_matrix3_by_vector3(rotational_matrix, upvector)it should be myforwardvector again. Also try to check if you intilise alll your totation matrice to identity – Paltoquet Oct 04 '17 at 07:22
  • I tried changing that line to: myforwardvector = multiply_matrix3_by_vector3(rotational_matrix, myforwardvector) but still doesn't move. Where would I check if I initialize my rotation matrix to identity? I don't know what you mean by this but I have posted my code above. Do you see where I am missing something? – Eric F Oct 04 '17 at 14:42
  • sorry for mispelling something, the theida shouldn't be the same in each of the matrices. It's a diffrent angle for each type of camera movement. They are 3 types of movement allowed. in a fps you will use your horizontal mose movement for the rotation around the Y axis (yaw) and vertical movement movement auround the X axis (Roll) matrice on the left Rx. So you need 2 angle or just ignore RX and you got what you had before. – Paltoquet Oct 04 '17 at 14:59
  • I actually just discovered that from the Wikipedia page too. I did pass the three angles respectively to each of the matrices. It still appears to rotate the camera around origin (0,0,0) and not my cam.position. Take a look at my updated question. The animation is what happens when I hold down the button that increases my yaw, never letting go during the animation. – Eric F Oct 04 '17 at 15:13
  • when you rotate around the y axis your upvector should stay the same (0,1,0). But when you are doing vertical movement, you need to also change your upvector. myupvector = multiply_matrix3_by_vector3(rotational_matrix, myupvector). do a 3D repere with your hand one of your finger should represent your upvector – Paltoquet Oct 04 '17 at 15:25
  • That is what I have already though. myforwardvector = multiply_matrix3_by_vector3(rotational_matrix, upvector) I already have upvector defined so it is the same as what you are saying. I have tried having upvector = Vector3.UnitY as well as upvector = Vector3.UnitZ called before I multiply it. – Eric F Oct 04 '17 at 16:06
1

@DraykoonD answer is good. But It seems you're a bit confused with geometry. So, here are some explanations:

If your objects don't move, you just have to steps to go: Camera and Projection.

Camera

Transforming the camera means translating and/or rotating it. Both movements are usually expressed by an only matrix, usually called "lookAt" matrix.
If you work with matrices, then C= R*T is the operation (lookAt C = Rotation * Translation, in this order, not T*R)

Translation
While T is pretty simple, perhaps you want to replace matrices operation with some code on your own. Something like:

camera.x = camera.x + deltaX
camera.y = camera.x + deltaY
camera.z = camera.x + deltaZ

Note that the deltas are not the necessary the same, as the user may whish to move in 1/2/3 axis at once.

If, instead of the above, you want to move the camera by stepSizein the current direction (that it aims to) then get a unit-vector in that direction and calculate deltas and use them as before:

vx = target.x - camera.x
vy = target.y - camera.y
vz = target.x - camera.z
modulus = Sqrt(vx*vx + vy*vy + vz*vz)
' components of the unit vector
dx = vx / modulus
dy = vy / modulus
dz = vz / modulus
' stepSize is a signed value, forward or backwards
deltaX = dx * stepSize
deltaY = dy * stepSize
deltaZ = dz * stepSize

Rotation
Fist of all: Things rotate by an angle around an axis, not around a point.
Read the last sentence again. It means that you can not simply give two angles and get the rotation, you also need the order in which those two rotations occurs, because the result is not the same if you change the order.
Now read it once more. It means you need an axis for each rotation.
What confuses people is that rotation needs an origin. Think of the nail of a compass you use to draw an arc.
So: an axis, an angle, and an origin.

OK. Let's explain rotations in a FPS-game-like. We will use for the origin of the rotation the camera position. Again, don't call this "rotating around a point".

Let's say you have your axis X+ to right, Y+ deep into the screen, Z+ up. This is a right-handled system: if you calculate the cross product of two unit vectors aligned with the axis you get the third unit-vector. Cross product, as matrices multiplication, is not commutative; so pay attention to the order, or you'll get a vector in the opposite direction.
This axis system is easy to understand if you use it as the world coordinates system.
A left-handled system is the same, but with some sign changed, for example Z= Y x X, instead of Z= X x Y

If your first rotation is over Z axis then, applying the Rz matrix shown in the Wikipedia Rotation to the current X-vector and Y-vector we get the new X',Y' vectors, Z' is the same as Z. If in the first place the camera target was (0, d, 0) then post-multiply it by Rz to get the new target (tx, ty, 0)
"Pre" or "post" matrix multiplication depends on if the matrix is column or row mayor order. Typically in OpenGL and many graphics APIs column mayor order is used, which is the same as that Wikipedia link.

Now suppose the user wants a down-up rotation. The axis needed is the X' we obtained before, after Z-axis rotation. This is a "local" axis. If we apply the Rx matrix we get a local rotation. Right. But what we need is a "global" direction, so as the objects are located well.
Here's when matrix stuff shines: just apply R2= Rx*Rz instead of R2= Rx. If you apply R2 to (0, d, 0) you get the new2 target (t2x, t2y, t2z)
It's so simple as "stacking" the transformations: Cn= Cn-1 * ... *C5*C4*C3*C2*C1 where Ck is the column-mayor-order matrix for the 'k' transformation (translation or rotation or whatever). Just be aware of the order; row-mayor-order reverses it.

Once you have your first "camera matrix" the following matrices are just a matter of stacking.

Note: translations can not be represented by a 3x3 matrix. But a 4x4 does. Using 4x4 matrices allows stacking any transformation.

Now the user wants a Z-axis (right-left) rotation again. Mmmm, let me see... When we do a rotation the whole axis-system changes. That means that now, after those two previous rotations, the Z-local-axis, where the "up-camera" vector is, is not the same as the Z-global. But the user does want a Z-global rotation, not a local one (user wants X-local but Z-global)
We've seen before that each Ck matrix represents a local transformation. What we need now is the Z-global axis expressed in local system. That is: Zv= R2 * (0,0,1)
OK, but I only know of Rx,Ry,Rz matrices and now Z-global is not the same as none of my local axis. True. We need the matrix rotation around any axis. This link shows it.

All right. But... how is the "first camera matrix" built? This lookAt link shows it. The parameters needed (camera position, target and up-camera) depends on what you initially want to "see".
If you are curious about the deduction of the matrix, I tell you that a transform matrix row is the new axis vector expressed in old axis vectors. And that you can get vectors using the cross product and normalizing them.

Good. Instead of playing with stacked matrices (I know they degenerate after many transformations due to numerical issues) I want to get "camera.position", "camera.target" and "camera.up" (important: all of them in global coordinates) and use that lookAtmatrix on my own, for every camera movement. Then you must calculate those three vectors every time.

The translation is showed at the beggining of this answer. You must translate both camera.position and camera.target. camera.up is a vector, not a point; thus, translating a vector has no sense.

For rotations recall we use the position of the camera as the origin of any rotation. That means that before rotating the target transform it's global coordinates to the origin, then rotate, then undo the translation, Something like this:

target.x = target.x - camera.position.x
target.y = target.y - camera.position.y
target.z = target.z - camera.position.z
target = RotateGeneric(axis, angle, target)
target.x = target.x + camera.position.x
target.y = target.y + camera.position.y
target.z = target.z + camera.position.z

I think it's good you have a RotateGeneric function that uses the matrix in the link I wrote some lines ago.

To rotate the up-camera vector you can use the same function (without translations, it's a vector, right?)

You are doing all calculations on global system. Z-axis rotation is simple. But X-local-axis, at any moment... how do we get it? Well, there are two options:

  • Keep track of its current (x,y,z) direction. In other words, rotate it also as you do for up-camera.
  • Use matrix magic again. Every rotation can be undone. The matrix needed is the inverse of the current matrix, IC= C-1. So if you have the X-local xv=(1,0,0) and want its components in global coordinates then xg= IC * xv. Of course, you need to update and store the current C matrix.

If you also rotate around Y-axis, the process is the same as for X-axis.

Projection

Using matrix stack, or lookAt matrix at every movement, what you get is an axis-system such as X and Y axis are aligned with OpenGL device axis-system.
Now two jobs left: projecting and dealing with Z-axis orientation.

As you correctly suspect, matrix is again here.

I will not write here about orthographic nor perspective matrix representations, that's not the subject of the question.
Just point out that those matrices have a -1 in a proper site that reverses the sign of Z, so OpenGL left-handed device system is happy. Well, that mostly always used value is not strictly needed; but if +1 is set, then you should change the depth compare function to tell OpenGL to use a right-handed system instead.
Stack the projection matrix "after" the camera matrix M= P*C and that's all.

Ripi2
  • 7,031
  • 1
  • 17
  • 33
  • So coding aside... the basic method seems to be 1) Set up original view matrix.2) When rotating or moving the camera, getting the current view matrix 3) Translating that matrix to be "above the axis" to rotate around (ie if rotating around the x, making x = 0) 4) Apply rotation matrix for the given axis of rotation (so if rotating around x, then the x matrix) 5) Translate back (if rotating around x, doing a transform matrix) 6) Then repeat if doing the other two axis. Is this the right methodology? – Eric F Oct 05 '17 at 13:49
  • For this "first person shooter" camera move style, all rotations have as origin the camera position. So, yes, translate-rotate-untranslate is the basic method. With "rotate" I mean as many rotations as you like, i.e. around several axis. – Ripi2 Oct 05 '17 at 14:19
  • *"rotating around the x, making x = 0"* What do you mean by this? – Ripi2 Oct 05 '17 at 14:21
  • @EricF Please, edit the question and move those comments (code) there. – Ripi2 Oct 05 '17 at 14:32
  • Updated. Take a look – Eric F Oct 05 '17 at 14:36
  • No. It isn't `translateToCam.x, rotateArounX, translateToCam.y, rotateAroundY, etc`. See in my answer the code where I use `RotateGeneric` – Ripi2 Oct 05 '17 at 14:42
  • Try imagining yourself holding in your head the axis.system while moving and looking around. Pay attention how axis rotate, Also "transform" may mean "translate" (as you seem to understand), but a rotation is also a transformation. Use clear names for your functions, – Ripi2 Oct 05 '17 at 14:45
  • Ah so the first translations are putting the camera to (0,0,0) first.. where mine was putting it to (0,something, something). Your RotateGeneric is basically my Get_Rotational_Matrix though isn't it? – Eric F Oct 05 '17 at 14:47
  • @EricF I don't know because you did'nt show `Get_Rotational_Matrix` code. If it's related to `rotational_matrix_...= ...` then no, nothing to do – Ripi2 Oct 05 '17 at 14:54
  • Take a look at my question now. I added my Get_rotational_matrix. It just picks which rotation matrix to use. – Eric F Oct 05 '17 at 14:58
  • @EricF. That update only uses rotations around local axis. What I'm talking about is a generic, any axis, rotation matrix. In both methods I exposed you need a generic version, because in this FPS example there are two kinds or rotations; around local X or around global Z. Please, please, before trying more code try first to understand what you need. – Ripi2 Oct 05 '17 at 15:04
  • So what you are saying is 1) Get current Matrix 2) Translate matrix to (0,0,0) 3) RotateGeneric 4) Translate back right? – Eric F Oct 05 '17 at 15:17
  • @EricF Translate to camera position, whether it's (0,0,0) or not. Please, read **slowly** my full answer. I know it's dense. With a bit of imagination you'll get used with local and global axis systems. – Ripi2 Oct 05 '17 at 15:20
  • I have read your answer a few times now.. So are you saying to move the world (global) to the camera's location (local) and not the camera to the world's location? That may be where we are off. Which matrix are you referring to as our starting one? The local camera matrix or the world global one? – Eric F Oct 05 '17 at 15:24
  • If you're out in the dark space and you see a ship coming to you... or it's you going to the ship? The result is the same. The matrix is the same, except the signs. Choose one. I chose to move the camera. – Ripi2 Oct 05 '17 at 15:29
  • OK so I chose that as well. So would you agree the order is 1) Get current Matrix 2) Translate matrix to camera position 3) RotateGeneric 4) Translate back to where it started right? – Eric F Oct 05 '17 at 16:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/156029/discussion-between-eric-f-and-ripi2). – Eric F Oct 05 '17 at 16:15
  • Yes, that's the way of rotating a point around camera position. In my answer I show an example for rotating `target`. – Ripi2 Oct 05 '17 at 16:16
  • It finally clicked how I have been rotating only around the global axis and not my local. So for example if I want to rotate around the Z axis as you have shown, I would do something like this: x_vector = (multiply_matrix3_by_vector3(Get_Rotational_Matrix3("z", yaw), x_vector)) and then y_vector = (multiply_matrix3_by_vector3(Get_Rotational_Matrix3("z", yaw), y_vector)) where x_vector, y_vector, and z_vector represent my camera axis. Once I have that, I can just use Matrix4.Lookat(camera_position, y_vector, z_vector) right? Since Matrix4.Lookat calls for eye, target, up vector in that order – Eric F Oct 05 '17 at 18:18
  • This works so far! :) so if I move my camera position, looking left and right (rotation around my local Z) works fine. For looking up and down, wouldn't I rotate around my x axis, so using the xrotation matrix on my local y and local Z axis? – Eric F Oct 05 '17 at 18:45