微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决投影矩阵的问题,一个 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),new Vector4(0,1 / _tanHalffov,-(_zFar + _zNear) / _zRange,2*_zNear*_zFar / _zRange),1,0));
}

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

它有效,但z方向似乎有点拉伸,如果我把zFar调大一点,拉伸就更明显了,所以我认为这可能与zRange有关,但我已经将其与zRange分开了矩阵,所以它不应该重新缩放吗?

以下是我的程序的结果。 the 1:1:1 cube looks weird after the projection even weirder in the corner

---更新---

这是顶点着色器

#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,-_CameraPosition.x),-_CameraPosition.y),-_CameraPosition.z),1));
    }

    public static Matrix4 Translation(Vector3 _Position)
    {
        return new Matrix4(new Vector4(1,_Position.x),_Position.y),_Position.z),1));
    }

    public static Matrix4 Scale(float _x,float _y,float _z)
    {
        return new Matrix4(new Vector4(_x,_y,_z,1));
    }

    public static Matrix4 RotateX(float _Angle)
    {
        double _radians = Math_of_Rotation.radians_of(_Angle);
        return new Matrix4(new Vector4(1,(float)Math.Cos(_radians),(float)Math.Sin(_radians),-(float)Math.Sin(_radians),1));
    }

    public static Matrix4 RotateY(float _Angle)
    {
        double _radians = Math_of_Rotation.radians_of(_Angle);
        return new Matrix4(new Vector4((float)Math.Cos(_radians),new Vector4((float)Math.Sin(_radians),1));
    }

    public static Matrix4 RotateZ(float _Angle)
    {
        double _radians = Math_of_Rotation.radians_of(_Angle);
        return new Matrix4(new Vector4((float)Math.Cos(_radians),1));
    }
    public static Matrix4 Projection(float _zNear,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 ),_tanHalffov,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),new Vector4(0,1 / _tanHalffov,-(_zFar + _zNear) / _zRange,-1),-2*_zNear*_zFar / _zRange,0));
}

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

通常的 OpenGL 坐标系是右手坐标系。在视图空间中,z 轴指向视线。标准化设备空间是一个左手系统。因此,z 轴被投影矩阵反转。见Left- vs. Right-handed coordinate systems

您可以在 3D Geometry

找到一个有效的 C#/OpenTK 示例 ,

@Rabbid76 非常感谢您的帮助。我解决了这个问题。答案是……我从来没有错。我进行了大量的测试,我发现答案确实是正确的,而且结果应该看起来是那样的。因为,如果你考虑一下,投影矩阵使 x 和 y 更小而 fov 更大(更宽的视野意味着更小的对象),但因为 z 是线性函数的输入

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

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

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

证明:Cube in unity also looks wired when the fov is 90

并且:So as mine

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。