在着色器上计算标准化设备坐标会显示与在 cpu 上计算不同的结果

Ric*_*uis 3 c++ opengl shader glsl matrix

我创建了一个正交投影相机,用于我的简单 opengl 2d 渲染器。目前,我遇到的问题是,当我在着色器上计算它们时,计算出的标准化设备坐标是错误的,但当我在 cpu 上计算它们时,我得到了所需的结果。

我使用以下公式创建了正交投影矩阵:

2 / (right - left), 0, 0, -((right + left) / (right - left)),
0, 2 / (top - bottom), 0, -((top + bottom) / (top - bottom)),
0, 0, -2 / (zFar - zNear), -((zFar + zNear) / (zFar - zNear)),
0, 0, 0, 1
Run Code Online (Sandbox Code Playgroud)

其中右 = 1280、左 = 0、顶部 = 0、底部 = 720、zFar = 1.0 且 zNear = -1.0。

因此,如果我使用以下顶点位置创建一个矩形:

float vertices[5 * 4] = {
    //vertex pos                tex pos
    0.0f,    720.0f, 0.0f,      0.0f, 0.0f, //bottom left
    1280.0f, 720.0f, 0.0f,      1.0f, 0.0f, //bottom right
    1280.0f, 0.0f,   0.0f,      1.0f, 1.0f, //top right
    0.0f,    0.0f,   0.0f,      0.0f, 1.0f  // top left
};
Run Code Online (Sandbox Code Playgroud)

它应该会产生一个填充整个屏幕的矩形。

为了计算标准化设备坐标,我使用以下公式:

ViewProjectionMatrix * Transform * Position
Run Code Online (Sandbox Code Playgroud)

在顶点着色器中它看起来像这样:

layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;

uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
uniform vec4 position;
out vec2 v_TexCoord;

void main()
{
    v_TexCoord =  a_TexCoord;
    gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
}
Run Code Online (Sandbox Code Playgroud)

u_ViewProjection 是上面提到的 ViewProjection 矩阵,u_Transform 是一个变换矩阵,在本例中是一个简单的单位矩阵,a_Position 是顶点位置。

使用上述值会产生以下渲染结果: 显示三角形的渲染图像

现在,如果我使用以下代码在 cpu 上进行计算:

Mat4x4f transform = Mat4x4f::translate(Mat4x4f(1.0f), Vector3f(0.0f, 0.0f, 0.0f)) * 
Mat4x4f::scale(Vector3f(1.0f, 1.0f, 1.0f));

// ViewProjectionMatrix * Transform * Position
Vector4f tl = camera.getViewProjectionMatrix() * transform * Vector4f(0.0f, 0.0f, 0.0f, 1.0f); 
Run Code Online (Sandbox Code Playgroud)

该变换与传递给 u_Transform 的变换相同,并且与传递给 u_ViewProjection 的camera.getViewProjectionMatrix 相同。

这里我得到了想要的结果:x = -1.0,y = 1.0,它应该是屏幕的左上角。

所以我的问题... 我在着色器中做错了什么还是可能是其他原因导致的?

编辑1 正如接受的答案中所说,投影矩阵需要转置,我通过使用以下来源来到这个矩阵:https ://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic -投影矩阵/正交投影矩阵。不幸的是,他们在这里显示的图像暗示它是列主,而实际上是行主,请参见下面的屏幕截图: 截屏

Rab*_*d76 5

您必须转置矩阵,因为 OpenGL 矩阵按列优先顺序存储。

请参阅OpenGL 着色语言 4.6、5.4.2 矢量和矩阵构造函数,第 108 页:

要通过指定向量或标量来初始化矩阵,组件将按列优先顺序分配给矩阵元素:

mat4(float, float, float, float,  // first column
     float, float, float, float,  // second column
     float, float, float, float,  // third column
     float, float, float, float); // fourth column
Run Code Online (Sandbox Code Playgroud)

4*4 OpenGL矩阵的内存图像

  c0  c1  c2  c3            c0  c1  c2  c3
[ Xx  Yx  Zx  Tx ]        [  0   4   8  12 ]
[ Xy  Yy  Zy  Ty ]        [  1   5   9  13 ]
[ Xz  Yz  Zz  Tz ]        [  2   6  10  14 ]
[  0   0   0   1 ]        [  3   7  11  15 ]
Run Code Online (Sandbox Code Playgroud)

看起来像这样:

[ Xx, Xy, Xz, 0, Yx, Yy, Yz, 0, Zx, Zy, Zz, 0, Tx, Ty, Tz, 1 ]
Run Code Online (Sandbox Code Playgroud)

所以视图矩阵必须是

2 / (right - left), 0,                  0,                   0,
0,                  2 / (top - bottom), 0,                   0,
0,                  0,                  -2 / (zFar - zNear), 0,
-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -((zFar + zNear) / (zFar - zNear)), 1
Run Code Online (Sandbox Code Playgroud)

另一种选择是反转顶点着色器中的乘法顺序:

gl_Position = vec4(a_Position, 1.0) * u_Transform * u_ViewProjection;
Run Code Online (Sandbox Code Playgroud)

请参阅GLSL 编程/向量和矩阵运算

如果一个向量从左边开始与矩阵相乘,则结果相当于从左边开始将行向量与矩阵相乘。这对应于将列向量与右侧的转置矩阵相乘: