顶点着色器中的变换仅适用于后乘

Nin*_*rez 6 opengl shader glsl matrix

我目前正在学习OpenGL和GLSL编写一个简单的软件来加载模型,在屏幕上显示它们,转换它们等等.

作为第一阶段,我在不使用OpenGL的情况下编写了一个纯C++程序.它工作得很好,它使用Row-major矩阵表示:

因此,例如mat [i] [j]表示第i行和第j列.

class mat4
{
    vec4 _m[4]; // vec4 is a struct with 4 fields
    ...
}
Run Code Online (Sandbox Code Playgroud)

这是相关的矩阵乘法:

mat4 operator*(const mat4& m) const
{
    mat4 a(0.0);

    for (int i = 0; i < 4; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            for (int k = 0; k < 4; ++k)
            {
                a[i][j] += _m[i][k] * m[k][j];
            }
        }
    }

    return a;
}
Run Code Online (Sandbox Code Playgroud)

为了从模型空间到剪辑空间,我在C++中做如下:

vec4 vertexInClipSpace =  projectionMat4 * viewMat4 * modelMat4 * vertexInModelSpace;
Run Code Online (Sandbox Code Playgroud)

现在,尝试在GLSL Shader(版本1.5)中实现它会产生奇怪的结果.它的工作原理,但只有当我发布乘以顶点,而不是乘前它和另外调换每个矩阵.

uniform mat4 m;
uniform mat4 v;
uniform mat4 p;

void main()
{
    // works ok, but using post multiplication and transposed matrices :
    gl_Position =  vec4(vertex, 1.0f) * m * v * p;
}
Run Code Online (Sandbox Code Playgroud)

虽然数学OK的v2 = P * V * M * v1是一样的transpose(v2) = transpose(v1) * transpose(M) * transpose(V) * transpose(P) ,我显然没有,因为我还没有看到,甚至1个参考他们乘张贴在顶点着色器顶点得到的东西.

总而言之,以下是具体问题:

  • 为什么会这样?在glsl中发布乘法是否合法?
  • 如何通过我的C++矩阵使它们在着色器中正常工作?

相关问题的链接:

链接1

链接2

编辑:

通过改变调用中的"transpose"标志,问题有点"解决":

glUniformMatrix4fv(
        m_modelTransformID,
        1,
        GL_TRUE,
        &m[0][0]
        ); 
Run Code Online (Sandbox Code Playgroud)

现在着色器中的乘法是预乘:

gl_Position = MVP*vec4(vertex,1.0f);

哪种类型让我感到困惑,因为数学对于作为行主矩阵转置的列主矩阵没有意义.

有人可以解释一下吗?

Hei*_*bug 5

引用 OpenGL常见问题解答

出于编程目的,OpenGL 矩阵是 16 值数组,其中基向量在内存中连续排列。转换组件占据 16 元素矩阵的第 13、14 和 15 个元素,其中索引编号为 1 到 16,如 OpenGL 2.1 规范的第 2.11.2 节所述。

列优先与行优先纯粹是一种符号约定。请注意,使用列主矩阵进行后乘会产生与使用行主矩阵进行预乘相同的结果。OpenGL 规范和 OpenGL 参考手册都使用列优先表示法。您可以使用任何表示法,只要明确说明即可。

关于一些约定:

行与列向量

只有当左矩阵的列数等于右矩阵的行数时,才能2 个矩阵相乘

MatL[r1,c] x MatR[c,r2]
Run Code Online (Sandbox Code Playgroud)

所以,如果你正在写一张纸,考虑到一个向量是一个一维矩阵,如果你想乘以4vec4x4matrix那么向量应该是:

  • 一个行向量,如果你张贴乘矩阵
  • 一个科拉姆载体,如果你预先乘矩阵

在计算机中,您可以将 4 个连续值视为一列或一行(没有维度的概念),因此您可以对同一矩阵的向量进行后乘预乘。隐含地,您坚持使用 2 个约定之一。

行主要与列主要布局

计算机内存是一个连续的位置空间。多维的概念不存在,它是一个纯粹的约定。所有矩阵元素都连续存储到一维存储器中。

如果您决定存储 2 维实体,则有 2 个约定:

  • 在内存中存储连续的行元素(row-major
  • 在内存中存储连续的列元素(column-major

顺便说一下,转置存储在row major中的矩阵元素,相当于按列主要顺序存储其元素。这意味着,交换向量和矩阵之间的乘法顺序等效于以相同的顺序将相同的向量与转置矩阵相乘。

打开 GL

如上所述,它没有正式规定任何约定。我建议您查看 OpenGL 约定,就好像翻译存储在最后一列中并且矩阵布局是列主要的。

为什么这有效?在glsl中发布乘法是否合法?

这是合法的。只要您在代码中保持一致,约定/乘法顺序都可以。

如何传递我的 C++ 矩阵,以便它们在着色器中正常工作?

如果您在 C++ 和着色器中使用 2 种不同的约定,那么您可以转置矩阵并保持相同的乘法顺序,或者不转置矩阵并反转乘法顺序。