引擎设计跟踪(九) 3DS MAX 导出插件

1. SDK和工程配置的什么的就不多说了,安装完SDK并且把工程模板复制到VC文件夹里,就可以根据模板创建工程了.
2.Ogre的导出插件

为了学习Ogre的MAX导出,下了最新的Ogre, v1.81大概.
对Ogre的导出感到很好奇, 他好像没有直接导出二进制,而是导出了xml,据说有另外的命令行工具把xml转成二进制.
还有,Ogre的官方插件, 把所有三角形共享的顶点都重复了一次,也就是说,每个三角形都有自己唯一的顶点,按它文档说明,这是为了处理多维材质时,
一个顶点有不同uv和法线,材质等属性的问题, 但是我觉得只需要复制重叠的边缘顶点就可以了,而且
即便是重复每个顶点,我记得Ogre的Mesh是按不同的材质分成不同的SubMesh的, 但在代码里面没有看到按多维子材质分组.
还有,Max的多维子材质被导出成了multi-pass,即每个子材质是一个pass,这个我也很不理解,
因为一个pass是针对该材质的所有几何数据的, 但Max的子材质貌似是分到不同的面上去的,即一个子材质对应该材质的所有几何数据中的一部分(或者全部).
或许是我的理解有问题吧.

3.切空间的导出.

根据Max的文档, 切空间向量只有在使用平滑组(smoothing group)的时候才会被计算, 问了一个朋友, 以前的美术同事, 他说现在平滑组基本不怎么用了, 所以最好的方法还是自己算. 也许可以手动调用max接口,生成平滑组

DllExport void 	AutoSmooth (float angle, BOOL useSel, BOOL preventIndirectSmoothing=FALSE);

但我手生,不知道这样会不会对artist有影响,所以暂不考虑.

第一个参考:
http://www.terathon.com/code/tangent.html

里面有导出切空间并且处理镜像的方法.

关于切空间,谈谈个人的理解.一个面有一个法线,无数个切线,理论上,这个法线和任何两个垂直的切线都可以构成正交的"切空间", 然而, 一般说的切空间的两个切线,是沿着空间几何表面的纹理的U和V方向上的两条空间切线.顺便,binormal这个叫法,个人觉得叫做bitangent更合适, 因为法线是唯一的,切线不是.上面的链接中也提到了这一点,大概原因是在曲线相关的术语中,是tagent,binormal,normal,但这个用法被(误)用到了面的范畴(切空间).

第二个参考: 龚敏敏大大的文章: 压缩tangent frame
http://www.opengpu.org/forum.php?mod=viewthread&tid=10467
思考:
为什么用了quaternion的情况下,三个向量都有了,为什么还要保存镜像符号, 而不是直接把bitangent取反?
答:bitangent取反以后得到了三个向量需要的最终向量,但是非正交了,不能用quaternion保存了.
更正: bitangent取反以后仍然是三个正交基, 但还不确定这种方法为什么不行,也许真的可以, 准备试一下.
以前镜像保存在tangent.w里面是因为只用了两个向量,如果把tangent取反,虽然cross以后得到的bitangent对了,但是tangent已经反了...而用了quaternion以后,理论上可以直接取反一个向量,所以应该不需要保存镜像符号.


第三个参考: 作者是提问的,但也提到了他的步骤,还有相关的问题.
http://www.gamedev.net/topic/347799-mirrored-uvs-and-tangent-space-solved/


其中一个问题就是镜像的UV,在两边镜像的衔接出(uv-mirror center line), 共享的顶点有两个tangent space, 一个是非镜像部份,一个镜像部份.
一般的导出,没有额外处理的,渲染会有缝的,见下图.(顺便搜到了Maya里面有专门处理这个问题的导出选项)
切空间-UV镜像

上图中,右边是镜像的UV导出的切空间,可以看到中间的一条缝,左边的只是加了detail normal盖住了缝.
图片出处:
http://wiki.polycount.com/NormalMap/#UV_Coordinates

这个问题只是原帖中,下面回答的人提醒的,但作者已经处理了,处理方法值得借鉴:
(前面的一些操作blah blah跟这个问题无关,我就不说了)
先根据镜像与否,把顶点/三角形分成两个组, 镜像中心顶点被重复放在两个组里面,然后再分组处理.

这个问题其实也是相同顶点的渲染属性不同,需要复制顶点的问题.

顺便贴一下作者的步骤,没时间翻译了

-First, I pass an array of positions, UV's, and indicies into my "mesh mender".
-I do some pre-processing to generate triangle connectivity and such
-I generate a per-triangle normal and tangent/bitangent, using the methods below:

(code fragment) //作者的代码被我省略了, 原链接(参考三)内有.

-The triangles are divided into smoothing groups based on a normal threshold

-Verticies that lie on a smoothing group edge are duplicated
-Per-vertex normals are calculated by summing up the normals of each triangle that shares that vertex and belongs to the same smoothing group, then normalizing
-Triangles are again divided into smoothing groups, but this time there are only two and they are determined by the handedness of the tangent and bitangent.
-Verticies are again duplicated based on smoothing group. This process is not affected by splits made in the normal smoothing process.
-Per-Vertex tangents/bitangents are calculated in the same fasion as the normals.
-The tangents are orthogonalized with the normal
-The new vertex vectors are mapped onto the original array (adding any new verts that were created) and indicies are adjusted accordingly.


更新:

使用quaternion时, 不能取反bitangent. 本来一个正交空间的三个基构造的quaternion可以被解出来.
但是取反一个轴以后, 手系改变的情况下不能使用quaternion保存了, 解出的三个轴向量也不对. 笔者数学不是特别好, 不知道这个是quaternion的什么性质.

比如如下代码:

 srand((unsigned) time(NULL));

        Vector3 x( rand(), rand(), rand() );
        x.normalize();

        Vector3 y( rand(), rand(), rand() );
        Vector3 z = x.crossProduct(y);
        z.normalize();

        y = z.crossProduct(x);
        y.normalize();

        scalar t = x.dotProduct(y);
        assert( t < 1e-6 );
        t = x.dotProduct(z);
        assert( t < 1e-6 );

        Matrix33 m(x, y, z);  //Matrix33 m(x, -y, z);
        Quaternion q(m);
        q.normalize();

        Vector3 x2 = q*Vector3::UNIT_X;
        Vector3 y2 = q*Vector3::UNIT_Y;
        Vector3 z2 = q*Vector3::UNIT_Z;

        Vector3 x3 = Vector3::UNIT_X*m;
        Vector3 y3 = Vector3::UNIT_Y*m;
        Vector3 z3 = Vector3::UNIT_Z*m;

用矩阵解出的x3 == x, y3== y, z3==z

用四元数解出来的x2== x, y2== y, z2 ==y.
但是如果取反y轴: Matrix33 m(x, -y, z) 之后, 

矩阵解出来仍然正确, 但是四元数的已经不一样了.
所以只能把镜像符号保存到w分量中.

另外再记录两个链接方便以后查资料.

//crytek网站上tangent space的计算, 带有代码和pdf

http://crytek.com/cryengine/presentations/triangle-mesh-tangent-space-calculation

//nvidia网站上meshmender(原工具找不到了)的pdf.

https://developer.nvidia.com/sites/default/files/akamai/gamedev/docs/texture_space_on_real_models.pdf

原文地址:https://www.cnblogs.com/crazii/p/2997601.html