RLA 文件中的法线信息提取
最近想在游戏中加一点简单的环境光,因为游戏中使用的都是 2d 图片,那么最廉价的方法应该是给图片加上法线图了。
好在我们游戏的原始图素都是用 3d 建模,然后再用平面修整的。基本几何结构信息可以从模型提取出来。当然,我们并不真的需要自己写程序去从模型中计算出最终渲染图的法线信息。所有渲染软件都可以输出它。
比如 3ds max ,如果你把渲染结果输出成 .rla 文件,那么就可以勾选 normal zbuffer 等额外的通道输出。
记得我读大学时写过一个 rla 文件解析程序,当时是为了提取里面的 Z 通道。这都过了十多年了,果然完全找不回当年写的代码了,也忘记曾经怎么实现的,所以就从头来搞。
动手之前先查阅了 imagemagick 的文档,发现它虽然能提取 rla 文件,但仅限于不同的颜色及 alpha 通道。核对了源代码 确认了这一点。google 也没有找到有人开源过相关库,那么只好自己写了。
RLA 是 wavefront 的 3d 图片文件格式。网上能找到格式说明 。
因为有固定长度的 C 结构头,解析出文件头还是很容易的。可惜不知道为什么,3ds max 导出的 rla 文件头中,即使导出了多个附加通道,NumOfAuxChannels 项还是 0 。
按 offset table 提取出 scanlines 后,试着解了一下 RGB 以及 alpha 通道都是正确的。暂时不理会文件头,每个 scanline 后都附有其它 channel (如果导出了的话)。暂时不知道如何识别每个通道对应着什么,不过如果你只导出 normal 通道的话,那么多出来的通道当然就是法线图了。
btw, 每个 scanline 节的最后,都有一些莫名其妙的数据,也不知道如何识别单个 scanline 结束标记。不过这些不影响我们做下面的工作。
做一个简单的测试会发现,如果在 max 里导出 Z 通道,那么在 rla 文件里会多 4 个通道出来;如果导出 normal 通道,会再多出 4 个通道。
按 rla 的格式说明,4 个通道的值可以合成一个 float 也可以合成一个 32bit 整数。 Z 通道看起来是一个 float ,而 normal 通道则比较像整数。(从导出图像上看,不像是 4 张独立的 8bit 通道图)
用 google 没有找到太多信息,搜到一些相关资料是来源于 3ds max 的 sdk 头文件。里面提到一个叫 DeCompressNormal 的函数。输入一个 uint32 ,输出三个 float 。可惜没有实现的源代码。
我反汇编了包含 DeCompressNormal 实现的动态库 geom.dll :
.text:67678890 ; class Point3 __cdecl DeCompressNormal(unsigned long) .text:67678890 public ?DeCompressNormal@@YA?AVPoint3@@K@Z .text:67678890 ?DeCompressNormal@@YA?AVPoint3@@K@Z proc near .text:67678890 .text:67678890 var_C = dword ptr -0Ch .text:67678890 arg_0 = dword ptr 4 .text:67678890 arg_4 = dword ptr 8 .text:67678890 .text:67678890 sub esp, 14h .text:67678893 mov eax, [esp+14h+arg_0] .text:67678897 mov edx, [esp+14h+arg_4] .text:6767889B fld ds:flt_676877EC ; -1.0 .text:676788A1 mov ecx, edx .text:676788A3 and ecx, 3FFh .text:676788A9 fld ds:flt_676877E8 ; 1.9569471e-3 .text:676788AF mov [esp+14h+var_C], ecx .text:676788B3 fild [esp+14h+var_C] .text:676788B7 fmul st, st(1) .text:676788B9 fadd st, st(2) .text:676788BB mov ecx, edx .text:676788BD fstp dword ptr [eax+8] .text:676788C0 shr ecx, 0Ah .text:676788C3 and ecx, 3FFh .text:676788C9 mov [esp+14h+var_C], ecx .text:676788CD fild [esp+14h+var_C] .text:676788D1 fmul st, st(1) .text:676788D3 fadd st, st(2) .text:676788D5 fstp dword ptr [eax+4] .text:676788D8 shr edx, 14h .text:676788DB and edx, 3FFh .text:676788E1 mov [esp+14h+var_C], edx .text:676788E5 fild [esp+14h+var_C] .text:676788E9 fmulp st(1), st .text:676788EB faddp st(1), st .text:676788ED fstp dword ptr [eax] .text:676788EF add esp, 14h .text:676788F2 retn .text:676788F2 ?DeCompressNormal@@YA?AVPoint3@@K@Z endp
稍有汇编基础就可以很容易看出,这个函数其实就是把一个 32bit 整数按 10bit 一段分成三段,每段是一个 0 到 1023 的整数,然后把这三个整数调整为 -1 到 1 之间的浮点数。
ps. 这里的常量 1.9569471e-3 是 1/511 。
最终,我把美术给我的 rla 文件。提取出一张色彩图:
和一张法线图:
顺手用 ejoy2d 写了一个简单的例子(定义一个 shader 传入鼠标位置做为光源方向,算出和法线图上的法向量的夹角作为光强,叠加到最终的屏幕上)。效果还不错。
Comments
Posted by: Cloud | (14) November 13, 2014 04:27 PM
Posted by: jichong | (13) November 13, 2014 09:28 AM
Posted by: 42 | (12) November 13, 2014 01:02 AM
Posted by: wwww | (11) November 12, 2014 11:01 AM
Posted by: Cloud | (10) November 11, 2014 06:35 PM
Posted by: wwww | (9) November 11, 2014 05:41 PM
Posted by: jichong | (8) November 7, 2014 10:58 AM
Posted by: kuma | (7) November 7, 2014 02:41 AM
Posted by: davidxu | (6) November 6, 2014 06:21 PM
Posted by: IM鑫爷 | (5) November 6, 2014 05:50 PM
Posted by: dwing | (4) November 5, 2014 03:11 PM
Posted by: 青春华航 | (3) November 5, 2014 11:32 AM
Posted by: john | (2) November 4, 2014 10:09 PM
Posted by: happy牛野 | (1) November 4, 2014 05:24 PM