So I have successfully managed to rotate, move, etc with keyboard keys my camera through my environment. I do this by multiplying my view matrix by rotational matrices:
ViewMatrix = ViewMatrix X Z_Rotational_Matrix(yaw_increment) X X_Rotational_Matrix(pitch_increment) X Y_Rotational_Matrix(roll_increment)
I can do so with inputs of yaw, pitch, and roll. So I am next trying to do these rotations with the mouse. I believe I have all of the heavy lifting done and should just need to supply the yaw, and pitch to my rotational matrix. Correct me if this is the wrong thought process.
I can capture the mouse world coordinate when I click and when I move so supposedly it should just be math based on initial_mouse_click_coordinates and current_mouse_click_coordinates. My thought is to project two vectors, one from the initial_mouse_click_coordinate and the other from current_mouse_click_coordinates. Both vectors are parallel to the vector created from my camera location and my camera lookat point. Then I can calculate the XY planar angle and the XZ planar angle. Once these are determined I pass these as the yaw and pitch to my rotational matrices.
Determining two angles between my two lines:
The problem I have is that these values appear to be really small so on the screen nothing really happens. Am I going about this the wrong way completely?
If this is the right method to try, am I messing up on my math somewhere?
'we have initial_mouse_world_coordinates and mouse_world_coordinates. These are the points on the x screen in world coordinates.
'Two lines must be constructed going through these points, parallel with our lookat vector.
'The two angles between these two vectors are the angles to use on our lookat vector
'X = X0 + Rx*T
'Y = Y0 + Ry*T
'Z = Z0 + Rz*T
'therefore T = (Z-Z0)/Rz
'at Z = 0: T = -Z0/Rz
Dim t As Decimal = -initial_mouse_world_coordinates.Z / cam.lookat.Z
Dim y As Decimal = initial_mouse_world_coordinates.Y + (cam.lookat.Y * t)
Dim x As Decimal = initial_mouse_world_coordinates.X + (cam.lookat.X * t)
Dim z As Decimal = 0
'new point = x,y,z => translate to new vector
Dim startline As New Vector3(initial_mouse_world_coordinates.X - x, initial_mouse_world_coordinates.Y - y, initial_mouse_world_coordinates.Z - z)
t = -mouse_world_coordinates.Z / cam.lookat.Z
y = mouse_world_coordinates.Y + (cam.lookat.Y * t)
x = mouse_world_coordinates.X + (cam.lookat.X * t)
z = 0
Dim endline As New Vector3(mouse_world_coordinates.X - x, mouse_world_coordinates.Y - y, mouse_world_coordinates.Z - z)
'now simply find the two angles between these two lines
'cos(theida) = ((Ai,Ak) ⋅ (Bi,Bk)) / (||Ai,Ak|| * ||Bi,Bk||)
Try
theida = Acos(((startline.X * endline.X) + (startline.Z * endline.Z)) / (Sqrt(startline.X ^ 2 + startline.Z ^ 2) * Sqrt(endline.X ^ 2 + endline.Z ^ 2)))
Catch
theida = 0
End Try
Try
phi = Acos(((startline.X * endline.X) + (startline.Y * endline.Y)) / (Sqrt(startline.X ^ 2 + startline.Y ^ 2) * Sqrt(endline.X ^ 2 + endline.Y ^ 2)))
Catch
phi = 0
End Try
theida = theida * (180 / PI)
phi = phi * (180 / PI)
Any help or guidance is appreciated. Again I may be going at this with the wrong idea in the first place.
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.
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
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:
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.
#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.
Introduction
I am somewhat new to using Open GL / Open TK. I have learned how to draw basic shapes, use matrices, lighting, shadowing, etc. I have a function that draws a sphere:
Private Sub drawSphere(r As Double, lats As Integer, longs As Integer)
Dim i As Integer, j As Integer
For i = 0 To lats
Dim lat0 As Double = PI * (-0.5 + CDbl(i - 1) / lats)
Dim z0 As Double = Sin(lat0)
Dim zr0 As Double = Cos(lat0)
Dim lat1 As Double = PI * (-0.5 + CDbl(i) / lats)
Dim z1 As Double = Sin(lat1)
Dim zr1 As Double = Cos(lat1)
GL.Begin(PrimitiveType.QuadStrip)
For j = 0 To longs
Dim lng As Double = 2 * PI * CDbl(j - 1) / longs
Dim x As Double = Cos(lng)
Dim y As Double = Sin(lng)
GL.Normal3(x * zr0 * r, y * zr0 * r, z0 * r)
GL.Vertex3(x * zr0 * r, y * zr0 * r, z0 * r)
GL.Normal3(x * zr1 * r, y * zr1 * r, z1 * r)
GL.Vertex3(x * zr1 * r, y * zr1 * r, z1 * r)
Next
GL.End()
Next
End Sub
I have other code that sets up the lights. I know the other code works because I have a separate function for drawing an STL object:
Dim texture As UInteger() = New UInteger(0) {}
Dim i As Integer = 0
If stl_table.Items.Count > 0 Then
find_center_of_part()
GL.Begin(PrimitiveType.Triangles)
GL.Color3(part_color.R, part_color.G, part_color.B)
Do Until i + 4 >= stl_table.Items.Count
GL.Normal3(Convert.ToSingle(stl_table.Items.Item(i).SubItems(0).Text), Convert.ToSingle(stl_table.Items.Item(i).SubItems(1).Text), Convert.ToSingle(stl_table.Items.Item(i).SubItems(2).Text))
GL.Vertex3(stl_table.Items.Item(i + 1).SubItems(0).Text - avgx, stl_table.Items.Item(i + 1).SubItems(1).Text - avgy, stl_table.Items.Item(i + 1).SubItems(2).Text - avgz)
GL.Vertex3(stl_table.Items.Item(i + 2).SubItems(0).Text - avgx, stl_table.Items.Item(i + 2).SubItems(1).Text - avgy, stl_table.Items.Item(i + 2).SubItems(2).Text - avgz)
GL.Vertex3(stl_table.Items.Item(i + 3).SubItems(0).Text - avgx, stl_table.Items.Item(i + 3).SubItems(1).Text - avgy, stl_table.Items.Item(i + 3).SubItems(2).Text - avgz)
i = i + 4
Loop
GL.End()
End If
This second function basically imports a CAD STL file and draws it as triangles. The normal vectors are simply an input from the CAD file (so they are already computed). This method's lighting works perfectly fine which makes me know my lighting code is correct.
Problem
The problem is that my sphere is not getting light correctly. I know through testing that this is due to my normal vectors.
With the current code, my sphere looks like this:
There is a "spot" of light which makes me think that is simply one of the quadstrips having the normal correct.
Does anybody have any suggestions on setting up the normal vectors correctly inside my function? Also before anybody suggests it, I can't use GLU or GLUT for what I am trying to accomplish, which is why I need the sphere function.
I am making an arc with a triangle fan. The triangle fan has to have set points for each of the vertices to make the arc shape. I have found a multitude of docs regarding DrawArc, but that is not what I am after, and cannot find anything on creating "x number of points" across the arc from point A to point B.
It has been several years since my last trig class, so I am hoping someone has an idea of how to increment the x/y location of the points between A-B. Here is what I have so far:
Dim points As Integer = 5 ' the number of points between top and right
Dim radius as Integer = 25
' Center point
Dim cx As Integer = loc.X + (size.Width - (radius))
Dim cy As Integer = loc.Y + thickness
' Top point
Dim x1 As Integer = loc.X + (size.Width - (radius))
Dim y1 As Integer = loc.Y
' Right point
Dim x2 As Integer = loc.X + (size.Width)
Dim y2 As Integer = loc.Y + radius
Dim trifan As New VertexArray(PrimitiveType.TrianglesFan)
trifan .Append(New Vertex(New Vector2f(cx, cy), col2)) ' Center point
trifan .Append(New Vertex(New Vector2f(x1, y1), col1)) ' Top point
For i = 1 To points
' append other points here...
Next
trifan .Append(New Vertex(New Vector2f(x2, y2), col1)) ' Right point
I'm just posting this here so it will have an answer for others who come across it. I had a lot of help with this (as recommended) here: https://math.stackexchange.com/questions/1789110/plot-points-on-an-arc
For i = 0 To points - 1
Dim x As Double
Dim y As Double
Dim t As Double = (PI / 2) * (i / points - 1)
x = cx + Cos(t) * (radius)
y = cy + Sin(t) * (radius)
trifan1.Append(New Vertex(New Vector2f(x, y), col1))
Next
I found a great script to arrange objects (shapes) into a circle here:
Aligning Shapes in a Circle using VBA, Microsoft Community
Sub Test()
Call AlignShapesInCircle(720 / 2, 540 / 2, 100, ActiveWindow.Selection.ShapeRange)
End Sub
Function AlignShapesInCircle(x As Single, y As Single, r As Single, shprng As ShapeRange)
'x,y = center point of the circle
'r = radius of the circle
'shprng = the shape selection that needs to be arranged
Dim angle As Single
Dim currentangle As Single
Dim x1 As Single
Dim y1 As Single
Dim i As Integer
currentangle = 0
angle = 360 / shprng.count
For currentangle = 0 To 359 Step angle
i = i + 1
x1 = r * Cos(D2R(currentangle))
y1 = r * Sin(D2R(currentangle))
shprng(i).Left = x + x1
shprng(i).Top = y + y1
Next
End Function
Function D2R(Degrees) As Double
D2R = Degrees / 57.2957795130823
End Function
Function R2D(Radians) As Double
R2D = 57.2957795130823 * Radians
End Function
Now I want the shapes to rotate so that if I use arrows the tip will always show towards the center.
I have to introduce a line here:
shprng(i).Left = x + x1
shprng(i).Top = y + y1
shprng(i).Rotation = ???
Any ideas where I could find the proper formula?
Silly - figured it out - it was easier than I thought. Don't need any SIN and COS which frightened me - just:
shprng(i).Rotation = (360 / (shprng.Count)) * (i - 1)