【问题标题】:Calculating normalized device coordinates on shader shows different results then if calculated on cpu在着色器上计算归一化设备坐标显示不同的结果,然后在 cpu 上计算
【发布时间】:2020-04-20 16:22:43
【问题描述】:

我创建了一个正交投影相机,用于我的简单 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

其中右 = 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
};

它应该会导致一个矩形填满整个屏幕。

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

ViewProjectionMatrix * Transform * Position

在顶点着色器中是这样的:

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

u_ViewProjection 是上面提到的 ViewProjection 矩阵,u_Transform 是一个变换矩阵,在这种情况下是一个简单的 Identity 矩阵,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); 

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

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

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

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

【问题讨论】:

  • 您没有提供足够的信息,但可能您必须转置矩阵。请注意,OpenGL 矩阵按列主要顺序存储。
  • 您对 CPU 实现的看法分歧在哪里?

标签: c++ opengl matrix glsl shader


【解决方案1】:

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

参见The OpenGL Shading Language 4.6, 5.4.2 Vector and Matrix Constructors,第 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

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 ]

看起来像这样:

[ Xx, Xy, Xz, 0, Yx, Yy, Yz, 0, Zx, Zy, Zz, 0, Tx, Ty, Tz, 1 ]

所以视图矩阵必须是

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

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

gl_Position = vec4(a_Position, 1.0) * u_Transform * u_ViewProjection;

GLSL Programming/Vector and Matrix Operations:

如果一个向量从左边乘以一个矩阵,结果对应于从左边乘一个行向量到矩阵。这对应于从右边将一个列向量与转置矩阵相乘:

【讨论】:

  • 好答案。值得注意的是,后乘以列为主的矩阵与预乘以行为主的矩阵相同。只要在 CPU 和 GPU 之间保持一致,这完全取决于他们更喜欢使用哪个应用程序。 ALU 并不真正关心这两种情况。
  • @3Dave 是的,我知道,但我尽量简短回答。
  • 我想你知道。评论真的是针对花生画廊的。 :)
  • @3Dave 无论如何,我在答案末尾添加了一条注释。
  • 这确实似乎解决了这个问题,虽然我使用scratchapixel.com/lessons/3d-basic-rendering/… 作为指导但这里他们使用了错误的图像显示行主投影矩阵同时告诉它是列主矩阵,但这很奇怪,仍然应该我自己看过这个
猜你喜欢
  • 2016-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-26
相关资源
最近更新 更多