今天学习dx11的时候遇到一个问题,事情是这样的
dx11引入了常量缓存(const buffer)用来实现数据的高速传输,这块儿buffer是cpu only write,gpu only read这样的特点,其他还是dx9的惯例,
SetShaderParameters
的时候,出现了这样的问题,先上代码:
D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);
result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
dataPtr = (MatrixBufferType*)mappedResource.pData;
dataPtr->world = worldMatrix;
dataPtr->view = viewMatrix;
dataPtr->projection = projectionMatrix;
deviceContext->Unmap(m_matrixBuffer, 0);
可以看到,worldMatrix,viewMatrix,projectionMatrix三个矩阵都是在进行了转置之后才传入常量缓存的,为何要转置呢,经过一番打探原来事情是这样样子的:
首先我们要知道,矩阵分为行主序和列主序两种矩阵,比如:内存中使用一个二维数组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
d3d是左手定则,opengl是右手定则,另外提一句,OGRE用的也是列主序,我们发现只要把行主序和列主序的行列对调就可以了,也就是转置一下就一样了,也就是说d3d和opengl如果想转换的话只需要转置一下就ok,跑远了,那dx11这里为什么要转置呢,因为dx11的constant buffer里面shader读取是列主序的读取,所以想在const buffer使用的话还是要转换成列主序,或者也可以编译shader的时候调用D3D10_SHADER_PACK_MATRIX_ROW_MAJOR这个来编译,其实效果是一样的,只不过转换做在了内部,其实早在dx11之前gpu也是一直读取列主序的矩阵的,只不过转换在内部进行了而已。
或者还有一种方法就是声明这个矩阵的时候直接声明为列主序的矩阵:row_major。
还有一个方法就是在shader里面mul矩阵的时候用矩阵在前,向量在后的方法也可以:
float4 transformedPosition = somePosition * someMatrix;
float4 transformedPosition = someMatrix * somePosition;
另外还要注意const buffer的desc权限 是 cpu only write &gpu only read。