投影矩阵的问题,一个1:1:1的立方体看起来沿着z轴拉伸



我正在制作一个渲染器,我在透视投影矩阵方面遇到了一些麻烦。

以下是我的透视投影矩阵

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));
}

然后将其与相机的变换矩阵和模型的变换矩阵相乘。

它工作,但z方向似乎被拉伸了一点,如果我使z大得多,拉伸甚至更明显,所以我认为它可能与zRange有关,但我已经将它与矩阵中的zRange分开,所以它不应该被重新缩放吗?

下面是我程序的结果。1:1:1的立方体在投影后看起来很奇怪在角落里更奇怪

——更新这是顶点着色器

#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
}
下面是我的矩阵 的完整代码
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);
}

还有矩阵函数的细节,但应该不会有任何问题,我测试了很多次。

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));
}

如果你想做和Matrix4.CreatePerspectiveFieldOfView一样的事情,你需要对矩阵进行转置并反转一些分量。

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, -1),
new Vector4(0                    , 0              , -2*_zNear*_zFar / _zRange  ,  0));
}

OpenGL矩阵以列为主顺序存储。第一列是x轴,接着是y轴和z轴。第四列是翻译。
这意味着每一行(Vector4)代表矩阵的一列。

通常的OpenGL坐标系统是一个右手坐标系。在视野空间中,z轴指向视线。归一化设备空间是一个左手系统。因此,z轴被投影矩阵反转。参见左手坐标系与右手坐标系。

你可以在3D Geometry中找到一个工作的c#/OpenTK示例

@Rabbid76非常非常感谢你的帮助。我解决了这个问题。答案是......我从来没有错。我做了大量的测试,我发现答案确实是正确的,结果应该看起来很奇怪。因为,如果你想一下,投影矩阵使x和y变小而视场变大(更宽的视野意味着更小的物体),但是因为z是一个线性函数的输入

(_zFar + _zNear) / (_zFar - _zNear) * z + -2 * _zNear * _zFar / (_zFar - _zNear)

所以当改变fov时,立方体的z的长度是不变的,但是x和y变小了,这就是为什么它看起来很奇怪。

正如@Rabbid76好心提醒我的提示,我想是因为我的游戏引擎也是左手系统,所以矩阵不同。

证明:当视场为90时,立方体在unity中看起来也是有线的

And: So as mine

相关内容

最新更新