[OpenGL 3.x] Camera Matrix Problem

Started by
5 comments, last by Pateman 14 years, 2 months ago
Hello to all members of Gamedev.net! :D My name is Patryk and I'm new here. I started programming in OGL 3.x and encountered a problem with the camera matrix. Here's the code I'm using to calculate the matrix (in Delphi):

{ .: TMainForm.Render :. }
procedure TMainForm.Render;
var
  ProjMat, Cam, ModelView: TMatrix;
begin
  glViewport(0, 0, ClientWidth, ClientHeight);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  //  Projection update
  if (ClientHeight <= 0) then
    ClientHeight := 1;
  ProjMat := MatrixProjection(40.0, ClientWidth / ClientHeight, 0.1, 1000.0);

  //  Camera transformations
  Cam := MatrixTransform(VectorNegate(Camera.Pos), VectorCreate(Camera.Pitch,
    Camera.Yaw, 0.0), VectorUniform(1.0));
  ModelView := MatrixTransform(VectorCreate(0.0, 0.0, 0.0),
    VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(0.5));
  ModelView := MatrixMultiply(ModelView, Cam);
  ModelView := MatrixMultiply(ModelView, ProjMat);

  //  Update the shader
  glUniformMatrix4fv(glGetUniformLocation(ShaderManager.GLSLPrograms['minimal'].
    ProgramHandle, 'mvpmatrix'), 1, ByteBool(GL_FALSE), @ModelView);

  //  Render the triangle
  glBindVertexArray(TriangleVAO);
	glDrawArrays(GL_TRIANGLES, 0, 3);
  glBindVertexArray(0);
end;
And here's the code of my vertex shader:

#version 150

precision highp float;

uniform mat4 mvpmatrix;

in vec3 in_Position;
in vec3 in_Color;

out vec3 ex_Color;
void main(void) {
	gl_Position = mvpmatrix * vec4(in_Position, 1.0);

	ex_Color = in_Color;
}
The thing is that whenever I change either the Camera.Pitch or Camera.Yaw values, the object rotates instead of the camera. How do I change the code so that I have a Quake-like FPS camera?

Regards,

Patryk Nusbaum.

www.nusbaum.pl

Advertisement
Does the math library that you're using assume the use of column vectors, or row vectors? It looks like your code assumes row vectors, so you might double-check and make sure this matches the convention that the math library uses. (Another thing to double-check is that the matrix layout in memory matches what OpenGL is expecting.)

I'm not sure what the MatrixTransform() function does internally, but it looks like you might be setting up your 'Cam' matrix incorrectly as well (simply negating the translation vector won't necessarily create the inverse transform you're looking for). Assuming you have a matrix inversion function available, try something like this instead:
Cam := MatrixTransform(    Camera.Pos, VectorCreate(Camera.Pitch, Camera.Yaw, 0.0), VectorUniform(1.0));Cam := MatrixInvert(Cam);
Oh right, I should've posted the math code, too.

Here's the definition of my vector and matrix type:
  { .: TVector :. }  PVector = ^TVector;  TVector = packed record    case Integer of      0:        (X: Single;          Y: Single;          Z: Single; );      1:        (V: array [0 .. 2] of Single);  end;  { .: TMatrix :. }  PMatrix = ^TMatrix;  TMatrix = packed record    case Integer of      0:        (_11, _12, _13, _14: Single;          _21, _22, _23, _24: Single;          _31, _32, _33, _34: Single;          _41, _42, _43, _44: Single);      1:        (M: array [0 .. 3, 0 .. 3] of Single);      2:        (V: array [0 .. 15] of Single);  end;

As you can see, both records are unions.

The MatrixTransform function code is as follows:
{ .: MatrixTransform :. }function MatrixTransform(const Position, Rotation, Scale: TVector): TMatrix;var  CosRx, CosRy, CosRz: Single;  SinRx, SinRy, SinRz: Single;begin  CosRx := Cos(Rotation.X); // Used 6x  CosRy := Cos(Rotation.Y); // Used 4x  CosRz := Cos(Rotation.Z); // Used 4x  SinRx := Sin(Rotation.X); // Used 5x  SinRy := Sin(Rotation.Y); // Used 5x  SinRz := Sin(Rotation.Z); // Used 5x  Result := MatrixIdentity;  with Result do  begin    _11 := CosRy * CosRz * Scale.X;    _12 := CosRy * SinRz * Scale.X;    _13 := -SinRy * Scale.X;    _21 := (CosRz * SinRx * SinRy * Scale.Y) - (CosRx * SinRz * Scale.Y);    _22 := (CosRx * CosRz * Scale.Y) + (SinRx * SinRy * SinRz * Scale.Y);    _23 := CosRy * SinRx * Scale.Y;    _31 := (CosRx * CosRz * SinRy * Scale.Z) + (SinRx * SinRz * Scale.Z);    _32 := (-CosRz * SinRx * Scale.Z) + (CosRx * SinRy * SinRz * Scale.Z);    _33 := CosRx * CosRy * Scale.Z;    _41 := Position.X;    _42 := Position.Y;    _43 := Position.Z;  end;end;

Regards,

Patryk Nusbaum.

www.nusbaum.pl

I can't remember off the top of my head if GLSL expects matrix basis vector elements to be contiguous in memory, so if you're not sure yourself you might double check and make sure you're not missing a transpose somewhere. Other than that though, your code looks correct for row-major, row-basis matrices (which is what you have there).

Also, as I mentioned previously it looks like you may be constructing your view matrix incorrectly, so you might take another look at that as well.
Quote:
I can't remember off the top of my head if GLSL expects matrix basis vector elements to be contiguous in memory, so if you're not sure yourself you might double check and make sure you're not missing a transpose somewhere.

I tried inverting the camera matrix, transposing it, and both together, and it still doesn't work.

Quote:
Also, as I mentioned previously it looks like you may be constructing your view matrix incorrectly, so you might take another look at that as well.

The code was used before in a different project and it worked. The same thing is with sending the matrices to GLSL - I carefully followed the examples on the official OpenGL's website. Still, the code doesn't work.

Can you please suggest anything else?

Regards,

Patryk Nusbaum.

www.nusbaum.pl

Finally, after weeks of struggling, the code works! :D

And here is a step-by-step guide:
1. First of all, I disposed of all my matrix code and rewrote it. Here are the functions you will need to compile the code: http://www.nopaste.pl/Source/mhc.txt
2. Use the following shaders: http://www.nopaste.pl/Source/mhd.txt
3. Your rendering code should look like this:
procedure TMainForm.RenderWorld;var  Model, View, Proj, MVP: TBrainMatrix;begin  //  Clear the frame buffer  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  //  Compute the model-view-projection matrix  Proj := Mat4CreatePerspective(60.0, ClientWidth / ClientHeight, 0.1, 1000.0);  Model := Mat4Translate(Mat4CreateRotationEuler(ModelRotation), Vec3Negate(ModelPosition));  View := Mat4Translate(Mat4CreateRotationEuler(CameraRotation), Vec3Negate(CameraPosition));  View := Mat4Inverse(View);  MVP := Mat4Multiply(Model, Mat4Multiply(View, Proj));  //  Update the shader  glUniformMatrix4fv(ShaderManager.ActiveProgram.GetUniformLocation('mvpmatrix'),    1, False, @MVP);  // Rendering code here...end;

Hope this helps. :)

Regards,

Patryk Nusbaum.

www.nusbaum.pl

Unfortunately, there's another problem. I noticed the code I posted doesn't take the direction vector into consideration. It's better explained in the video here:


It seems like the transformation matrix doesn't rotate the axes of an object, so it still moves at the same direction, even though it was rotated. Here's how I wanted to fix that bug:
function TBrainObject.GetMatrix: TBrainMatrix;var  M: TBrainMatrix;  V: TBrainVector;begin  if UpdateNeeded then  begin    M := Mat4CreateRotationEuler(FRotation);    V := Mat4ApplyToVector(M, Vec3(0.0, 0.0, -1.0));    V := Vec3Normalize(V);    CachedMatrix := Mat4Scale(Mat4Translate(M,      Vec3Negate(Vec3Add(FPosition, V))), FScale);    UpdateNeeded := False;  end;  Result := CachedMatrix;end;

But it doesn't seem to help too much.

Can you tell me what's wrong with the code? Or at least suggest some changes?

Regards,

Patryk Nusbaum.

www.nusbaum.pl

This topic is closed to new replies.

Advertisement