Triangle Types and Particle Engine in OpenGL (L10)

 

2015-12-22_142743

今天要介紹 Particle EngineTriangle Types。本篇主要是參考 NeHe Tutorial19 所改寫的,內容概要差不多,只是呈現方式不一樣而已。

● Triangle Types

首先介紹繪畫三角形的方式,在這之前我們都會使用 glBegin(GL_QUADS) 來繪製多邊形,今天介紹三種不同繪製三角形的方法,或者說是利用三角形繪畫法來描繪不同型態的多邊形。這三種方法分別為 GL_TRIANGLESGL_TRIANGLE_STRIPGL_TRIANGLE_FAN

1334232485_5764

  1. GL_TRIANGLES 為基礎的三角形繪畫法,給定三個點座標,就可以畫出所想要的三角形了,與 GL_QUADS 作法相同。
  2. GL_TRIANGLE_STRIP 此繪畫方式可以只用 N+2 個頂點畫出 N 個三角形出來,但必須注意每個三角形在繪製頂點時的方向,必須都為順時針或逆時針來繪製。以上圖為例,繪製第一個三角形時,頂點順序為 V0, V1, V2;第二個三角形頂點順序為 V2, V1, V3;第三個三角形 V2, V3, V4 ... 以此類推。
  3. GL_TRIANGLE_FANGL_TRIANGLE_STRIP 類似,但繪畫順序為 V0, V1, V2 ... 第一個點為扇形的圓心點(V0)。
glBegin(GL_TRIANGLE_FAN); // triangle fan
glTexCoord2f(1,1);	glVertex3f(0.0f, 0.0f, z);// V0
glTexCoord2f(0,1);	glVertex3f(2.0f, 0.0f, z);// V1
glTexCoord2f(1,0);	glVertex3f(1.4f, 0.8f, z);// V2
glTexCoord2f(0,0);	glVertex3f(0.8f, 1.4f, z);// V3
glTexCoord2f(1,1);	glVertex3f(0.0f, 2.0f, z);// V4
glEnd();

 


● Particle Engine

接下來介紹本篇的重點,粒子引擎( Particle Engine )。粒子引擎的目的是在屏幕上繪出大量的粒子,再藉由畫面的更新,讓粒子在三維空間中移動,製造出像粒子爆發或者射散出去的視覺觀感。在這裡我是藉由粒子引擎模擬出婚禮拉炮的視覺效果。 首先定義出粒子的 struct 和粒子顏色。

//@ particle
struct Particle
{
 bool active;
 float life;
 float fade;
 float r, g, b;
 float x, y, z;
 float xi, yi, zi;
 float xg, yg, zg;
};
Particle particle[1000];
GLfloat particle_colors[12][3] =
{ 
 {1.0f, 0.0f, 0.0f} , {1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 1.0f},
 {0.0f, 0.0f, 1.0f} , {1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.5f}, {0.5f, 1.0f, 0.3f},
 {0.2f, 0.5f, 0.8f} , {1.0f, 0.0f, 0.5f}, {0.5f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.2f}
};

active 表示粒子是否活化;life 是粒子存活與否;fade 則是粒子褪色,這會影響到 life 參數(後面會說明);rgb 為粒子顏色;xyz 為粒子位置;xi yi zi 為粒子的移動方向;xg yg zg 為環境的重力場分量;particle[1000] 為每次在屏幕出現的粒子數為 1000 個;particle_colors 為定義多組顏色給各個不同的粒子使用。

//@ particle initialize
 for(int loop = 0; loop < 1000; loop++)
 {
 particle[loop].active = 1;
 particle[loop].life = 1.0f;
 particle[loop].fade = float(rand()%100)/1000.0f + 0.03f;
 particle[loop].r = particle_colors[loop%12][0];
 particle[loop].g = particle_colors[loop%12][1];
 particle[loop].b = particle_colors[loop%12][2];

particle[loop].x = 0.0f;
 particle[loop].y = 0.0f;
 particle[loop].z = 0.0f;

particle[loop].xi = float( rand()%50 + 25.0f) * 10.0f;
 particle[loop].yi = float((rand()%50) - 25.0f) * 10.0f;
 particle[loop].zi = float((rand()%50) - 25.0f) * 10.0f;

particle[loop].xg = 0.0f;
 particle[loop].yg = -9.8f;
 particle[loop].zg = 0.0f;
 }

接著在 initializeGL() 裡面定義粒子參數,這裡定義每個粒子的 active 都是1表示存活;life 設為 1.0f,而 fade 則會根據 rand() 函式來決定每個粒子退化的速度;rgb 與 xyz 就是顏色與起始位置;特別注意到 xi yi zi 的數值範圍,xi 介於 25~74、yi 與 zi 都介於 -25~24 之間,由於我這裡要模擬婚禮拉炮的效果,所以 x 向量的分量要是正的(單一方向),且要大於 y分量與 z分量,表示粒子大部分受力來至於 xi 的方向;重力場只有 gy=-9.8 有值,且每個粒子所受的重力一致。

void myopenGL::paintGL()
{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 glLoadIdentity();
 glTranslatef(0.0f, 0.0f, -40.0f);
 glRotatef(rota_x, 1.0f, 0.0f, 0.0f);
 glRotatef(rota_y, 0.0f, 1.0f, 0.0f);
 glRotatef(rota_z, 0.0f, 0.0f, 1.0f);

 //# blend
 glColor4f(1.0f, 1.0f, 1.0f,0.5f);
 glBlendFunc(GL_ONE, GL_ZERO);
 glEnable(GL_BLEND);
 //# texture
 glEnable(GL_TEXTURE_2D);
 glBindTexture(GL_TEXTURE_2D, texture[0]);
 //# cone
 glPushMatrix();
 glTranslatef(-20.0f, 0.0f, 0.0f);
 glRotatef(-90, 0.0f, 1.0f, 0.0f);
 gluCylinder(quadratic, 3.0f, 0.0f, 8.0f, 32, 32); //cone
 glPopMatrix();
 double slowdown = 1.0f; //velocity
 glTranslatef(-20.0f, 0.0f, 0.0f);
 for(int loop = 0; loop < 1000; loop++)
 {
 if(particle[loop].active)
 {
 float x = particle[loop].x;
 float y = particle[loop].y;
 float z = particle[loop].z;

 //# blend
 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
 glColor4f(particle[loop].r, particle[loop].g, particle[loop].b, particle[loop].life);
 //# texture
 glDisable(GL_TEXTURE_2D);
 // Triangle Strip
 glBegin(GL_TRIANGLE_STRIP);
 glTexCoord2f(1,1); glVertex3f(x+0.5f,y+0.5f,z);//top right
 glTexCoord2f(0,1); glVertex3f(x-0.5f,y+0.5f,z);//top left
 glTexCoord2f(1,0); glVertex3f(x+0.5f,y-0.5f,z);//bot right
 glTexCoord2f(0,0); glVertex3f(x-0.5f,y-0.5f,z);//bot left
 glEnd();

particle[loop].x += particle[loop].xi/(slowdown*1000);
 particle[loop].y += particle[loop].yi/(slowdown*1000);
 particle[loop].z += particle[loop].zi/(slowdown*1000);

particle[loop].xi += particle[loop].xg;
 particle[loop].yi += particle[loop].yg;
 particle[loop].zi += particle[loop].zg;
 particle[loop].life -= particle[loop].fade;

 if(particle[loop].life < 0)
 {
 particle[loop].life = 1.0f;
 particle[loop].fade = float(rand()%100)/1000.0f + 0.003f;
 particle[loop].x = 0.0f;
 particle[loop].y = 0.0f;
 particle[loop].z = 0.0f;
 particle[loop].xi = float( rand()%50 + 25.0f) * 10.0f;
 particle[loop].yi = float((rand()%50) - 25.0f) * 10.0f;
 particle[loop].zi = float((rand()%50) - 25.0f) * 10.0f;
}}}}

最後就是在 paintGL() 裡繪出每個粒子的位置與顏色,可以特別注意每個粒子的位置會受到 slowdown 變數影響(粒子速度)和 重力g 影響;至於 life 會隨著 fade 而慢慢減少(life 代表每個粒子的透明度),當 life 減至零時,表示粒子已經死亡,則會再生成另一個粒子出來。另外可以注意到 code 裡面 #texture #blend 的差異,因為畫面裡的圓錐是非透明的 texture,而粒子是有透明的顏色,所以透過 texture 與 blend 的開關來做轉換。

 


執行結果:

 


reference :

http://nehe.gamedev.net/tutorial/particle_engine_using_triangle_strips/21001/

http://blog.csdn.net/xiajun07061225/article/details/7455283

 

Leave a Reply

Your email address will not be published. Required fields are marked *