WebGL矩阵(mat4)表示法是否对应于数学矩阵表示法

Сер*_*уло 1 javascript shader opengl-es matrix webgl

OpenGL ES着色语言的文档(第5.11节)指出向量和矩阵乘法运算以正确的线性代数方式执行.线性代数教导我们如果我们将矩阵乘以向量,我们得到这样的结果:

1.0 1.0 0.0   -1.0   (1.0*-1.0)+(1.0*0.0)+(0.0*0.0)=-1.0
0.5 0.0 0.0 *  0.0 = (0.5*-1.0)+(0.0*0.0)+(0.0*0.0)=-0.5
0.0 0.0 0.0    0.0   (0.0*-1.0)+(0.0*0.0)+(0.0*0.0)= 0.0

1.0 1.0 0.0    0.0    1.0
0.5 0.0 0.0 *  1.0 =  0.0
0.0 0.0 0.0    0.0    0.0

1.0 1.0 0.0    1.0    1.0
0.5 0.0 0.0 *  0.0 =  0.5
0.0 0.0 0.0    0.0    0.0
Run Code Online (Sandbox Code Playgroud)

但是当我在WebGL顶点着色器中使用这个数学知识用于矢量乘法矩阵时,我面临着问题.当我尝试乘以上述矩阵的模拟时:

mat4 matrix=mat4(
1.0, 1.0, 0.0, 0.0,
0.5, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
Run Code Online (Sandbox Code Playgroud)

三个垂直:红色(x = -1,y = 0,z = 0,w = 1),绿色(x = 0,y = 1,z = 0,w = 1),蓝色(x = 1,y) = 0,z = 0,w = 1)最简单的等腰三角形我得到奇怪的实际坐标:红色(-1,-1,0,1),绿色(0.5,0,0,1),蓝色(1,1) ,0,1),而不是上述理论计算的预期:红色(-1,-0.5,0,1),绿色(1,0,0,1),蓝色(1,0.5,0,1).显示问题的源代码在这里可用的.

只有当假设mat4矩阵在乘法之前自动转置时,这些奇怪的坐标才是完全正确的(OpenGL ES着色语言的文档声明OpenGL以列主要顺序存储矩阵数据:可能是这个原因?).

那么,问题是,WebGL表示法

mat4 matrix=mat4(
1.0, 1.0, 0.0, 0.0,
0.5, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
Run Code Online (Sandbox Code Playgroud)

在数学意义上意味着这样的矩阵:

1.0 0.5 0.0 0.0
1.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0
0.0 0.0 0.0 1.0
Run Code Online (Sandbox Code Playgroud)

或不?我应该相信自己的眼睛还是文件?

Rab*_*d76 8

那么,问题是,WebGL表示法......在数学意义上意味着像......那样的矩阵吗?

是的,这意味着.

你实际初始化的是一个转置矩阵,与你的预期相比.要么必须更改矩阵初始化,要么必须将向量乘以左边的矩阵.


说明

请参阅OpenGL ES着色语言,5.4.2向量和矩阵构造函数,第43页:

通过指定矢量初始化一个矩阵,或者通过所有的4,9,或16的float mat2,mat3mat4分别.浮点数按列主要顺序分配给元素.

mat2(vec2, vec2);
mat3(vec3, vec3, vec3);
mat4(vec4, vec4, vec4, vec4);

mat2(float, float,
     float, float);

mat3(float, float, float,
     float, float, float,
     float, float, float);

mat4(float, float, float, float,
     float, float, float, float,
     float, float, float, float,
     float, float, float, float);
Run Code Online (Sandbox Code Playgroud)

这意味着,以下初始化一个 mat4

mat4 matrix=mat4(
    1.0, 1.0, 0.0, 0.0,
    0.5, 0.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, 0.0, 1.0);
Run Code Online (Sandbox Code Playgroud)

对应于以下数学矩阵:

  c0  c1  c2  c3             c0   c1   c2   c3
[ Xx  Yx  Zx  Tx ]        [ 1.0  0.5  0.0  0.0 ]     
[ Xy  Yy  Zy  Ty ]        [ 1.0  0.0  0.0  0.0 ]     
[ Xz  Yz  Zz  Tz ]        [ 0.0  0.0  1.0  0.0 ]     
[  0   0   0   1 ]        [ 0.0  0.0  0.0  1.0 ] 
Run Code Online (Sandbox Code Playgroud)

但矩阵的记忆图像是:

   Xx,  Xy,  Xz,   0,  Yx,  Yy,  Yz,   0,  Zx,  Zy,  Zz,   0,  Tx,  Ty,  Tz,   1
[ 1.0, 1.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]
Run Code Online (Sandbox Code Playgroud)


另请参见数据类型(GLSL) - 矩阵构造函数

对于矩阵,施工相当复杂.

如果矩阵是用单个标量值构造的,则该值用于初始化矩阵对角线上的所有值; 其余的都是零.因此,mat4(1.0)是4x4单位矩阵.

对于多个值,矩阵按列主要顺序填充.也就是说,前X个值是第一列,第二个X值是下一列,依此类推.例子:

mat2(
    float, float,   // first column
    float, float);  // second column
Run Code Online (Sandbox Code Playgroud)



访问矩阵的字段时也是如此:

请参阅OpenGL ES着色语言,5.6矩阵组件,第45页:

可以使用数组下标语法访问矩阵的组件.将单个下标应用于矩阵将矩阵视为列向量数组,并选择单个列,其类型是与矩阵大小相同的向量.最左边的列是第0列.然后第二个下标将对列向量进行操作,如前面对向量所定义的那样.因此,两个下标选择一列然后选择一行.

mat4 m;
m[1] = vec4(2.0); // sets the second column to all 2.0
m[0][0] = 1.0; // sets the upper left element to 1.0
m[2][3] = 2.0; // sets the 4th element of the third column to 2.0
Run Code Online (Sandbox Code Playgroud)



注意,矢量和矩阵的乘法定义如下:

请参阅OpenGL ES着色语言,5.11向量和矩阵运算,第50页:

....例外是矩阵乘以向量,向量乘以矩阵,矩阵乘以矩阵.这些不是分量运算,而是执行正确的线性代数乘法.它们需要操作数的大小匹配.

vec3 v, u;
mat3 m;
Run Code Online (Sandbox Code Playgroud)

u = v * m; 相当于

u.x = dot(v, m[0]); // m[0] is the left column of m
u.y = dot(v, m[1]); // dot(a,b) is the inner (dot) product of a and b
u.z = dot(v, m[2]);
Run Code Online (Sandbox Code Playgroud)

而且u = m * v;相当于

u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
Run Code Online (Sandbox Code Playgroud)


参考问题中的第一个例子,这意味着,结果

mat4 m = mat4(
    1.0, 1.0, 0.0, 0.0,
    0.5, 0.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, 0.0, 1.0);

vec4 v = vec4( -1.0, 0.0, 0.0, 1.0 );

u = m * v 
Run Code Online (Sandbox Code Playgroud)

是:

u.x  = m[0].x * v.x  + m[1].x * v.y + m[2].x * v.z + m[3].x * v.w;
u.y  = m[0].y * v.x  + m[1].y * v.y + m[2].y * v.z + m[3].y * v.w;
u.z  = m[0].z * v.x  + m[1].z * v.y + m[2].z * v.z + m[3].z * v.w;
u.w  = m[0].w * v.x  + m[1].w * v.y + m[2].w * v.z + m[3].w * v.w;

u.x  =  1.0 * -1.0   +  0.5 * 0.0   +  0.0 * 0.0   +  0.0 * 1.0  =  -1.0
u.y  =  1.0 * -1.0   +  0.0 * 0.0   +  0.0 * 0.0   +  0.0 * 1.0  =  -1.0
u.z  =  0.0 * -1.0   +  0.0 * 0.0   +  1.0 * 0.0   +  0.0 * 1.0  =   0.0
u.w  =  0.0 * -1.0   +  0.0 * 0.0   +  0.0 * 0.0   +  1.0 * 1.0  =   1.0
Run Code Online (Sandbox Code Playgroud)

但结果u_ = v * m将是:

u_.x  =  dot(v, m[0]);
u_.y  =  dot(v, m[1]);
u_.z  =  dot(v, m[2]); 
u_.w  =  dot(v, m[3]); 

u_.x  =  dot( vec4( -1.0, 0.0, 0.0, 1.0 ), vec4( 1.0, 1.0, 0.0, 0.0 ) )  =  -1.0 
u_.y  =  dot( vec4( -1.0, 0.0, 0.0, 1.0 ), vec4( 0.5, 0.0, 0.0, 0.0 ) )  =  -0.5
u_.z  =  dot( vec4( -1.0, 0.0, 0.0, 1.0 ), vec4( 0.0, 0.0, 1.0, 0.0 ) )  =   0.0
u_.w  =  dot( vec4( -1.0, 0.0, 0.0, 1.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) )  =   1.0 
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢您提供非常详尽和详尽的书面答复!这完全消除了我的困难. (2认同)