Pillow简介

时间:2022-05-02 08:36:04

摘要:本文介绍了轻量级多边形建模工具Pillow的编写过程和大体结构。

关键词:计算机图形;多边形建模;细分算法

中图分类号:TP302 文献标识码:A文章编号:1009-3044(2007)03-10835-02

1 引言

Pillow是一个我编写的轻量级多边形3D建模程序。目前,这个程序还处于初级阶段,只实现了一些基本的多边形建模操作,比如对点、边、面、体的挪动旋转以及缩放操作、删除、焊接、切割等等。另外还有catmull-clark细分。出于对速度的考虑,目前人为地限定在最大5次细分,实际上细分的次数可以不限。因为这个程序还在起步阶段,仍存在很多需要解决的问题,但是不管怎么说是个不错的开始。据我所知,许多小规模的建模工具都要开发很多年,而我才搞了两个多月。

2 最初的设想

大概是在2004年10月,我开始有自己编一个建模工具的想法。当时是要学DirectX,我觉得学编程必须要动手才行,所以想到写个像silo的程序。因为我觉得silo是一个面向使用的程序,而且是所有3D建模工具里规模比较小的。今年7月大学毕业后,我开始写这个程序。

因为最初是为了练习DirectX,所以最早是想用c#和managed directx来写。当时还用过一个truevision3d的引擎。原因很明显,引擎里屏幕拾取等操作已经都给你做好了。但是因为引擎的数据结构特别大,效率很低,所以后来我就直接用directx来编了。而我现在的这个程序则是重新用c++和opengl写的。最开始有人问我为什么不选择opengl,我的回答是每一个图形库都很有学问,一个尚且学不好,怎么能顾得上学另一个。不过自从接触过opengl之后,我觉得opengl没有directx封装的利害,感觉用起来很直接。我很喜欢opengl,用c++的目的首先是因为要用opengl。当然,tao framework我也试过,不理想。第二个原因是因为效率,交互性强的程序太需要效率了。第三个原因是c++应该是编图形程序的首选语言。至于程序的界面,我选择了wxWidgets。我编程的时候一般都非常在意界面。我认为外观设计不是什么无足轻重的东西,如果苹果公司没有它的工业设计,这样一个不兼容的怪胎早就被淘汰了。不过很遗憾,我找不到满足我要求的c++界面库。我希望能够有一个跨平台,最好开源,可以换肤,控件比较丰富的界面库,但是没有。编这个程序的时候我先后用了cegui,wxWidgets,qt4,但哪个都不满意。最后选择wxWidgets是因为silo也用它。

3 关于程序结构

我的程序大体分成了三个层次,界面层负责和使用者交互,并且把操作的结果显示出来。中间的控制层负责管理整个场景,负责将图形的操作转换成对于数据的操作。这部分实际上是整个程序的核心。最里面的是数据层,存储的是场景的图形数据。场景管理器和数据层之间的交互被严格定义为一组命令,并且每一个命令都会被一个叫做历史管理器的东西记录下来,便于随时将操作进行回退。在图中,蓝颜色的箭头表示的是命令的走向,紫颜色表示的是记录命令,红颜色是显示数据的走向。

目前我的程序还不具备undo和redo的功能,但实际上我在程序中已经实现了。但是和界面结合的时候我没有把它添上,因为这部分程序写得有点乱。之前对于如何实现程序的核心部分,我考虑了很长时间,但如何与界面结合,我考虑的不多。另外一个原因是我没有找到一个得心应手的界面包,我尝试了很多的界面方案,几乎每一种界面都有自己的一套编程风格。由于历史记录是比较容易出问题的部分,尤其是容易内存泄露,我因此暂时没有把它包含进来。

当初要实现undo和redo的时候我想到要用命令的方式,每个命令都有个逆命令,操作的时候记录每条命令,需要回退的时候再调用相应的逆命令。但是很多操作,比如说求平均,是没有逆命令的。把求平均之前的情况都记录下来,显然也是个比较笨的办法。后来我想到把操作分层,因为不管一个图形操作的过程有多么复杂,归根结底都是要改变数据,改变数据的方式无非就是添加、删除和改动这几种,所以我只要记录这些简单的操作,就可以应对复杂的图形操作了。实际上,在我的程序中定义的简单操作有几十种,并不只是添加、删除和修改这几种。

图3

什么是历史管理器呢,实际上是一个队列和栈的结构,命令被压入一个叫undo的队列头部,如果超过了队列的长度,则从队列的尾部直接抛弃。如果用户想要回退一步操作,就直接弹出在队列头部的一个命令进行回退,同时把这个命令翻译成逆命令放到redo中去。这个redo是一个栈的结构,如果要重做这步操作就弹出redo栈,执行,然后把命令翻译成逆命令再次放到undo队列中。

如何防止内存泄露,我的设计是这样的:把所有在堆中的数据的指针进行统一管理,这个结构我叫做data pool,实际上是个动态数组。任何其它的操作只能够引用这些数据,引用是通过数组的下标,而不是指针。释放指针由这个data pool统一进行。有两种方式,一种是直接释放,另一种是当需要记录删除操作的时候,把这个指针交给历史管理器,然后当历史管理器也不需要这个指针的时候,再释放掉它的空间。这样就可以防止指针已经丢失但空间仍没有释放的情况,因为任何时候被分配的空间都至少在data pool中保留了一个指向它的指针。然后通过data pool释放这个指针,就可以释放掉这个空间。

细分算法是多边形建模工具的关键。商业的多边形建模工具都具备很好的交互性,用户在模型细分的情况下进行编辑仍然可以立刻得到效果,这就要求细分算法的速度比较快。从理论上讲,细分算法由增加细节和求取平均两步来完成。但是在实际的操作中,严格按照这两步来操作并不明智,因为这样会增加遍历整个数据的次数。因为你可能需要分别遍历所有的点,边和面的数据。在我的程序中,我想办法只遍历一次所有的面,在遍历过程中计算好所有的数据。另外一点就是要尽量减少重复计算,比如:现在要遍历所有的面,对于每个面,都将它的邻接边一分为二,这样对于两个面的公共边,有可能出现重复计算的情况。我曾经看到上的专家建议用哈希表来避免重复,但我认为这个方法也不太好。在我的数据结构中,每一个图形元素都有一个细分编号。每一次细分开始的时候,程序都生成一个新的细分的编号,遍历的过程中,不断地比较这个编号和图形元素拥有的编号是否一致,如果不一致则认为是第一次运算进行处理,然后将这个图形元素的编号更新成新生成的细分编号。另外,很多时候使用者习惯先进行细分,然后再调整控制点来改变造型。这就需要增加交互性。我采用的方法就是局部的细分算法。当使用者选定一些控制点准备操作的时候,我的程序要先判断在当前的细分层次下的操作范围并且缓存,然后当操作发生的时候,只对缓存的面进行更新。

4 结束语

最初我把我编这个程序的想法告诉别人时,有人问我,这个程序到底有什么创新的地方,既然天下多边形建模工具已经那么多了。应该说目前还没有,但是以后会有的。现在这个程序虽然很普通,但是这是今后创新的一个平台,未来有可能在这个基础上做一些真正自己的东西。通过编这个程序我学到了不少东西,也多了不少认识。我感觉c++是一个陷阱,确切地说指针是陷阱,用的时候觉得很方便,肆无忌惮,但是当程序大到一定程度,就会突然发现它已经是千疮百孔了,处处都有可能造成泄露。但是一个图形的程序又怎能不用指针呢。我也想过把它都换成smart pointer,但是不知道效率上会打多大的折扣。

5 操作方法简介

下面介绍一下这个建模程序Pillow的基本操作方法。模型做的比较粗糙,上面这幅作品是完成之后经3D Max简单渲染的效果:

Pillow是一个模仿silo的程序,因而在操作方法上与其高度一致。silo把建模的操作合理分配到左右手,我觉得能很好地提高工作的效率。简单的来说是这样的:

alt健加鼠标左键拖动用来旋转视角

alt键加鼠标中建拖动用来平移视角

alt键加鼠标滚轮用来缩放视角

鼠标左键拖动用来单面选择

鼠标中键拖动用来双面选择

c为增加细分

v为减少细分

ctrl为沿视平面进行平移,前提是选择了物体

shift为增加选择内容

z为突出面或者边

w为移动模式

e为旋转模式

r为缩放模式

1-7为切换不同的视角

delete为移除点或边,其中点只能是有两个邻接边的点,这个操作目前有bug。

所有的操作都集中在菜单里面,而编辑窗口有几个我用OpenGL做的工具栏,这里面都是最常用的一些操作。把界面设计成这样是因为我曾经用过的一个silo的界面,记得当时我在cgfinal发过。silo可以自定义部分界面以及快捷键,但是我这个还不行。左侧的按钮选择编辑物体的编辑层次。右面的是选择窗口的层次。上面一条是文件操作,建立新物体和编辑新物体,还有截图快捷键和帮助。下面是选择显示的形式和选择照相机的形式。

建模的过程:

我做模型的时候比较喜欢box modelling的方式,总是从一个盒子的形状开始,这样比较容易从大局上把握形状,但是也容易让人忽视掉细节。我做模型的时候比较喜欢用参考图。关于 参考图,我的经验是从电影电视剧或者mv中去找,因为这样比较容易找到不同角度的参考,而如果直接去找照片的话,正面照好找,侧面照就比较少了。有了参考 图之后,要对正面和侧面的图进行一定的缩放和旋转,使得它们的比例一致。在这个过程中我比较喜欢用photoshop的标尺,在photoshop中让两 个参考图的下巴和头顶处于水平,鼻尖和眼睛的高度一样,还有嘴角和耳根也要一样。我的这个侧面参考图不完整,所以我用鼠标补了一些脑袋。对齐之后剪裁成两个高度一致的图。

然后开始建模。首先就是要在Pillow中指定参考图(option->set viewport image),silo中可以直接用鼠标来调节参考图位置,但是我这个还不行,需要手动输入位置。然后新建一个box(create-> cube)。然后对这个方盒子分别在前视图和侧视图中进行调节,使得在前视图近似为头的一半大小,在侧视图中刚好覆盖头部。调整好之后在透视图中删除中间 的面,也就是说我们要镜像复制一个当前的模型,因为脸是对称的。点击菜单上的(create->mirror instance)复制出镜像,对称平面为默认。

然后按图进行切割,这个切割线分别对应了头的眼睛位置和耳朵的位置。目前Pillow只支持在一个边的中间进行切割,其实核心的程序允许在任意位置切割,但 是和界面整合的时候我偷了点懒。然后将显示模式切换到线框模式,分别在前视图和侧视图中调节点的位置,使得这个造型和参考图近似。

可以随时对模型细分进行观察,目前Pillow在编辑模型的时候还不能实时地更新法线,主要是我还没有想好有什么快的局部更新法线的算法。所以如果想观察光照效果需要从 菜单执行更新法线的命令(modify->update normal)。然后新增加一条竖线,这条线要对应人的瞳孔和嘴角。

然后要不断的调节点的位置,使得模型接近参考图。现在继续切割出两条线,一条是在额头,另一条在嘴的位置。同样要在各个视图中调节位置。让线条过渡自然一点。

然后横向切割出鼻子,并且调节。随时可以细分进行观察。在调节好点的位置之后要切割出眼睛和嘴的大致形状。

然后在视图中调节,同时增加眼睛的细节。继续增加细节和调节点。调节点,可以用线框模式来显示,这样比较便于观察。

仍然是增加细节和调节点的工作。继续增加细节,但是这个时候要清楚线条的走向。建模有个原则--线条要符合肌肉的走向,就是图中我用红线画出来的线条。眼睛和嘴的线条要形成一个圈。

在增加线条的同时要不断切割出新的边,但是同时还要删除旧的边。因为我们是盒子建模,一开始的边都是横平竖直的,这个会对我们的最终模型产生一定影响。所谓盒子建模不容易把握细节,很大程度上就是这个原因。其实我觉得懂得如何删要比懂得如何添要重要的多。

如果你最后的成品依然有很多横平竖直的线条,那一定是很失败的作品。继续添加鼻翼附近的线条,这样也是为了增加嘴部的细节。

当嘴的细节到达一定的程度就可以进一步刻画了。要做出黑人厚嘴唇的效果。在眼睛的位置切割出更多的边。

在鼻子上切割更多的边,这个是为了增加细节好开出鼻孔。现在可以依据参考图开出鼻孔了。首先切割出一个矩形的区域。

在这个区域上使用突出(modify->extrude)几次,做出洞来,然后要调节点。时不时细分一下来观看效果。继续增加鼻子的细节,一方面要做出鼻翼凸起的效果,另一方面是修改三角面,尽量把三角面改成四边形。

继续制作鼻翼。现在增加眼睛周围的圈数,增加细节准备做眼睛。

不断调整眼睛周围点的位置,然后选中眼睛上的面删除。删除之后就形成了眼眶。

如果把握不好眼睛的造型,可以生成一个球体作为眼球,放在眼睛的后面作为依据。细致刻画眼睑的效果。

选中眼睛镂空的一圈边,然后将视角转动到头的里侧,按住z键,拖拽出眼睛没入脑袋的部分。继续调节点的位置,让模型有厚度,不是像面具一样只是一个面片。

这是目前侧视图的效果。将嘴上的一组面删除,将嘴裂开,同时增加更多的边,做出嘴唇鼓起的效果。

要在侧视图中进行调节,细分观察效果。做出镜像的效果观察。

调节脸型鼻子和嘴上的点。和做眼睛类似,选择嘴镂空的一圈边之后将视图转动到脑袋里侧,按住z键突出处一些面。

然后调节这些面。现在要做出下巴骨,不知道应该怎么说。让这个地方的线条更加明显,这个线条要成直角。

如图红色的地方,让点往里缩,这样可以显现出下巴骨的线条。然后要制作脖子了,选择脖子位置的面删除。然后再脖子镂空的地方,选择一圈边进行突出。

突出以后在前侧视图调节点的位置,使得它们契合参考图。不要忘了在透视图中进行调节。注意途中红线的位置,这个地方要做出一个凸起。

如图增加细节,做出凸起的效果。调节,细分。

换个角度。复制出镜像来观察。

下面做耳朵,在侧视图中删除耳朵位置上的面。在侧视图中调整出一个耳朵的轮廓。选择一个边突出一小部分,然后以这个突出的部分为基础按照图中的顺序继续突出记下,形成一个耳朵的轮廓。

还要到头视图里面调整这个轮廓的位置。然后细分进行观察。

这个地方我做的比较糙。实际上应该做出耳朵里面的褶皱,但是我偷懒了。我这里简单的把耳朵的轮廓进行了延展,然后焊接,也就是把上面的框框给封起来了。不要忘了调整耳根后面的点,这个地方的点要往里收缩。

这个时候要把脑袋后面一些刚才忽略的细节给补上。基本就算完成了,然后细分一下,使一个重新定义控制点(subdivision->redefine control mesh),把模型定格在细分的状态,如图:

详细信息可访问我的博客:/

本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。

上一篇:支持移动计算的反射式中间件 下一篇:基于OpenGL的军用物资装载仿真系统研究

文档上传者
热门推荐 更多>
精品范文更多>