OpenGL이 구현한 불꽃 입자 시스템

9801 단어 플레이엔진
최근에 OpenGL을 공부하고 있는데 한 네티즌이 쓴 불꽃놀이 시스템을 봤어요. 너무 예뻐요. 자기도 한번 실현해서 공부하는 연습으로 삼았어요.코드는 각각 VC와 안드로이드에서 이루어지고 이따가 제가 올리면 여러분들이 교류를 다운로드할 수 있습니다.기본 원리:
물리적으로 입자는 먼저 위로 고른 감속 직선운동을 하고 s=v*t, v=a*t;x,z 방향 속도는 0입니다.최고점에 도달한 후 360도에서 24개의 작은 입자를 골고루 튀기고 x,z방향에서 골고루 직선운동을 하고 vx=r*sin(radian), vz=r*cos(radian);y방향으로 균등가속직선운동을 한다.꼬리를 끄는 실현 원리는 입자마다 한 꿰미의 입자가 뒤따르는 것이다. 마치 한 마리의 엄마 닭 뒤에 크기의 한 무리의 인형을 데리고 있는 것과 같다. 입자 인형의 위치는 입자 엄마가 최근에 N번 운동을 거친 위치(N은 뒤따르는 입자 인형의 개수를 표시), 입자 인형의 크기는 각각:sizebaby = size_mother * (1 - (float)n/N);유사하게 입자 인형의 투명 효과:alphababy = alpha_mother*(1-(float)n/N)는 간단하지만 실현된 꼬리 효과가 좋네요. 더 좋은 효과를 추구한다면 점차적으로 줄어드는 비선형 함수를 시도해 보세요. 예를 들어 1-sin(3.141593f/2*n/N).데이터 구조:
const int MAX_FIRE = 5;       //   5   
const int MAX_PARTICLES = 24; //               
const int MAX_TAIL = 30;      //      

typedef struct {
   float r, g, b;      /* color */
   float x, y, z;      /* position  */
   float xs, ys, zs;   /* speed  */
   float xg, yg, zg;   /* gravity  */
   boolean up;         /* up or down */
} Particle;

typedef struct {
   Particle particle[MAX_PARTICLES][MAX_TAIL]; //       
   float life, fade, rad; //   ,    ,x-z        
} Fire;

Fire fire[MAX_FIRE];

초기화:
for(int loop = 0; loop < MAX_FIRE; loop++) {
   resetFire(loop);
}//for loop end
   
void resetFire(int loop) {
   // init position
   float xtemp = rand()%30 - 15.f;
   float ytemp = -1*rand()%5 - 15.f;//8.f;
   float ztemp = -1*rand()%5 - 15.f;//100.f;
   float speed = rand()%5 + 15.f;

   fire[loop].life = 1.5f;//1.0f;
   fire[loop].fade = (float) ((rand()%100)/20000 + 0.002);
   fire[loop].rad  = rand()%3 + 4.0f;

   for (int loop1 = 0; loop1 < MAX_PARTICLES; loop1++) {
      Particle* pat = &fire[loop].particle[loop1][0];
      //    
      pat->r = 1.0f; pat->g = 1.0f; pat->b = 1.0f;
      //    
      pat->x = xtemp; pat->y = ytemp; pat->z = ztemp;
      //    
      pat->xs = 0.0f; pat->ys = speed; pat->zs = 0.0f;
      //     
      pat->xg = 0.0f; pat->yg = -5.f; pat->zg = 0.0f;
      pat->up = true;

      //     
      for(int loop2 = 1; loop2 < MAX_TAIL; loop2++) {
         pat = &fire[loop].particle[loop1][loop2];
         pat->x = fire[loop].particle[loop1][0].x;
         pat->y = fire[loop].particle[loop1][0].y;
         pat->z = fire[loop].particle[loop1][0].z;
      } //for loop2 end
   }//for loop1 end
}

입자 동작 및 렌더링 기본 논리:
int DrawGLScene(GLvoid)	{ // Here's Where We Do All The Drawing
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear Screen And Depth Buffer
   glLoadIdentity();		// Reset The ModelView Matrix

   //               
   float rgb_max_value = 0.0f;
   for(int loop = 0; loop < MAX_FIRE; loop++) {
      float rgb_value = fire[loop].particle[0][0].r + fire[loop].particle[0][0].g + fire[loop].particle[0][0].b;
      if (rgb_value > rgb_max_value) {
         rgb_max_value = rgb_value;
         glColor4f(fire[loop].particle[0][0].r, fire[loop].particle[0][0].g, fire[loop].particle[0][0].b, 1.0f);
      }
   }

   //     
   glBindTexture(GL_TEXTURE_2D, texture[1]);
   glBegin(GL_TRIANGLE_STRIP);  // Build Quad From A Triangle Strip
   float w = 0.05522847498307933984022516322796f, h = 0.04142135623730950488016887242097f;
   // h = tan(45.f/2) * 0.1f; w = 640.f/480.f*h;
   glTexCoord2d(1,1); glVertex3f(w, h, -0.1f); // Top Right
   glTexCoord2d(0,1); glVertex3f(-w, h, -0.1f); // Top Left
   glTexCoord2d(1,0); glVertex3f(w, -h, -0.1f); // Bottom Right
   glTexCoord2d(0,0); glVertex3f(-w, -h, -0.1f); // Bottom Left
   glEnd(); // Done Building Triangle Strip

   //    ,         
   glBindTexture(GL_TEXTURE_2D, texture[0]);
   for(int loop = 0; loop < MAX_FIRE; loop++) {
      int color = rand()%12;
      for (int loop1 = 0; loop1 < MAX_PARTICLES; loop1++) {
         for(int loop2 = 0; loop2 < MAX_TAIL; loop2++) {
            float x = fire[loop].particle[loop1][loop2].x;
            float y = fire[loop].particle[loop1][loop2].y;
            float z = fire[loop].particle[loop1][loop2].z + zoom;
            float dt = 1 - (float)loop2/MAX_TAIL;
            glColor4f(fire[loop].particle[loop1][0].r, 
               fire[loop].particle[loop1][0].g, 
               fire[loop].particle[loop1][0].b, 
               fire[loop].life * dt);

            float size = 0.5f * dt;
            glBegin(GL_TRIANGLE_STRIP);   // Build Quad From A Triangle Strip
            glTexCoord2d(1,1); glVertex3f(x+size,y+size,z); // Top Right
            glTexCoord2d(0,1); glVertex3f(x-size,y+size,z); // Top Left
            glTexCoord2d(1,0); glVertex3f(x+size,y-size,z); // Bottom Right
            glTexCoord2d(0,0); glVertex3f(x-size,y-size,z); // Bottom Left
            glEnd(); // Done Building Triangle Strip
         }

         // update position
         for(int loop2 = MAX_TAIL-1; loop2 > 0; loop2--) {
            fire[loop].particle[loop1][loop2].x = fire[loop].particle[loop1][loop2-1].x;
            fire[loop].particle[loop1][loop2].y = fire[loop].particle[loop1][loop2-1].y;
            fire[loop].particle[loop1][loop2].z = fire[loop].particle[loop1][loop2-1].z;
         } //for loop2 end

         fire[loop].particle[loop1][0].x += fire[loop].particle[loop1][0].xs / (slowdown * 1000);
         fire[loop].particle[loop1][0].y += fire[loop].particle[loop1][0].ys / (slowdown * 1000);
         fire[loop].particle[loop1][0].z += fire[loop].particle[loop1][0].zs / (slowdown * 1000);

         // update y speed
         float yd = fire[loop].particle[loop1][0].yg / (slowdown * 1000);
         fire[loop].particle[loop1][0].ys += yd;
         if (fire[loop].particle[loop1][0].up && fire[loop].particle[loop1][0].ys < -yd) {
            fire[loop].particle[loop1][0].up = false;
            //int color = rand()%12;
            fire[loop].particle[loop1][0].r = colors[color][0];
            fire[loop].particle[loop1][0].g = colors[color][1];
            fire[loop].particle[loop1][0].b = colors[color][2];

            // x-z speed
            double radian = 3.14159*loop1*15.0/180.0;
            fire[loop].particle[loop1][0].xs = (float)(fire[loop].rad*sin(radian));
            fire[loop].particle[loop1][0].zs = (float)(fire[loop].rad*cos(radian));
         }

         if (keys[VK_UP] && fire[loop].particle[loop1][0].yg < 1.5f) {
            fire[loop].particle[loop1][0].yg += 0.01f;
         }

         if (keys[VK_DOWN] && fire[loop].particle[loop1][0].yg > -1.5f) {
            fire[loop].particle[loop1][0].yg -= 0.01f;
         }

         if (keys[VK_LEFT] && fire[loop].particle[loop1][0].xg < 1.5f) {
            fire[loop].particle[loop1][0].yg += 0.01f;
         }

         if (keys[VK_RIGHT] && fire[loop].particle[loop1][0].xg > -1.5f) {
            fire[loop].particle[loop1][0].yg -= 0.01f;
         }
      }

      fire[loop].life -= fire[loop].fade;
      if (fire[loop].life < 0) {
         resetFire(loop);
      }
   }
   
   if (keys[VK_TAB])	{ // Tab Key Causes A Burst
      for (int loop = 0; loop < MAX_FIRE; loop++) {
         resetFire(loop);
      }
   }
   return TRUE;
}

입자의 생명(life)이 0으로 줄어들면 입자가 죽어 알파치로 표시한다.
효과도:
 
추천 학습: 1., 이 책은 많은 OpenGL 학습자들에게 홍보서라고 불리며 Khronos팀이 작성한 공식 지침으로 품질과 권위성은 의심할 여지가 없지만 비교적 무미건조하기 때문에 여러분은 NEHE 강좌에 맞추어 공부할 수 있습니다.2, NEHE의 OpenGL 네트워크 자습서(http://nehe.gamedev.net/이 강좌는 48개의 예로 구성되어 있으며 저자는 개발 프레임워크를 구축하는 것부터 OpenGL을 예로 상세하게 설명하여 매우 볼 만하다.
코드 다운로드: VC 플랫폼:http://download.csdn.net/detail/ynnmnm/3894301안드로이드 플랫폼:http://download.csdn.net/detail/ynnmnm/3724974(android 플랫폼의 코드는 아날로그에서 조정된 것이다. 아날로그가 느리고 프레임 수가 낮기 때문에 실행할 때 위로 버튼을 누르면 슬로우다운을 줄여 속도를 높여야 한다. 만약에 핸드폰에서 실행하면 vc가 설정한 초기 y방향 속도와 가속도를 참고하여 수정하면 된다.)
update 1——2014.09.10
프로그램을 다 쓴 후에 오랫동안 다시 돌아와 보지 못했다.오늘 제가 csdn에 보내서 다운로드한 자원 평가를 봤는데 몇 가지 질문이 있었어요.
VC 플랫폼: 1.번역할 수 없다.2. 운행 오류 보고;3. 운행 시 폭발 효과가 없다.
내가 자세히 검사해 보니 주로 64비트 시스템이 지원하는 문제가 이미 복구되었다.이 슬라이드에서는 복구 방법에 대해 설명합니다.
    1. 64비트 시스템에서 일부 정적 라이브러리의 기본 링크가 64비트인 경우 컴파일 오류가 발생했습니다.라이브러리의 경로를 수정하고 32비트 라이브러리를 강제로 연결하면 됩니다.예를 들면, 나의 현재 기계 설정 아래, win8.1 64위 + vs 2013, win8 때문에1sdk는 32비트와 64비트의glu32를 자체로 가지고 있다lib, 기본적으로 64비트를 불러오기 때문에 컴파일은 error LNK2019: 해석할 수 없는 외부 기호gluPerspective@32.나는 이 줄 코드 #pragma comment(lib, "glu32.lib")를 #pragma comment(lib, "C:/Program Files(x86)/Windows Kits/8.1/Lib/winv6.3/um/x86/glu32.lib")로 바꾸면 된다.
    2. 실행 오류, 이것은 dll 라이브러리가 부족하기 때문일 가능성이 높습니다.라이브러리 파일은 32비트 또는 64비트에 따라 해당 디렉토리에 배치해야 합니다.
나는 csdn 다운로드 채널에서 모든 의존 라이브러리를 틀어 놓았지만, 최신 것이 아니라, 11년 동안 이 글을 썼을 때 틀어 놓았지만 사용할 수 있다.링크를 다운로드하려면 다음과 같이 하십시오.http://download.csdn.net/detail/ynnmnm/3759504
lib 라이브러리 추천...\vs 설치 디렉토리\VC\lib(예: 내 시스템: D:\Program Files(x86)\Microsoft Visual Studio 12.0\VC\lib 또는...\Windows Kits\Lib\um\x86 or x64, 예를 들어 내 컴퓨터에는 C:\Program Files(x86)\Windows Kits\8.1\Lib\winv6.3\um\x86
dll은 32비트 시스템의 경우 C:\Windows\System32 디렉토리에 배치합니다.
64비트 기기의 경우 32비트 라이브러리는 C:\Windows\SysWOW64 디렉터리에 두어야 한다. 예를 들어glut.dll --> C:\Windows\System32        glu32.dll/glut32.dll/glew32.dll/glew32mx.dll --> C:\Windows\SysWOW64
    3.운행에 폭발 효과가 없다.코드를 자세히 검사한 결과 입자의 생명은 프레임마다 감소하기 때문에 어떤 기계는 성능이 비교적 좋고 fps가 매우 높아서 입자가 아직 흩어지기 시작하지 않았는데 생명이 끝났다는 것을 발견했다.프레임을 정하거나 생명을 줄이는 것은 시간과 관련이 있으면 된다. 예를 들어 Fireworks::::Initialize() 방법에 FPS:::Instance()->fixFps(60)를 추가할 수 있다.또는 입자의 생명이 줄어드는 곳에 프레임이 지나간 시간을 곱한다.친선
지금 우리 집과 단위 기계는 모두 win8이다.1 64위 + vs 2013, 상기 방법에 따라 수정한 후 컴파일을 통해 완벽하게 실행할 수 있으며 효과는 위의 캡처와 같다.
안드로이드 플랫폼: 주요 반영 1.실행할 수 없음;2. 운행이 효과가 없다.
이것은 죄송합니다. 11년 동안 이 글을 썼을 때 저는 아직 안드로이드 핸드폰이 없었습니다. 당시 아날로그에서 실행된 효과는 win32 플랫폼의 효과와 똑같습니다.프로젝트 좀 봐.properties의 target=3은 오래된 것을 알 수 있다.나중에 시간이 나면 수정할 수밖에 없어요.

좋은 웹페이지 즐겨찾기