Gamebryo杂记- CG迷途大熊猫- 博客园


 

 

 10月10日

杂事两三件

使用左(右)手拇指指向坐标轴正向,握住坐标轴,4指环绕方向为正方向

不管是左手右手,行向量还是列向量。矩阵存储基本都是[row][col]形式

这时该如何建立矩阵呢? 
答案就是区分你用的是行向量还是列向量.

也许有人会问为什么不区分是左手坐标系还是右手坐标系呢?
因为矩阵M可以变换到矩阵M1, 那么他们一定是同在左手坐标系或右手坐标系,变换只能在可以互相转换的坐标系之间进行。 这也是为什么gamebryo摄像机朝向X.但最终dx 还是z值判断深度。这也是矩阵变换的结果

如果你用的是行向量:由于行向量只能左乘矩阵(注意乘与乘以的区别)

gamebryo 默认模型朝向-Y轴。摄像机看向X轴方向。上方向为Y. 当然变换后的矩阵在z向上世界坐标里面是看向Y轴。

gamebryo 矩阵和dx矩阵相乘方向相同 Rx * Ry * Rz 先执行Rx,然后 Ry,{zh1}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中这些子网格是如何附加到这个虚拟体上的。

 

关于这个话题,网上有n多个版本,今天,我也来说说这个话题。
(一)首先,无论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;

 

郑重声明:资讯 【Gamebryo杂记- CG迷途大熊猫- 博客园】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——