DirectX 11 在把矩阵(例如View矩阵)传给shader之前为什么要进行转置(相关引擎坐标系及左乘与右乘的区别总结)

问题描述:在DriectX11中的代码不清楚为什么要把矩阵转置之后要传递给shader困惑很久终于在网上查了描述该问题的原因,以下是我遇到的代码片段:

    D3DXMatrixTranspose( &pcbAllShadowConstants->m_WorldViewProj, &dxmatWorldViewProjection );
    D3DXMatrixTranspose( &pcbAllShadowConstants->m_WorldView, &dxmatCameraView ); 
    D3DXMatrixTranspose( &pcbAllShadowConstants->m_World, &dxmatIdentity );

下面是摘录人说明的原因:

一,
在DirectX11里, 能看到这样的代码

D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);

目的是把矩阵转置, 之后再传给shader

二,

根据3D数学的相关知识, 把一个矩阵代表的变换作用于一个Vector(或Point), 需要矩阵和Vector相乘
如果矩阵是row-major, 则矩阵在右, Vector在左
如果矩阵是column-major, 则矩阵在左, Vector在右 (例如UNITY)
注意这里的左乘/右乘和内存中的数据存放方式没有关系, 它只是纯粹的数学

三,
下面来看在DirectX11里的shader代码
output.position = mul(input.position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);
这里的矩阵都已经是转置过的矩阵, 即column-major, 但是为什么它们都是在乘法的右侧呢
根据前面所说的, column-major矩阵应该是在乘法左边才对

四, 详细解释

矩阵位置索引
[ 1 2 3 4 ]
[ 5 6 7 8 ]
[ 9 10 11 12 ]
[ 13 14 15 16 ]
如果有一个矩阵形如
[ a b c d]
[ e f g h]
[ i j k l ]
[m n o p]

那么
行主, 内存位置索引为
a b c d e f h i j k l m n o p

列主, 内存位置索引为
a e i m b f j n c g k o d h l p
行主乘法如下
V * M = 数字为内存位置索引[V * M[1,5,9,13], …] = 数字为矩阵位置索引[V * M[1,5,9,13], …] = [V * M[a,e,i,m], …]

列主乘法如下
M * V = 数字为内存位置索引[M[1,5,9,13] * V, …] = 数字为矩阵位置索引[M[1,2,3,4] * V, …] = [V * M[a,e,i,m], …]

在HLSL中比较特殊, 按理说矩阵在右侧应该是行主乘法, 但是HLSL又默认期望所有矩阵都是列主
所以HLSL中的矩阵乘法和前面有所不同, 如下
V * M = 数字为内存位置索引[V * M[1,2,3,4], …] = 数字为矩阵位置索引[V * M[1,2,3,4], …] = [V * M[a,e,i,m], …]

五, 为什么要这样? 因为速度
根据
http://www.gamedev.net/topic/662986-hlsl-mul-and-rowcolumn-major-matricies-in-directx
里的回复
HLSL生成的shader代码型如
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
正如[四,]里写出的HLSL乘法公式, 注意是数字为内存索引位置的那个

而使用普通的列主乘法, shader代码会变成
mul r0, v0.y, c1
mad r0, c0, v0.x, r0
mad r0, c2, v0.z, r0
mad oPos, c3, v0.w, r0

回复者的原话是
“Having single column in one constant register allows to quickly calc resulting vector component with one dot instruction.”

参考资料:
http://www.gamedev.net/topic/662986-hlsl-mul-and-rowcolumn-major-matricies-in-directx/
http://xboxforums.create.msdn.com/forums/t/112276.aspx
http://stackoverflow.com/questions/16578765/hlsl-mul-variables-clarification
http://www.rastertek.com/dx11tut04.html

关键是这句话重要“在HLSL中比较特殊, 按理说矩阵在右侧应该是行主乘法, 但是HLSL又默认期望所有矩阵都是列主所以HLSL中的矩阵乘法和前面有所不同”。

下面在来总结以下行主矩阵与列矩阵在以下引擎的使用情况:

opengl  右手坐标系 列向量 左乘 列主序存储矩阵
osg     右手坐标系 行向量 右乘 行主序存储矩阵
d3d      左手坐标系 行向量 右乘 行主序存储矩阵
ogre     右手坐标系 列向量 左乘 行主序存储矩阵

osg 底层使用的是opengl ,行向量,由于使用矩阵是行主序存储,
相同变换矩阵与opengl矩阵内存内容一直,可直接使用api设置变换矩阵;

ogre 支持opengl,d3d,opengl与ogre上层矩阵库运算顺利相同,
但opengl api设置矩阵前需要修改矩阵的存储顺序为列主序,
d3d使用右乘,ogre上层运算的矩阵应用到d3d api前需要转置处理,
d3d使用与ogre不同的坐标系,设置ogre的ViewMatrix到d3d api前取反look方向(view坐标系z轴)

1.矩阵在内存中的存储

不管是D3D还是OpenGL,使用的矩阵都是线性代数标准的矩阵,只是在存储方式上有所不同。分别为:行主序(Direct3D),列主序(OpenGL)

存储顺序说明了线性代数中的矩阵如何在线性的内存数组中存储。
例如:内存中使用一个二维数组m存储矩阵,第i行第j列的表示方法分别为: 
行主序:m[i][j]
列主序:m[j][i]

线性代数意义的同一个矩阵,在d3d 和 openGL 中的存储顺序
线代:a11,a12,a13,a14 d3d : a11,a12,a13,a14 OpenGL: a11,a21,a31,a41
a21,a22,a23,a24 a21,a22,a23,a24 a12,a22,a32,a42
a31,a32,a33,a34 a31,a32,a33,a34 a13,a23,a33,a43
a41,a42,a43,a44 a41,a42,a43,a44 a14,a24,a34,a44

2.行主序的矩阵与列主序的矩阵如何转换呢?

不难发现,M行=M列T(T:转置矩阵)。
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值