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

标签 opengl matrix glsl shader

我目前正在学习 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 版)中实现它会产生奇怪的结果。它有效,但前提是我 发帖 乘以顶点而不是预先乘以它并另外转置每个矩阵。
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 faq :

    For programming purposes, OpenGL matrices are 16-value arrays with base vectors laid out contiguously in memory. The translation components occupy the 13th, 14th, and 15th elements of the 16-element matrix, where indices are numbered from 1 to 16 as described in section 2.11.2 of the OpenGL 2.1 Specification.

    Column-major versus row-major is purely a notational convention. Note that post-multiplying with column-major matrices produces the same result as pre-multiplying with row-major matrices. The OpenGL Specification and the OpenGL Reference Manual both use column-major notation. You can use any notation, as long as it's clearly stated.



    关于一些约定:

    行与列向量

    Multiply只有当左矩阵的列数等于右矩阵的行数时,2 个矩阵才是可能的。
    MatL[r1,c] x MatR[c,r2]
    

    因此,如果您正在处理一张纸,请考虑 向量是一维矩阵,如果你想为 4x4matrix 乘以 4vec 那么向量应该是:
  • 行向量 如果您 后乘矩阵
  • 列向量 如果您 预乘矩阵

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

    行主要与列主要布局

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

    如果您决定 store a 2 dimensional entity ,你有两个约定:
  • 在内存中存储连续的行元素( 行主 )
  • 在内存中存储连续的列元素( 列优先 )

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

    打开GL

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

    Why does this works? is it even legal to post multiply in glsl?



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

    How can I pass my C++ matrices so that they work properly inside the shader?



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

    关于opengl - 顶点着色器中的变换仅适用于后乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37571703/

    相关文章:

    glsl - Shadertoy - fragCoord vs iResolution vs fragColor

    javascript - 将 vec2 数组传递给 THREE.js 中的着色器

    opengl - 多设备 OpenCL/OpenGL 环境

    delphi - 在 Delphi 和 openGL 中按下多个箭头键来移动对象

    opengl - 在 Ubuntu 上设置 OpenGL

    c++ - 使用并行编程 C++ 计算/访问 vector

    c - 渲染一个包含两个 VBO 的 VAO

    python - numpy中3维矩阵的乘法

    r - 如果通常的 `t( )` 不起作用,如何在 r 中转置矩阵?

    c++ - GLSL - 在 SSB 中解压 (V2)