一种基于DirectX 9.0 API的G代码逆向渲染方法
来源:互联网 发布:linux 创建用户加入组 编辑:程序博客网 时间:2024/06/05 16:40
G代码是一种工业加工描述语言,现在也广泛的运用于3D打印中。但G代码是一种单向的描述语言,很难逆向回三维模型。但近日,日本出现了一款名为MakePaintable的软件,它可以将G代码逆向回三维模型。下图所示的就是这一软件的效果图:
图0-1 原始三维模型
图0-2 逆向出的三维模型
可以看到,逆向出的模型除了与原模型形状相同,还有光照与材质。但是国内还没有这样的软件。
另一方面,现在游戏编程中的图形渲染一般基于多边形网络(主要是三角形),绝大多数图形格式诸如STL等都是如此。G代码可以从这些三角形网络获得,但G代码却很难对游戏编程有什么帮助。
本文就提出了一种方法来逆向G代码,并且逆向出的模型包含完整形状、材质、光照、贴图等等。除了可以单纯的逆向G代码,成为一款G代码逆向器,还可以应用于游戏编程,成为一种新的图形描述办法。
该方法思路如下:
本文也就由此展开
1.G代码的解析
G代码本身只是一段文本,可以使用fstream流读入。读入之后必须将其编制成为符合要求的数据结构。
才能从中高效率的提取有关信息。G代码的语法结构与汇编语言颇有些相似,都是功能字+操作数。而一个标准的功能字又可以拆分为一个字母(比如G01的G)和一个数字(比如G01的01)。我们可以定义一个GSentence的类来表示一条完整的G指令。并用一个GProgram来表示所有G指令类GSentence的集合。在GSentence中定义一个名为GFuncition的简单数据结构来描述一条功能字:
struct GFuncition{ char Word; //字母 char Num; //数字 };
同时功能字后面的操作数理论上可以有无数个,所以我们必须用一个动态数组来存储它。
double *Arr; Arr=new double[arrSize];
其中arrSize由操作数的个数而定。
读入后首先删去注释、空格,并字母全部大写,之后通过正则表达式匹配,补齐续效指令和为0略去的参数,提取出功能字中的字母”G””M”及数字“01”“02”等。获取参数个数到arrSize并马上动态分配数字空间。下面的代码片段展示了使用正则表达式把X坐标获取到数组中:
repattren pat(“X(\d+)”) match_results::backref_type xaxis=pat.match(Arr[1],results);
这一切完成之后需要校错。对于三维模拟,无需考虑实际上机可能发生的操作规则错误。只需考虑代码互斥、参数缺失和语法结构错误。对于前两者,我们用一个三维数组来解决它:
GFunction GT[72][72][72];
G指令一共72条,该三维数组的第一维用来存放G指令表示,第二维用0来表示在任何情况下都不发生互斥,如果发生互斥,就把可能与其发生互斥的语句记录到这一维;第三维存放至少需要多少参数。测错时只要与这张表比对即可。部分书写错误甚至可以直接纠正。语法结构错误可以使用正则表达式匹配解决。
还有一个问题就是子程序。尽管ISO标准的G代码只支持顺序结构,但西门子、法兰克等数控研发企业已将G代码类C化,有了子程序功能。目前有两种办法处理在子程序:一种是用栈存储以供调用,另一种就是把子程序拼接回主程序。本文采用第二种。在需要执行子程序的地方读取到要调用的子程序的名称和次数、参数,直接将其替换掉。将G代码遍历完成后把子程序块删掉就可以了。这个实现不需要正则表达式,使用一段批处理脚本就可以实现,代码如下:
Set f=主程序 Set f2=需要替换的子程序 For /?>%f% If exist %f2% del %f2% For/f"tokens=*delims=% "%l in(%f%) do ( set line=%%l set line=!line:FOR=F_O_R! echo !line!>>%f2% )
把这段脚本保存为Replace.bat。需要注意的是这段脚本使用了命令扩展,在C++里调用时不能直接调用,必须带参数,如下所示
system("start cmd /v:on /c Replace.bat");
这样,一段不规则的G代码就被处理为一段规范化的G代码了。
2.G代码的编译
把G代码的数据提取完成之后,还需要编译成基于点的结构体以便下一步的绘制。
但一个代码段中必然存在模态指令,如何处理这些模态指令呢?这里我们引入了一种名为有限状态机的机制来用switch..case..模拟模态指令,一旦有模态指令触发马上跳转到对应的case进行坐标系转换、单位转换等等操作。遍历完成后跳出状态机。还需要注意的是D3D使用左手坐标系,所以还需要转换。经过这样一番处理后,所有参数变成了点的坐标,这样就可以开始使用D3D进行绘制了。
3.使用D3D绘制出线架
在绘制之前,必须考虑插补的问题。D3D有直接画直线的函数,所以G01,G02可以灵活顶点格式定义一个FVP轻松的使用下面的代码模拟出来:
struct CUSTOMVERTEX{ float x,y,z; } CUSTOMVERTEX Vertices[]= { {X坐标,Y坐标,Z坐标, 偏移量,颜色} }
只要把从G代码中获得的坐标信息编制成如上格式,使用DrawPrimitive方法将其渲染成线段条带(因为线段条带可以不共面)即可。
但D3D的基本图形并不包括圆弧,所以圆弧必须插补。本文使用一种简单的数据采样法进行插补,也可以使用D3D提供的三角扇形来插补,基本思路都是一样的。
一条弧线可以使用它的弦线来模拟,如图3-1.但误差从图上看来特别大。主要是因为∠α过大造成的。当∠α变小时,弧线和弦线之间将会变得非常近。圆弧插补就是这样模拟的。
4.为绘制出的图形附加纹理贴图
通过G代码生成后的物体是没有材质的,没有材质显然不能用于游戏编程,所以我们要为其附加材质。
对于一个已经绘制出线框的物体,我们需要为其附加纹理贴图。Direct3D支持多种格式的纹理贴图,比如bmp,jpg,png,tga等。同时对大小也没有绝对的限制,但为了渲染效率,我们一般使用边长为2的整数次幂的正方形图片。比如256X256、512X512等。
纹理贴图是由纹理元素组成的。纹理元素就是一个存储了颜色值的多维数组。为了将二维纹理映射到三维图形上,D3D使用了一个稍有别于(x,y)平面直角坐标系的(u,v)坐标系它的特点就是u轴向右为正方向,v轴向下为正方向。并且取值范围都是[0,1]。我们可以定义一个FVP灵活顶点格式,再使用第3步创建好的坐标数组Vertices[]的数据填充完纹理坐标后使用IDIRECT3D9TEXTURE9接口的D3DXCreateTextureFromFil-e方法创建纹理并SetTexture启用纹理即可。(D3D默认重复寻址,使用无缝衔接纹理就可以为其附加材质贴图,如图4-1)。
图4-1 使用重复寻址附加材质贴图的过程
5.为绘制出的图形附加光照与材质
材质与光照,这两样是紧密相连的。既然附加了材质纹理,那么光源也是必不可少的。好在DirectX提供了成熟的光源算法,可以轻松为物体添加光照。为了模拟太阳光,我们可以直接使用D3D的光源结构体D3DLIGHT9中的D3DLIGHT_DIRECTIONAL属性,即方向光源。并分别将漫反射、镜面反射和环境光颜色分别设定为0.5f,0.5f,0.5f,1.0f;1.0f,1.0f,1.0f,1.0f;0.3f,0.3f,0.3f,1.0f,衰减系数默认即可。另外,本文第4段只是附加了材质贴图,并没有附加材质,所以我们还需要使用D3DMATERIAL9结构体设置一下物体材质(物体颜色)。有一个权威公式:物体颜色总和=物体反射环境光+物体发射漫反射光+物体发射镜面发射光+自发光。所以可以使用这个公式来指定材质,就是更改结构体中五个颜色分量的值。如不赋予材质D3D将使用默认材质:反射所以漫反射光,不镜面反射,也不反射环境光,也不自发光如图5-1所示。
图5-1 平行光(方向光)照,默认材质
6.小结
本文通过解析G代码、 编译G代码、使用D3D绘制出线架、附加纹理贴图、 附加光照与材质这几个步骤将G代码使用Direct3D应用于游戏编程中。提出了把G代码当做一种模型描述方法的思想,并做了一个简单的实现。不过要将此方法真正应用于游戏开发中的话,还有可以完善的地方,比如说附加碰撞处理等更多属性。
(PS:这是偶以前写的一篇论文,所以风格中规中矩,大家将就着看一下吧。网上也有人用OpenGL实现了一个跟本文差不多的系统,不过没有提出把G代码用于游戏编程,所以本文比较新的一个观点就是把数控加工中的G代码用于游戏编程。。。写的不好,献丑了。。。)
<欢迎转载,但请附带以下标志:
原标题:一种基于DirectX 9.0 API的G代码逆向渲染方法
原作者:XFRACTION
2016/2/3 发表于CSDN>
- 一种基于DirectX 9.0 API的G代码逆向渲染方法
- 一种API代码结构的设计思路
- 一种简单实现卡通勾边渲染的方法
- 一种让程序支持多渲染器的方法
- 又一种Qt + OpenGL 的离屏渲染方法
- 对付API-splicing的一种简单方法
- 驱动接口API设计的一种方法
- 快速构建基于代码级性能测试方法的一种思路和简单实现
- 一种基于自定义代码记录用户访问日志在Sharepoint网站的应用方法!
- 一种基于Redis的10行代码实现IP频率控制方法
- 一种基于有序序列mapjoin的方法
- 一种基于流水线的视频文件校验方法
- 一种基于画布的绝对定位方法
- 一种基于中断的按键检测方法
- 一种基于BIOS的WINCE启动方法
- 渲染管线以及directx的发展过程
- 一个使用Directx渲染的Havok程序
- DirectX的OBJ模型加载与渲染
- 打印HttpServletRequest
- linux SVN命令
- linux下重启Planning
- Notepad++快捷键
- Table边框的总结
- 一种基于DirectX 9.0 API的G代码逆向渲染方法
- Tomcat远程调试
- HDU 5505 GT and numbers(GCD魔法)
- linux进程使用占比前十
- iOS开发-黑魔法 method swizzle(一)
- 问题No enclosing instance of type E is accessible的解决
- mfc中怎样在一个基于对话框程序中添加菜单栏
- Ping++ 第三方支付SDK 如果没安装支付宝客户端,不能弹出网页版支付宝的解决方案
- EPM常用新建系统用户语句