读书人

OpenGLES2应用 - 手机上的实时毛发模拟

发布时间: 2013-10-08 17:08:58 作者: rapoo

OpenGLES2应用 -- 手机上的实时头发模拟

首先先要向一篇博文的作者致敬。两年前在想自己留学研究计划时,正好有幸读到Moli的博文《爱丽丝的发丝》。当时就觉得,实时的头发模拟可以做到这么逼真很不可思议。由着自己对物理模拟和图形学向往和那时的感动,就把自己的研究课题订为了头发模拟。正好也想做图形引擎(实际上称不上引擎,只是练习),又在学OpenGLES2,所以顺势把目标瞄准到了手机平台。后来就有接近一年断断续续写了个OpenGLES2的框架,支持自己Android和iOS移动平台上图形的研究。


OpenGLES2应用 - 手机上的实时毛发模拟

Samsung GalaxyS3, 实时模拟4368根动态发丝

对于OpenGLES2框架的说明已经写在了前面的文章里,是个微型的主要使用C++编写的框架。接近开学时,框架已经8成完成了,就花了1个月用弹簧质点系统做了个头发模拟的程序,模拟都在CPU走,绘制使用GLES2。可以实时模拟大约100根头发。然后就把程序导入Android手机上拿给指导老师看,那时渲染还做得很差,老师说你可以试试把目标定为做到PC上模拟的质量,有些没有方向时实验室同学介绍给我一篇论文《Fast Simulation of Inextensible Hair and Fur》,后来按这这篇论文的思路改写了模拟的部分,大致性能提高了几十倍,基本上做到了手机上的实时模拟。

下面是相关的细节。


1. 头发实时模拟(含渲染)事件

这些大部分是显卡厂商的Demo,可以说是头发模拟游戏应用方面上的里程碑。这里顺便把模拟和渲染的方法也列举一下,模拟方法以弹簧系统为主流,Verlet-integration实现的类弹簧系统较为高效,另外PC上基本都用GPU来做模拟加速了。nVidia和ATI/AMD的Demo基本是开源的,可供参考的包括源码下载和TechnquePaper。


2004 ATI ruby hair

Simulation:Non

Rendering: KajiyaKay shading with highlight shift,Approximated depth-sorting

OpenGLES2应用 - 手机上的实时毛发模拟


2004 nVidiaNalu hair

Simulation: spring system

Rendering:Marscher shading, OpacityShadowMap

OpenGLES2应用 - 手机上的实时毛发模拟


2008 nVidiaRealTime hair

Simulation: spring system (Verlet integration)

Rendering: KajiyaKay shading with highlight shift, DeepOpacityMap

OpenGLES2应用 - 手机上的实时毛发模拟


2011 Alice

Simulation: spring system (Verlet integration)

Rendering: KajiyaKay shading

OpenGLES2应用 - 手机上的实时毛发模拟


2012 Square Enix Agni

Simulation: ?

Rendering: EnviromentLighting, OderIndepedentTransparency

OpenGLES2应用 - 手机上的实时毛发模拟


2013 AMDTressFX Tomb raider

Simulation: spring system (Verlet integration)

Rendering: KajiyaKay shading with highlight shift, ShadowMap

OpenGLES2应用 - 手机上的实时毛发模拟


2. 模拟方法

当使用弹簧系统实现模拟头发时,需要限制时间步长,还需要额外的对绳子(即头发)的长度约束(可参看博文《爱》)。其原因就是弹簧系统容易照成发丝的非自然伸展,时间步长的限制会增加迭代次数导致模拟低效,对头发做长度约束(比如用Long Range Attachment)可以部分解决此问题。实际上弹簧系统模拟的头发大都只模拟了几百根头发(guide),然后会制作一些围绕着guide插值出更多发丝(可达到上万根)做渲染。论文《Fast Simulation of Inextensible Hair and Fur》中,提出了一种模拟头发的方—FTL,此方法保证头发的非延展性,同时十分高效,在CUDA实现模拟的条件下实时模拟了两万根以上的头发。下面是这个算法的描述

OpenGLES2应用 - 手机上的实时毛发模拟


上图左边描述的是绳索模拟算法FTL(Follow The Leader),右边是DFTL—ynamic FollowThe Leader)。字如其意,算法FTL中当第一个粒子P0(比如发根)移动后,第二粒子P1为了保证和P0的距离不变,向着第一个粒子新位置P0’的方向追赶,移动d1到达P1’。之后的粒子跟着以同样方式调整位置。FTL的缺点是无法表现后面的粒子对前方粒子的作用,造成质量不均衡感。
DFTL根据FTL做了一个改进: FTL中每个粒子Pi的移动向量记为di,则可以说FTL只会考虑第i个粒子和第i - 1个粒子的位置关系,而DFTL还会考虑第i个粒子和第i+1个粒子的位置关系,以此体现后方粒子的作用力。OpenGLES2应用 - 手机上的实时毛发模拟


OpenGLES2应用 - 手机上的实时毛发模拟


公式(1)(2)描述的是FTL,对DFTL的描述还需要(3)~(5)。

公式中p为质点位置,L为距离约束(标量),g表现重力,Sdamping为速度矫正参数,取值[0,1],在后来的实验里,发现当质点较少时此值需要较多调整,根据质点的顺序取0.2~0.95。另外,取则0退变为FTL。

OpenGLES2应用 - 手机上的实时毛发模拟 OpenGLES2应用 - 手机上的实时毛发模拟


上图为关于FTL和DFTL的实验,模拟256根绳索,每根绳索12个质点。前一张图是FTL,后面一张是DFTL,在重力和障碍物的影响下,只有DFTL模拟的绳索体现出了后端绳索拉扯前段绳索的效果(圈出的部分)。256根绳索,每根绳索12个质点。


3. OpenGLES2的限制和绘制策略

模拟方法可以使用之前说的DFTL,在三星的GraxyS3上有2000根发丝(CPU模拟,每个发丝6个控制点)FPS20上的效率。不过对于绘制的实现,也非一帆风顺,原因是OpenGLES2只是OpenGL2的一个子集,许多特性无法支持。碰到的问题如下:

1) RTT(Render to texture)的限制

OpenGLES2支持RTT,不过不使用扩展的OpenGLES2(Core OpenGLES2)不支持16位的浮点纹理,能使用最高精度的RTT纹理格式为RGBA8888。另外MRT(multiple rendertarget)多目标渲染也不被支持。这样类似Depth Shadow Map或是SSAO这些算法,就无法以高效率表现更好的效果。

2) 顶点Shader无法访问纹理

OpenGLES2包含功能类似OpenGL2的vertex-shader和fragment-shader着色器。不过如之前提到的,vertex-shader中无法访问纹理(不使用扩展为前提)。所以假设使用fragment-shader做GPGPU加速模拟运算,数据还必须从GPU送往CPU处理生成几何,一般这会是非常费时的操作。

3) 硬件的像素填充率

从芯片体积来看,手机GPU中计算单元数量应该较笔记本还差一些吧。像素填充率是十分容易产生性能瓶颈的地方。OIT(Oder Independent Transparency)算法需要关闭Z遮挡,这会造成需要处理的片元(Fragment)数量上升,当镜头接近角色头发,放大的头发几何和容易重叠,片元会大量重叠在某些像素上,造成像素填充率瓶颈。所以OIT也很难实时实现。

最后采取的绘制策略主要考虑如何分配合适的计算量在CPU和GPU上。模拟都在CPU上完成,然后头发一根一根地绘制。详细说来,在CPU上模拟的控制点(每根发丝6个点)会以uniform变量送入vertex-shader。在vertex-shader中构建贝塞尔曲线,然后以线段绘制,或者根据Tangent作出面向屏幕的四边形构成三角形带绘制。后者则可以大量减少需要模拟的发丝数量以及Draw Call。着色的话使用简化了的KajiyaKai着色模型。

color=Cd?sinTH + Cs?pow(sinTH,Ks)

Cd,Cs,Ks为漫反射颜色,高光颜色,和高光强度系数。

TH则为切线T(Tangent)和H(Half Vector)的点积,Half Vector由(LightDir+EyeDir)/2可得出。


4. 两个加速Trick
OpenGLES2应用 - 手机上的实时毛发模拟

第一个Trick,加上可以插值出的发丝slave strand,根据主导发丝移动。这里使用了FLT把slave node绑定到DFTL后端,减少活动的质点以节省计算消耗。这个Trick使在相同的计算量中,稍微增加了发丝的多样性和数量。

第二个Trick,使用PostProcess来制作发尖(Tip)的AlphaBlend。原因是OpenGL的AlphaBlend要求半透明物体按深度排序(否则无法正确混合背景和物体,但是深度排序比较费时),使用AlphaTest的话会造成锯齿。(注:OpenGLES2的AlphaTest的实现方法是在fragment-shader中使用discard语句)。所以这里想到把以三角形带绘制的头发的切线和纹理坐标绘制到渲染目标上,然后使用PostProcess,探测发尖的部分,加上Alpha半透明处理的方法。

OpenGLES2应用 - 手机上的实时毛发模拟OpenGLES2应用 - 手机上的实时毛发模拟OpenGLES2应用 - 手机上的实时毛发模拟

上图分别说明a)绘制切线,纹理坐标。b)沿着切线(屏幕空间中)探测发尖。c)得到的发尖区域。

OpenGLES2应用 - 手机上的实时毛发模拟

AlphaBlend,AlphaTest和原创的Post process的效果比较,可以发现只有原创的Post process可以柔和地表现以三角形带绘制的发尖的半透明效果。

5. 结果OpenGLES2应用 - 手机上的实时毛发模拟

最后程序导入不同的目标手机,进行可执行性和性能测试:。

下面是实测的三组数据:

1. Motorola XT319, Android 2.3.4,

134 strands三角形带@30 fps(Post process打开14fps)

2. Apple iPhone 4S, iOS 6

4368 strands线段@16fps

3. Samsung Galaxy S3, Android 4.0.4

4368 strands线段@18fps

1289 strands三角形带Post process@30fps

OpenGLES2应用 - 手机上的实时毛发模拟

转头时的头发动画序列。

OpenGLES2应用 - 手机上的实时毛发模拟 OpenGLES2应用 - 手机上的实时毛发模拟

一个模仿过山车的程序,由此程序发现自己实现的DFTL中,有些头发在高速移动中还是被拉伸了。




6. 讨论

算法以不使用扩展的GLES2为前提,可以做到最大的兼容性。这里对于表现头发至关重要的阴影还没有完成。OpenGLES 3.0的设备现在才刚出来,等到3.0普及后,顶点纹理和多渲染目标可以用来完成GPU上的头发模拟。

还是大学本科学生时想自己做些计算机动画出来会有些成就感,可惜计算机图形实在太深太广,叹息心有余而力不足,只好走马观花,且游且行(好吧,其实是龟速爬行)。


下面是算法和手机上实时模拟的相关视频:

http://youtu.be/8nEc8KXNkdE

另外,自己使用的移动平台框架和头发渲染的源码有准备公开,有意者请联系sinotaku1112@gmail.com


读书人网 >移动开发

热点推荐