粒子特效编程与手机应用研究

粒子系统于 1983 年由 reeves 提出 ,最初它并不是针对非刚性物体的建模而提出,它是为了解决对一些自然现象和自然景物的模拟而产生的,如雨,雪,雾,水波,火焰,焰火,烟尘,流星等自然现象,后来才逐渐的用于对非刚性的物体建模之中。

构成物体的物质都是由基本粒子构成,水由水分子构成,烟雾由气体分子和尘埃构成,火焰由光子和空气微粒构成,研究这些构成物质的粒子的状态和运动方式,并建立相应的物理模型,就可以用计算机模拟出相应的自然现象。粒子系统的方法就是将大量的粒子单元集合在一起,通过其属性的变化表现物体的物理特性的物体模拟方法。每个粒子是有着形状大小颜色透明度位置及速度等属性的几何单元,一个粒子究竟有什么样的属性主要取决于具体的应用。粒子系统不是一个简单的静态系统,随着时间的推移,系统中已有的粒子不仅不断改变形状,不断运动,而且不断有新的粒子加入,并有旧粒子的消失,这样才能演示出我们这个变幻万千,生机勃勃的世界。而粒子与我们显示设备中的像素具有一定的相通相似性。利用一个或者多个像素构造粒子,再赋予粒子各种属性,状态,运行轨迹,把成百上千个同样的粒子组合起来,利用随机函数,程序员有时可以创造出令艺术家都惊叹的视觉效果。

最近在网上阅读突然对粒子起了兴趣,趁空闲时研究了一下网上有关粒子编程的水波,火焰,下雪效果,按照相应的算法,写出相应的手机算法,初期效果不理想,后期根据相应手机平台优化后,运行速度得到极大提升,在某些情况下,我不得不使用 TIMER 延缓每帧播放的速度。

三种效果,水波的模拟运算量最高,火焰次之,下雪最少。下雪特效使用了网上通用的算法,几乎不做优化就能使用。我做的工作主要在于单个雪花形状的渲染,通过设计不同形状,不同大小,不同水平升垂直速度的雪花,体现某种空间感。使用 FOR 循环随机为雪花赋值坐标,橫向速度,纵向速度,生命周期,不同大小的雪花可以形成空间层次感,速度的不同可以表现出雪花随风的动态,雪花生命周期结束时停留下来生成雪地。这个程序也可以模拟落叶和落花效果和下雨效果,就是更改一下横向的速度,落叶和落花由于空气浮力的存在,在下落上会表现的更大的横向飘移,雨的纵向速度要比横向更大一些,而且偏移方向更一致一些。效果如下,左边是原图,右边是加入了飞落雪花的效果。

雪花渲染如下:

void drawonwsnow(S32 x, S32 y, gdi_color c)

{

       switch (rand()%16)

       {

 

              case 0:

                     gdi_act_put_pixel(x, y, c);

                     break;

              case 1:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     break;

              case 2:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(y-1, y, c);

                     break;

              case 3:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     break;

              case 4:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

                     break;

              case 5:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

                     break;

                    

              case 6:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

                     break;

 

              case 7:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                    

                     break;

                    

              case 8:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y+1, c);

                     break;

              case 9:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

                     break;

              case 10:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

 

                     gdi_act_put_pixel(x-1, y-1, c);

                     gdi_act_put_pixel(x+1, y+1, c);

                     gdi_act_put_pixel(x+1, y-1, c);

                     gdi_act_put_pixel(x-1, y+1, c);

                     break;

              case 11:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

 

                     gdi_act_put_pixel(x-1, y-1, c);

                     gdi_act_put_pixel(x+1, y+1, c);

                     gdi_act_put_pixel(x+1, y-1, c);

                     gdi_act_put_pixel(x-1, y+1, c);

 

                     gdi_act_put_pixel(x-2, y, c);

                     break;

              case 12:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

 

                     gdi_act_put_pixel(x-1, y-1, c);

                     gdi_act_put_pixel(x+1, y+1, c);

                     gdi_act_put_pixel(x+1, y-1, c);

                     gdi_act_put_pixel(x-1, y+1, c);

 

                     gdi_act_put_pixel(x+2, y, c);

                     break;

              case 13:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

 

                     gdi_act_put_pixel(x-1, y-1, c);

                     gdi_act_put_pixel(x+1, y+1, c);

                     gdi_act_put_pixel(x+1, y-1, c);

                     gdi_act_put_pixel(x-1, y+1, c);

 

                     gdi_act_put_pixel(x, y-2, c);

                     break;

              case 14:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

 

                     gdi_act_put_pixel(x-1, y-1, c);

                     gdi_act_put_pixel(x+1, y+1, c);

                     gdi_act_put_pixel(x+1, y-1, c);

                     gdi_act_put_pixel(x-1, y+1, c);

 

                     gdi_act_put_pixel(x, y+2, c);

                     break;

              case 15:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

 

                     gdi_act_put_pixel(x-1, y-1, c);

                     gdi_act_put_pixel(x+1, y+1, c);

                     gdi_act_put_pixel(x+1, y-1, c);

                     gdi_act_put_pixel(x-1, y+1, c);

 

                     gdi_act_put_pixel(x, y+2, c);

                     gdi_act_put_pixel(x, y-2, c);

                     gdi_act_put_pixel(x+2, y, c);

                     gdi_act_put_pixel(x-2, y, c);

                     break;

              default:

                     gdi_act_put_pixel(x, y, c);

                     gdi_act_put_pixel(x-1, y, c);

                     gdi_act_put_pixel(x+1, y, c);

                     gdi_act_put_pixel(x, y-1, c);

                     gdi_act_put_pixel(x, y+1, c);

                     break;

       }

}

 

而对于火焰来说,就没有那么幸运,每一帧差不多要对一个完整的屏幕大小的数据 BUFFER 运算赋值,这使得他的渲染不能再使用 gdi_act_put_pixel 直接赋值,需要直接修改单个屏幕层的 BUFFER 来实现。火焰算法网上有很多,这里不多讲解:

1.       放置火源

for(x=0;x

              {

                     //srand(time(NULL));

                     fire[blue_h-1][x]=rand()%256;

              }

2 .火焰上升,原本要除以四,考虑到火焰衰减,这里从乘以 1000 ,除以 3925 得到,主要用于优化速度,避免除以一个浮点数降低程序的效率。

fire[y][x]=(S32)(((fire[y][x]+fire[y+1][x-1]+fire[y+1][x+1]+fire[y+1][x])*1000)/3925)%256;

 

      

左边是小火,右边是加入了衰减后的大火,可以渲染整个窗口

对于水波算法,网上也有很多,主要是考虑到折射和衰减,其运算量大约相当于三到四屏次数的运算,为了优化算法,达到每秒十帧到二十帧。算法的优化是必不可少的,最重要的还是渲染,水波详细算法可以在网上看到,首先和火焰一样,放置波源:

 

1 .放置波源

// 加入波源

void DropStone(S32 x,S32 y, U16 stonesize, U8 stoneweight)

{

    S32 posx, posy;

      

    if ((x + stonesize) > MY_WATER_W || (y+stonesize) > MY_WATER_H ||

        (x-stonesize)<0 ||(y-stonesize)<0)

       {

        return;

 

       }

 

    for (posx = x - stonesize; posx < x+stonesize; posx++)

       {

        for (posy = y-stonesize; posy < y+stonesize; posy++)

           {

            if ((posx-x)*(posx-x) + (posy-y)*(posy-y) < stonesize*stonesize)

               {

                my_water_buf1[MY_WATER_W*posy+posx] = -stoneweight;

               }

           }

       }

}

 

2 .计算缓冲区,这段代码出于效率考虑,我对他做了优化,比网上的多了一些代码,细心的朋友很快就能发现

// 计算缓冲区

void RippleSpread()

{

       S32 i, j;

       char * p;

 

       if (isrun)

       {

              for (i=MY_WATER_W; i

              {

                     // 波能扩散

                     my_water_buf2[i] = ((my_water_buf1[i-1]+

                            my_water_buf1[i+1]+

                            my_water_buf1[i-MY_WATER_W]+

                            my_water_buf1[i+MY_WATER_W])

                            >>1)

                            - my_water_buf2[i];

                     // 波能衰减

                     my_water_buf2[i] -= my_water_buf2[i]>>5;

              }    

              p = my_water_buf1;

              my_water_buf1 = my_water_buf2;

              my_water_buf2 = p;

              for (i=0; i

              {

                     isrun = FALSE;

                     if (my_water_buf1[i] != 0)

                     {

                            isrun = TRUE;

                            break;

                     }

 

              }

       }

}

4 .渲染屏幕,这一步是很简单的,和网上是差不多的,直接拉来也可以使用,只是效率低一些,自己简单优化一下就会有很好的效率。

 



你可能感兴趣的:(粒子特效编程与手机应用研究)