OpenGL 4.2 LookAt 矩阵仅适用于眼睛位置的 -z 值

OpenGL 4.2 LookAt matrix only works with -z value for eye position

我正在尝试理解和应用现代 OpenGL 矩阵变换。我已经阅读了很多不同的来源,但我不确定我到底做错了什么。

我遇到的问题在代码中也有评论:如果我将 Matrix4.LookAt 的眼睛坐标设置为大于或等于 0 或小于 -2 的 z 值,则三角形不再可见。

谁能解释一下为什么?据我所知,三角形应该只从另一边可见(明确禁用面部剔除不会改变任何东西)。

另一件事很奇怪:如果我旋转三角形,如果我使用 eye-z = -2,它似乎会被剪掉;如果我使用 -1,它看起来 "smoother"。有什么想法吗?

完整程序如下:

using System;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;

namespace OGL420_Matrices
{
    // OpenTK version 3.1.0
    internal class Program
    {
        public static void Main(string[] args)
        {
            var program = new Program();
            program.Run();
        }

        private GameWindow _gameWindow;
        private Matrix4 _projectionMatrix;
        private Matrix4 _viewMatrix;
        private Matrix4 _viewProjectionMatrix;
        private Matrix4 _modelMatrix;
        private int _vbaId, _programId, _viewProjectionUniformId, _modelMatrixUniformId;

        private void Run()
        {
            // 4, 2 is OpenGL 4.2
            using (_gameWindow = new GameWindow(800, 600, GraphicsMode.Default, "", GameWindowFlags.Default,
                DisplayDevice.Default, 4, 2, GraphicsContextFlags.Default))
            {
                _gameWindow.Load += OnLoad;
                _gameWindow.Resize += OnResize;
                _gameWindow.RenderFrame += OnRenderFrame;

                _gameWindow.Run();
            }
        }

        private void OnResize(object sender, EventArgs e)
        {
            var clientArea = _gameWindow.ClientRectangle;
            GL.Viewport(0, 0, clientArea.Width, clientArea.Height);
        }

        private void OnLoad(object sender, EventArgs e)
        {
            _projectionMatrix = Matrix4.CreateOrthographic(3, 3, 0.001f, 50);

            // change -1 to -2.1f you dont see anything
            // change -1 to -2f you still see the same
            // change -1 to >= 0 you dont see anything; of course 0 doesn't make sense but 1 would
            _viewMatrix = Matrix4.LookAt(
                new Vector3(0, 0, -1f), // eye
                new Vector3(0, 0, 0), // target
                new Vector3(0, 1, 0)); // up
            _modelMatrix = Matrix4.Identity;

            var data = new float[]
            {
                0, 0, 0,
                1, 0, 0,
                0, 1, 0
            };

            var vboId = GL.GenBuffer();

            GL.BindBuffer(BufferTarget.ArrayBuffer, vboId);
            GL.BufferData(BufferTarget.ArrayBuffer, data.Length * sizeof(float), data, BufferUsageHint.StaticDraw);

            _vbaId = GL.GenVertexArray();

            GL.BindVertexArray(_vbaId);
            GL.BindBuffer(BufferTarget.ArrayBuffer, vboId);

            GL.EnableVertexAttribArray(0);
            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 0, 0);

            var vertexShaderId = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vertexShaderId, @"#version 420

layout(location = 0) in vec3 position;

uniform mat4 viewProjection;
uniform mat4 model;

out vec3 outColor;

void main()
{
    gl_Position = viewProjection * model * vec4(position, 1);
    outColor = vec3(1,1,1);
}");

            GL.CompileShader(vertexShaderId);
            GL.GetShader(vertexShaderId, ShaderParameter.CompileStatus, out var result);

            if (result != 1)
                throw new Exception("compilation error: " + GL.GetShaderInfoLog(vertexShaderId));

            var fragShaderId = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fragShaderId, @"#version 420

in vec3 outColor;
out vec4 fragmentColor;

void main()
{
    fragmentColor = vec4(outColor, 1);
}");

            GL.CompileShader(fragShaderId);
            GL.GetShader(fragShaderId, ShaderParameter.CompileStatus, out result);

            if (result != 1)
                throw new Exception("compilation error: " + GL.GetShaderInfoLog(fragShaderId));

            _programId = GL.CreateProgram();
            GL.AttachShader(_programId, vertexShaderId);
            GL.AttachShader(_programId, fragShaderId);

            GL.LinkProgram(_programId);
            GL.GetProgram(_programId, GetProgramParameterName.LinkStatus, out var linkStatus);

            if (linkStatus != 1) // 1 for true
                throw new Exception("Shader program compilation error: " + GL.GetProgramInfoLog(_programId));

            GL.DeleteShader(vertexShaderId);
            GL.DeleteShader(fragShaderId);

            _viewProjectionUniformId = GL.GetUniformLocation(_programId, "viewProjection");
            _modelMatrixUniformId = GL.GetUniformLocation(_programId, "model");
        }

        private void OnRenderFrame(object sender, FrameEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit);

            _viewProjectionMatrix = _projectionMatrix * _viewMatrix;

            GL.UniformMatrix4(_viewProjectionUniformId, false, ref _viewProjectionMatrix);
            GL.UniformMatrix4(_modelMatrixUniformId, false, ref _modelMatrix);

            GL.UseProgram(_programId);
            GL.BindVertexArray(_vbaId);
            GL.DrawArrays(PrimitiveType.Triangles, 0, 3);

            _gameWindow.SwapBuffers();
        }
    }
}

首先我将引用对 OpenTK 问题的评论:Problem with matrices #687:

Because of how matrices are treated in C# and OpenTK, multiplication order is inverted from what you might expect in C/C++ and GLSL. This is an old artefact in the library, and it's too late to change now, unfortunately.


与 glsl 相比,列主阶矩阵必须从右到左相乘,其中右矩阵是应用的矩阵 "first",在 OpenTK 中,矩阵必须相乘从左到右。

这意味着,如果你想在 glsl 中计算 viewProjectionMatrix,它先进行视图变换然后进行投影,那么在 glsl 中它是(对于列主序矩阵):

mat4 viewProjectionMatrix = projectionMatrix * viewMatrix;

如果你想在 OpenTK 中使用 Matrix4 做同样的事情,那么你必须做:

_viewProjectionMatrix = _viewMatrix * _projectionMatrix;