I am working on a renderer, and I am having some troubles with the perspective projection matrix.
Following is my perspective projection matrix.
public static Matrix4 Projection(float _zNear, float _zFar, float _Width, float _Height, float _fov)
{
float _ar = _Width / _Height;
float _tanHalffov = (float)Math.Tan(Math_of_Rotation.Radians_of(_fov / 2));
float _zRange = _zFar - _zNear;
return new Matrix4(new Vector4(1/(_tanHalffov * _ar), 0 , 0 , 0),
new Vector4(0 , 1 / _tanHalffov , 0 , 0),
new Vector4(0 , 0 , -(_zFar + _zNear) / _zRange , 2*_zNear*_zFar / _zRange),
new Vector4(0 , 0 , 1 , 0));
}
I then multiplied it with the camera's transform matrix and the model's transform matrix.
it works but the z direction seems stretched a bit, and if I make zFar larger, the stretching is even more obvious, so I figured it may be something to do with the zRange, but I have divided it with zRange in the matrix, so isn't it supposed to be rescaled?
Following is the result of my program. the 1:1:1 cube looks weird after the projection even weirder in the corner
---Update---
This is the vertex shader
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;
uniform vec3 cam_pos;
uniform mat4 transform;
uniform mat4 nptransform;
vec4 temp_Pos;
out vec3 normal0;
out vec2 texCoord0;
out vec3 cam_angle;
out vec3 position0;
void main()
{
temp_Pos = nptransform * vec4(position, 1.0);
position0 = vec3(temp_Pos.x,temp_Pos.y,temp_Pos.z);
cam_angle = normalize(cam_pos - position0);
normal0 = normal;
texCoord0 = texCoord;
gl_Position = transform * vec4(position, 1.0);//the bug is about this line
}
The following is the complete code of my matrix
public Matrix4 GetTransform(Vector3 _OffSet)
{
return Matrix4.Translation(Position - _OffSet) * Matrix4.RotateX(Rotation.x) * Matrix4.RotateY(Rotation.y) * Matrix4.RotateZ(Rotation.z) * Matrix4.Scale(Scale.x, Scale.y, Scale.z);
}
public Matrix4 GetProjectdTransform(Vector3 _OffSet)//This is the one I sent to the shader.
{
Transform CameraTransform = Core.The_Camera.Attaching_GameObject.transform;
return Matrix4.Projection(Core.MainCamera.zNear, Core.MainCamera.zFar, Core.MainCamera.Width, Core.MainCamera.Height, Core.MainCamera.fov) * Matrix4.RotateX(CameraTransform.Rotation.x) * Matrix4.RotateY(CameraTransform.Rotation.y) * Matrix4.RotateZ(CameraTransform.Rotation.z) * Matrix4.CameraTranslation(CameraTransform.Position) * GetTransform(_OffSet);
}
And there is the detail of the matrix functions, but there shouldn't be any problem, I tested them a lot of times.
public static Matrix4 CameraTranslation(Vector3 _CameraPosition)
{
return new Matrix4(new Vector4(1, 0, 0, -_CameraPosition.x),
new Vector4(0, 1, 0, -_CameraPosition.y),
new Vector4(0, 0, 1, -_CameraPosition.z),
new Vector4(0, 0, 0, 1));
}
public static Matrix4 Translation(Vector3 _Position)
{
return new Matrix4(new Vector4(1, 0, 0, _Position.x),
new Vector4(0, 1, 0, _Position.y),
new Vector4(0, 0, 1, _Position.z),
new Vector4(0, 0, 0, 1));
}
public static Matrix4 Scale(float _x, float _y, float _z)
{
return new Matrix4(new Vector4(_x, 0, 0, 0),
new Vector4(0, _y, 0, 0),
new Vector4(0, 0, _z, 0),
new Vector4(0, 0, 0, 1));
}
public static Matrix4 RotateX(float _Angle)
{
double _Radians = Math_of_Rotation.Radians_of(_Angle);
return new Matrix4(new Vector4(1, 0, 0, 0),
new Vector4(0, (float)Math.Cos(_Radians), (float)Math.Sin(_Radians), 0),
new Vector4(0, -(float)Math.Sin(_Radians), (float)Math.Cos(_Radians), 0),
new Vector4(0, 0, 0, 1));
}
public static Matrix4 RotateY(float _Angle)
{
double _Radians = Math_of_Rotation.Radians_of(_Angle);
return new Matrix4(new Vector4((float)Math.Cos(_Radians), 0, -(float)Math.Sin(_Radians), 0),
new Vector4(0, 1, 0, 0),
new Vector4((float)Math.Sin(_Radians), 0, (float)Math.Cos(_Radians), 0),
new Vector4(0, 0, 0, 1));
}
public static Matrix4 RotateZ(float _Angle)
{
double _Radians = Math_of_Rotation.Radians_of(_Angle);
return new Matrix4(new Vector4((float)Math.Cos(_Radians), -(float)Math.Sin(_Radians), 0, 0),
new Vector4((float)Math.Sin(_Radians), (float)Math.Cos(_Radians), 0, 0),
new Vector4(0, 0, 1, 0),
new Vector4(0, 0, 0, 1));
}
public static Matrix4 Projection(float _zNear, float _zFar, float _Width, float _Height, float _fov)
{
float _ar = _Width / _Height;
float _tanHalffov = (float)Math.Tan(Math_of_Rotation.Radians_of(_fov / 2));
float _zRange = _zFar - _zNear;
return new Matrix4(new Vector4((_tanHalffov ) , 0 , 0 , 0),
new Vector4(0 , _tanHalffov , 0 , 0),
new Vector4(0 , 0 , -(_zFar + _zNear) / _zRange , 2*_zNear*_zFar / _zRange),
new Vector4(0 , 0 , 1 , 0));
}
@Rabbid76 Thanks very very much for your helps. I solved the question. The answer is...... I was never wrong. I ran through tons of test, and I found that the answer were in deed correct, and the outcome is suppose to look that wired. Cause, if you think about it the projection matrix makes x and y smaller while fov is larger(wider view means smaller object), but cause z is the input of a linear function
(_zFar + _zNear) / (_zFar - _zNear) * z + -2 * _zNear * _zFar / (_zFar - _zNear)
so when changing fov the length of a cube's z is never change but the x and y is smaller, that's why it looks weird.
As the hints @Rabbid76 so kindly to remind me, I think cause my game engine is also left handed system, so the matrix is different.
Proof: Cube in unity also looks wired when the fov is 90
And: So as mine