环境是 webgl 。
感觉是哪里错了,但是又很奇怪不知道错在哪里,关键字搜了 why transpose 之类的也是下结论之类的。 向大家请教,请喝杯咖啡。
在 glsl 手册 56 页
vec3 v, u;
mat3 m;
u = v * m;
is equivalent to
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]);
And
u = m * v;
is equivalent to
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;
向量使用的是列向量吧,比如这时候有一个平移变换 T(tx, ty, tz)
按照我的理解应该是
[
1, 0, 0, tx,
0, 1, 0, ty
0, 0, 1, tz,
0, 0, 0, 1
]
但是在 gl-matrix 变换里 是这样:
[
1, 0, 0, 0,
0, 1, 0, 0
0, 0, 1, 0,
tz, ty, tz, 1
]
这时候在一个 shader (这是实际正确的) 里
attribute vec4 aVertexPosition;
uniform mat4 uModelMatrix;
void main(void) {
gl_Position = uModelMatrix * aVertexPosition;
}
在上面的 shader 里,如果按照 gl-matrix 的写法,变成了
M = [
1, 0, 0, 0,
0, 1, 0, 0
0, 0, 1, 0,
tz, ty, tz, 1
]
V = [x, y, z, 1];
gl_Position = M * V;
相乘后 tx, ty, tz 不能变换上去吧?
但是 gl-matrix 是对的(我看的这篇教程 也是这样),请问这是为什么?
1
3dwelcome 2021-06-17 11:29:12 +08:00
gl-matrix 是定死的,但是 shader 里却是灵活的。可以 Row-Major,也可以 Column-Major 。
区别就是 M*v 和 v*M 的写法不一样。 至于为什么 Shader 里要同时提供两种矩阵格式,那是微软年代的历史遗留问题。当年 GPU 弱鸡,为了性能优化,少几个 DP4 都是赚到的,具体可看 http://www.mvps.org/directx/articles/nontranspose.htm |
2
ccraohng OP @3dwelcome
是我的表述问题。 上面手册里定点向量实际为 [x, y, z, 1] 它的平移变换矩阵,按照 `uModelMatrix * aVertexPosition` 的写法应该(我的理解)为 ``` [ 1, 0, 0, tx, 0, 1, 0, ty 0, 0, 1, tz, 0, 0, 0, 1 ] ``` 但是 gl-matrix 是这样的 ``` [ 1, 0, 0, 0, 0, 1, 0, 0 0, 0, 1, 0, tz, ty, tz, 1 ] ``` gl-matrix 写法是 T(tx, ty, tz) 的转置矩阵,按照我的理解在 shader 里位置应该调换一下 ``` gl_Position = aVertexPosition * uModelMatrix ``` 为啥 |
4
3dwelcome 2021-06-17 12:13:12 +08:00
"但是 gl-matrix 是这样的"
不是啊,https://github.com/toji/gl-matrix/blob/master/src/mat4.js 一开始注释里,就写着是 column-major 格式,也就是应该用 Matrix * V 的写法。 |
5
3dwelcome 2021-06-17 12:17:09 +08:00
不是啊,github.com/toji/gl-matrix/blob/master/src/mat4.js 一开始注释里,就写着是 column-major 格式,也就是应该用 V * Matrix 的写法。
上面打错了,汗。 Matrix * V 是 row-major 的写法。 |
6
ccraohng OP @3dwelcome 你看下 https://github.com/toji/gl-matrix/blob/master/src/mat4.js#L829 fromTranslation 函数, 变换值是写在 12 13 14 索引上的
|
8
3dwelcome 2021-06-17 12:34:48 +08:00
@ccraohng 你说的对,那就是注释写错了。
我看 mat4.js 里的 translate 函数,也证明内部结构 row-major,并不符合标准的 OpenGL Matrix 格式,也许是为了和一些 3D 软件相互兼容。 那么 gl_Position = M * V;这样写就没错了。(可明明 V * M 更快) |
9
ccraohng OP https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/uniformMatrix
这个方法假定矩阵是列优先。 [ 1, 0, 0, tx, 0, 1, 0, ty 0, 0, 1, tz, 0, 0, 0, 1 ] 会变成 [ 1, 0, 0, 0, 0, 1, 0, 0 0, 0, 1, 0, tz, ty, tz, 1 ] 与正确结果相反,所以 gl-matrix 使用的是 [ 1, 0, 0, 0, 0, 1, 0, 0 0, 0, 1, 0, tz, ty, tz, 1 ] |
10
3dwelcome 2021-06-17 14:13:15 +08:00
好吧,我又来打脸了。gl-matrix 注释没错,矩阵是列优先。
我去查看了 https://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays 里面的详细说明。 这个矩阵 [ 1, 0, 0, tx, 0, 1, 0, ty 0, 0, 1, tz, 0, 0, 0, 1 ] 在连续内存里,真实排列就是: [1,0,0,0][0,1,0,0][0,0,1,0][tx,ty,tz,1] 所以 fromTranslation 函数, 变换值是填在 12 13 14 索引上,是完全没问题的。 所以不是 gl-matrix 写法问题,而是内存布局本来就是这样写的。 |
11
sillydaddy 2021-06-17 14:40:15 +08:00 2
@ccraohng
@3dwelcome 这个行主序、列主序,确实挺绕脚的,今天看两位在这讨论,于是又去查了一下, 下面这个解释应该是靠谱的: https://blog.lazybee.me/d3dopengl_matrix/ 意思就是,行主序和列主序,在实际程序里面,真正的内存布局都是一样的,都是 m[12] m[13] m[14]表示平移值,但是,2 者对于行和列的解释不一样,行主序主张第 i 行第 j 列的值存储在 a[i][j],即 m[i*4+j]中,列主序则主张第 i 行第 j 列的值存储在 a[j][i],即 m[j*4+i]中。 那么这两种不同的主张,为什么会有相同的内存布局呢?这是因为,行主序主张把向量看作是“行”,坐标变换写作 v=v*M,而列主序主张把向量看作是“列”,于是 v=M*v 。这样的要求,导致按行主序(v*M)运算时,要从 M 中按列取,而按列主序(M*v)运算时则要按行取。而行主序对“行”的定义,并不是列主序对“行”的定义,反而恰恰是列主序对“列”的定义(如前所述的 a[i][j]和 a[j][i]),所以,内存布局就一样了。所以关键在于是 v*M 还是 M*v 。 下面这个说明里面也解释了作者的无奈。 https://glmatrix.net |
12
sillydaddy 2021-06-17 14:42:46 +08:00
@ccraohng
所以,不用再担心需要矩阵转置啦,内存布局都是一样的。只需注意,行主序时,向量的变换要写成 v*M,把向量看作行;列主序则是 M*v,把向量看作列。 |
13
sillydaddy 2021-06-17 15:07:45 +08:00
@sillydaddy
上面解释的有点啰嗦,用一句话概括下: m[0],m[4],m[8],m[12],对于行主序矩阵来说,是其第一列。而对于列主序来说,是其第一行。 所以行矩阵的 v*M[第一列],与列矩阵的 M[第一行]*v,是一样的。 |
14
ccraohng OP @sillydaddy
什么行列优先。 我是奇怪 gl-matrix 的写法为何“反着”的, 在 shader 里是 [ 1, 0, 0, tx, 0, 1, 0, ty 0, 0, 1, tz, 0, 0, 0, 1 ] * [x, y, z, 1] MDN 上说了 uniformMatrix 是以列优先录入值,即坐标 0 - 3 会被转成 m[0] ( m[0] is the left column of m ) [ 1, 0, 0, 0, 0, 1, 0, 0 0, 0, 1, 0, tz, ty, tz, 1 ] 刚好被 转成 [ 1, 0, 0, tx, 0, 1, 0, ty 0, 0, 1, tz, 0, 0, 0, 1 ] |
15
ccraohng OP @sillydaddy 没有 “转成”这个概念,我错了。是你发的链接中的 不同解释,老哥可以的话加我 v cmhkaWFuamk=
|
16
sillydaddy 2021-06-17 18:13:57 +08:00
@ccraohng
哈哈。☕️就不用啦。你发这个帖子也帮我把行列主序的概念理解清楚了。🙏 |