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

时间:2016-06-01 14:34:55

标签: opengl matrix glsl shader

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

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

因此,例如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 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;
}

虽然在数学上可以正常v2 = P * V * M * v1transpose(v2) = transpose(v1) * transpose(M) * transpose(V) * transpose(P)相同, 我显然没有得到一些东西,因为我甚至没有看到它们在顶点着色器中发布乘法顶点的一个参考。

总之,以下是具体问题:

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

相关问题的链接:

link 1

link 2

编辑:

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

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

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

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

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

有人可以解释一下吗?

2 个答案:

答案 0 :(得分:4)

引用OpenGL faq

  

出于编程目的,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 然后矢量应该是:

  • 行向量,如果后乘矩阵
  • 如果预乘矩阵,
  • colum vector

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

行主要与列主要布局

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

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

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

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

打开GL

如上所述,它没有正式规定任何惯例。我建议您查看OpenGL约定,就好像转换存储在最后一列中,矩阵布局是列专业。

  

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

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

  

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

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

答案 1 :(得分:0)

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

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

  

对于正交正交同质变换矩阵如果你   转置矩阵就像你反转一样

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

transpose(M) = inverse(M)

另一个问题是,如果可以发布乘法只是常规问题的顶点,并且在 GLSL 中不被禁止。 GLSL 的重点在于你几乎可以做任何事情。