http://topameng.spaces.live.com/blog/cns!F962D4854A8233D!495.entry
10月10日
杂事两三件
使用左(右)手拇指指向坐标轴正向,握住坐标轴,4指环绕方向为正方向
不管是左手右手,行向量还是列向量。矩阵存储基本都是[row][col]形式
这时该如何建立矩阵呢?
答案就是区分你用的是行向量还是列向量.
也许有人会问为什么不区分是左手坐标系还是右手坐标系呢?
因为矩阵M可以变换到矩阵M1, 那么他们一定是同在左手坐标系或右手坐标系,变换只能在可以互相转换的坐标系之间进行。 这也是为什么gamebryo摄像机朝向X.但最终dx 还是z值判断深度。这也是矩阵变换的结果
如果你用的是行向量:由于行向量只能左乘矩阵(注意乘与乘以的区别)
gamebryo 默认模型朝向-Y轴。摄像机看向X轴方向。上方向为Y. 当然变换后的矩阵在z向上世界坐标里面是看向Y轴。
gamebryo 矩阵和dx矩阵相乘方向相同 Rx * Ry * Rz 先执行Rx,然后 Ry,最后Rz。
在全屏调试下程序crash原因非常麻烦。设置断点也会导致全屏情况不能切换回桌面。这个有个简单办法就是在断点前加入一个MessageBox操作。通过弹出对话框,从全屏状态恢复到窗口状态。后面就是你的断点了。cool~
对于NiNode代表的场景图。要节省效率可以做2个场景图。一个放入静态对象。这些对象不需要Update更新。另一个放入动画对象。这个场景图需要一直更新。
同理对于实体-场景架构也可以用这个办法,但不包括大范围运动物体,如玩家和大范围寻路npc之类。这样会拉大其所在结点的包围体大小
注意GameBryo 使用的场景图是一种包围体层次结构BHV,这种结构退化的表现就是八叉树。但注意不同于八叉树。可以参考3D游戏编程大师技巧上面的说明。这本书上说包围体层次结构建立可以使用分而治之或者集群技术算法。但GameBryo 并没有使用算法来确定树结构,所以通过程序Node attach 结点方法建立起来的包围体层次结构效率会非常差。而在3dmax里面是通过美术人员手动 group 一下相关网格来组成一个节点层次结构。如果遇到经验不足的,直接所有mesh挂在根结点下。效率也会非常差。不过还好3dmax很多很多操作会自动产生节点,所以通过group相关的(主要是空间相关、效果相关,可参考mout)节点在一起可以提高效能。
softparticles 写深度到另一个rendertarget
MeshCreation 使用了floodgate. 波浪由 floodgate 产生
StandardMaterial 展示了标准的shader
material 只是引用了 shader 。
有很多使用光源方法。投射光源等等。
scene graph bounding Spheres是用摄影机进行裁切。地表系统用了quadtree。
NiMesh 比 NiGeomentry 有什么不同
因为floodgate 等的使用,要提供一个更有弹性的系统。 NiMesh 取代之。更多东西可以在Mesh上分享。还有如GPU Instancing等功能。以前有三角形 ,线条等。现在统一为NiMesh
gamebryo2.5 支持1024*1024地形大小。由于gb对应max是1:1的,这样相当于1平方公里. lightspeed 版本好像没有限制了。这个也可以不是1平方公里。就像魔兽世界一个地形网格代表的是3.333的样子。也不是1.就是说max人物建筑之类的可以缩小到1/3放入地形场景。这样地形就大了。如果要求不太高还可以缩小。
地形支持4层材质(不是纹理。材质可以是很多纹理的混合。bump map. light map. 等等一个材质信息要丰富很多。如魔兽4个纹理的地形,可能就相当于一个材质如MOUT中的地形材质)。
RenderStep 相当于一个effect. 如:hdr,dof 等等,游戏场景也是一个effect.相当于 dx 的 d3deffect. 负责 rendertargetgroup
renderclick 是一个单独的渲染pass.可以设置摄像机和 culler. 一个renderstep的多个renderclick,操作的是相同的rendertargetgroup
renderview 为 renderclick 提供一些列需要渲染的几何体数据
对于 .fx 之外的 shader 一般需要一个 .nsf 描述文件,来描述shader文件中的外部变量。在程序中动态更新某个shader内变量如下:
NiShaderFactory::UpdateGlobalShaderConstant("LightDiffuse",sizeof(afLightDiffuse), &afLightDiffuse);
如果没有用nsf描述外部变量,可以通过NiShaderFactory::RegisterGlobalShaderConstant()函数来注册 shader 脚本中的外部变量
注意 NiAplhaAccumulate 对物体是从后向前排序的,这样适合绘制alpha物体,但对于无alpha物体应该从前到后排序,这样减少像素值重复覆盖次数。就像bsp树的做法。 orion 例子里面的 Accumulate 就是这种做法
NiSortAdjustNode 可以让引擎按节点下面网格顺序来draw alpha 物体而不进行排序操作,但还不清楚在3dmax中这些子网格是如何附加到这个虚拟体上的。
http://www.cnblogs.com/cgwolver/archive/2009/07/29/1533570.html
行向量,列向量,行主序矩阵,列主序矩阵(row vector,column vector,row major-matrix,column-major matrix)
(一)首先,无论dx还是opengl,所表示的矢量和矩阵都是依据线性代数中的标准定义的:
“矩阵A与B的乘积矩阵C的第i行第j列的元素c(ij)等于A的第i行于B的第j列的对应元素乘积的和。”(实用数学手册,科学出版社,第二版)
例如c12 = a11*b11+a12*b21+a12*b13...
(二)在明确了这一点后,然后我们再看“矩阵的存储方式”,矩阵存储方式有两种,一种是“行主序(row-major order)/行优先”,另一种就是“列主序(column-major order)/列优先”
1)Direct3D 采用行主序存储
“Effect matrix parameters and HLSL matrix variables can define whether the value is a row-major or column-major matrix; however, the DirectX APIs always treat D3DMATRIX and D3DXMATRIX as row-major.”(见d3d9 document/Casting and Conversion 一节)
2)OpenGL 采用列主序存储
“The m parameter points to a 4x4 matrix of single- or double-precision floating-point values stored in column-major order. That is, the matrix is stored as follows”
(见msdn glLoadMatrixf API说明)
存储顺序说明了线性代数中的矩阵如何在线性的内存数组中存储,d3d 将每一行在数组中按行存储,而opengl将每一列存储到数组的每一行中:
线性代数意义的同一个矩阵,在d3d 和 ogl 中却有不同的存储顺序
线代:a11,a12,a13,a14 d3d : a11,a12,a13,a14 gl: 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
(三)矩阵乘法顺序和规则
矩阵乘法在线性代数中的定义是确定的,然而在不同的实现中出现了“左乘”和“右乘”的区别,或者叫做“前乘(pre-multiply),后乘(post-multiply)”
这个规则取决于vector的表示形式,即行向量还是列向量。如果是行向量,其实就是一个行矩阵。那么表示线性代数意义的“行x列”,就是前乘。矩阵乘法也是如此。
如d3d中,
D3D 是行向量,行优先存储,OpenGL是列向量,列优先存储。同一个矩阵用D3D存储还是用opengl存储虽然不同,但是变换的结果却是相同,
因为opengl 变换向量是把向量视作列向量,并同矩阵的每一列相乘,用来实现线性代数中同一个变换。
我们通常很难看到opengl变换坐标的代码,以下代码出自opengl source code,让我们一窥顶点变换的“庐山真面目”
void FASTCALL __glXForm3(__GLcoord *res, const __GLfloat v[3], const __GLmatrix *m)
{
__GLfloat x = v[0];
__GLfloat y = v[1];
__GLfloat z = v[2];
res->x = x*m->matrix[0][0] + y*m->matrix[1][0] + z*m->matrix[2][0]
+ m->matrix[3][0];
res->y = x*m->matrix[0][1] + y*m->matrix[1][1] + z*m->matrix[2][1]
+ m->matrix[3][1];
res->z = x*m->matrix[0][2] + y*m->matrix[1][2] + z*m->matrix[2][2]
+ m->matrix[3][2];
res->w = x*m->matrix[0][3] + y*m->matrix[1][3] + z*m->matrix[2][3]
+ m->matrix[3][3];
}
可见确实如上所述,“OPENGL列向量和矩阵的每一列相乘,仍然表示线性代数行向量和矩阵的每一行相乘”
再来看一下opengl 矩阵相乘,“用a的每一列去乘b的每一行”。
/*
** Compute r = a * b, where r can equal b.
*/
void FASTCALL __glMultMatrix(__GLmatrix *r, const __GLmatrix *a, const __GLmatrix *b)
{
__GLfloat b00, b01, b02, b03;
__GLfloat b10, b11, b12, b13;
__GLfloat b20, b21, b22, b23;
__GLfloat b30, b31, b32, b33;
GLint i;
b00 = b->matrix[0][0]; b01 = b->matrix[0][1];
b02 = b->matrix[0][2]; b03 = b->matrix[0][3];
b10 = b->matrix[1][0]; b11 = b->matrix[1][1];
b12 = b->matrix[1][2]; b13 = b->matrix[1][3];
b20 = b->matrix[2][0]; b21 = b->matrix[2][1];
b22 = b->matrix[2][2]; b23 = b->matrix[2][3];
b30 = b->matrix[3][0]; b31 = b->matrix[3][1];
b32 = b->matrix[3][2]; b33 = b->matrix[3][3];
for (i = 0; i < 4; i++) {
r->matrix[i][0] = a->matrix[i][0]*b00 + a->matrix[i][1]*b10
+ a->matrix[i][2]*b20 + a->matrix[i][3]*b30;
r->matrix[i][1] = a->matrix[i][0]*b01 + a->matrix[i][1]*b11
+ a->matrix[i][2]*b21 + a->matrix[i][3]*b31;
r->matrix[i][2] = a->matrix[i][0]*b02 + a->matrix[i][1]*b12
+ a->matrix[i][2]*b22 + a->matrix[i][3]*b32;
r->matrix[i][3] = a->matrix[i][0]*b03 + a->matrix[i][1]*b13
+ a->matrix[i][2]*b23 + a->matrix[i][3]*b33;
http://kb.cnblogs.com/a/1115571/
思考:矩阵及变换,以及矩阵在DirectX和OpenGL中的运用问题:左乘/右乘,行优先/列优先,...
思考:矩阵及变换,以及矩阵在DirectX和OpenGL中的运用
1。矩阵和线性变换:一一对应
矩阵是用来表示线性变换的一种工具,它和线性变换之间是一一对应的。
考虑线性变换:
a11*x1 + a12*x2 + ...+a1n*xn = x1'
a21*x1 + a22*x2 + ...+a2n*xn = x2'
...
am1*x1 + am2*x2 + ...+amn*xn = xm'
对应地,用矩阵来表示就是:
|a11 a12 ... a1n | |x1| |x1'|
|a21 a22 ... a2n | |x2| |x2'|
|... |* |...|= |... |
|am1 am2 ... amn | |xn| |xm'|
也可以如下来表示:
|a11 a21 ... am1|
|a12 a22 ... am2|
|x1 x2...xn|*|... |= |x1' x2'... xm'|
|a1n a2n ... amn|
其中涉及到6个矩阵。分别为A[m*n],X[n*1],X'[m*1]以及X[1*n],A[n*m],X'[1*m]。
可以理解成向量x(x1,x2,...,xn)经过一个变换矩阵A[m*n]或A[n*m]后变成另外一个向量x'(x1',x2',...,xm'))。
2。矩阵的表示法:行矩阵 vs. 列矩阵
行矩阵和列矩阵的叫法是衍生自行向量和列向量。
其实,矩阵A[m*n]可以看成是m个n维的row vector构成的row matrix,也可看成是n个m维的column vector构成的column matrix。
其中,X[n*1]/X'[m*1]就等价于1个n/m维的column vector。X[1*n]/X'[1*m]就等价于1个n/m维的row vector。
Row matrix和Column matrix只是两种不同的表示法,前者表示把一个向量映射到矩阵的一行,后者表示把一个向量映射到矩阵的一列。
本质上体现的是同一线性变换。矩阵运算规定了它们可以通过转置运算来改变这个映射关系。
3。矩阵的相乘顺序:前乘或左乘 vs. 后乘或右乘
需要注意的是两种不同的表示法对应不同的运算顺序:
如果对一个column vector做变换,则变换矩阵(row matrix/vectors)必须出现在乘号的左边,即pre-multiply,又叫前乘或左乘。
如果对一个row vector做变换,则变换矩阵(column matrix/vectors)必须出现在乘号的右边,即post-multiply,又叫后乘或右乘。
一般不会弄错,因为矩阵乘法性质决定了相同的内维数的矩阵才能相乘。至于为什么是这个规律,为什么要row vector乘以column vector或column vector乘以row vector???想想吧。。。
所以左乘还是右乘,跟被变换的vector的表示形式相关,而非存储顺序决定。
4。矩阵的存储顺序:按行优先存储 vs. 按列优先存储
涉及到在计算机中使用矩阵时,首先会碰到存储矩阵的问题。
因为计算机存储空间是先后有序的,如何存储A[m*n]的m*n个元素是个问题,一般有两种:按行优先存储和按列优先存储。
row-major:存成a11,a12,...,amn的顺序。
column-major:存成a11,a21,...,amn的顺序。
这样问题就来了,给你一个存储好的矩阵元素集合,你不知道如何读取元素组成一个矩阵,比如你不知道a12该放在几行几列上。
所以,每个系统都有自己的规定,比如以什么规则存储的就以什么规则读取。DX使用Row-major,OGL使用Column-major.即一个相同的矩阵A[m*n]在DX和OGL中的存储序列是不一样的,这带来了系统间转换的麻烦。
不过,一个巧合的事情是:DX中,点/向量是用Row Vector来表示的,所以对应的变换矩阵是Column Matrix/Vectors,而OGL中,点/向量是用Column Vector来表示的,所以对应的变换矩阵是Row Matrix/Vectors.所以,如果在DX中对一个向量x(x1,x2,x3,1)或点(x(x1,x2,x3,1))应用A[4*4]的矩阵变换,就是x' = x(x1,x2,x3,1) * A[4*4],由于采用Row-major,所以它的存储序列是a11,a12,...,a43,a44。在OGL中,做同样的向量或点的变换,因为其使用Row Matrix/Vectors,其应用的变换矩阵应该是A'[4*4] = A[4*4]( ' 表示Transpose/转置),就是x' = A'[4*4] * x'(x1,x2,x3,1),但是由于采用Column-major,它的存储序列正好也是a11,a12,...,a43,a44!!!
所以实际上,对DX和OGL来讲,同一个变换,存储的矩阵元素序列是一样的.比如:都是第13,14,15个元素存储了平移变化量deltaZ,deltaY,deltaZ.
Refs:
http://mathworld.wolfram.com/Matrix.html
http://www.gamedev.net/community/forums/topic.asp?topic_id=321862