【问题标题】:Transformation in vertex shader only works with post-multiplying顶点着色器中的转换仅适用于后乘
【发布时间】:2016-10-01 00:01:33
【问题描述】:

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

作为第一阶段,我编写了一个不使用 OpenGL 的纯 C++ 程序。 它工作得很好,而且它使用行主矩阵表示:

例如 mat[i][j] 表示第 i 行和第 j 列。

class mat4
{
    vec4 _m[4]; // vec4 is a struct with 4 fields
    ...
}

这是相关的矩阵乘法方法:

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

为了从模型空间到剪辑空间,我在 C++ 中执行以下操作:

vec4 vertexInClipSpace =  projectionMat4 * viewMat4 * modelMat4 * vertexInModelSpace;

现在,尝试在 GLSL 着色器(1.5 版)中实现它会产生奇怪的结果。它有效,但前提是我 post 将顶点相乘而不是预乘它,并且另外转置每个矩阵。

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

虽然 v2 = P * V * M * v1transpose(v2) = transpose(v1) * transpose(M) * transpose(V) * transpose(P) 在数学上是一样的, 我显然没有得到任何东西,因为我什至没有看到他们在顶点着色器中发布乘法顶点的 1 个参考。

总结一下,具体问题如下:

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

相关问题的链接:

link 1

link 2

编辑:

问题通过改变调用中的“转置”标志来“解决”:

glUniformMatrix4fv(
        m_modelTransformID,
        1,
        GL_TRUE,
        &m[0][0]
        ); 

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

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

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

谁能解释一下?

【问题讨论】:

    标签: opengl matrix glsl shader


    【解决方案1】:

    引用OpenGLfaq

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

    列优先与行优先纯粹是一种符号约定。笔记 与列主矩阵后乘产生相同的结果 结果作为与行主矩阵的预乘。 OpenGL 规范和 OpenGL 参考手册都使用 column-major 符号。您可以使用任何符号,只要明确说明即可。

    关于一些约定:

    行与列向量

    Multiply 2 个矩阵只有在左矩阵的列数等于右矩阵的行数时才有可能。

    MatL[r1,c] x MatR[c,r2]
    

    所以,如果你正在处理一张纸,考虑到向量是一维矩阵,如果你想将 4vec 乘以 4x4matrix 那么向量应该是:

    • 行向量,如果您后乘矩阵
    • 列向量,如果您预乘矩阵

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

    行主要与列主要布局

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

    如果你决定store a 2 dimensional entity,你有两个约定:

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

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

    Open GL

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

    为什么会这样?在 glsl 中发布乘法是否合法?

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

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

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

    【讨论】:

      【解决方案2】:

      如果您有任何差距,请参阅Understanding 4x4 homogenous transform matrices

      如果您在列主要(OpenGL 矩阵)和行主要(DX 和您的矩阵)矩阵顺序之间进行交换,那么它与转置相同,所以您是对的。你缺少的是:

      对于正交正交齐次变换矩阵,如果你 转置一个矩阵,就像你反转

      我认为这是您问题的答案。

      transpose(M) = inverse(M)
      

      另一个问题是否可以发布乘法顶点,这只是约定的问题,并且在 GLSL 中不被禁止。 GLSL 的全部意义在于您几乎可以在那里做任何事情。

      【讨论】:

      • 感谢您的回答和链接。据我所知,只有旋转矩阵是正交的。我的矩阵链也包括翻译和投影。您能否详细说明并参考我所做的编辑?
      • @NinaKaprez 仅倾斜非线性变换会破坏正交性。任何平移比例或旋转都可以。是的,如果您传递从 GL 转接的矩阵,那么您无需更改任何内容....
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-03
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多