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;
我正在尝试理解和应用现代 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;