Post Lists

2018년 9월 2일 일요일

4. The Particle Physics Engine

4. The Particle Physics Engine
우리는 이제 중력에서 파티클들의 움직임을 재현할 수 있는 우리의 처음으로 작동하는 물리엔진을 가진다.

그것이 그러한 간단한 코드 조각인 것을 고려하면, 나는 그것 뒤에 있는 이론에 대해 말하는데 긴 시간을 썼다. 이것은 우리가 오브젝트의 회전을 위해 같은 종류의 로직을 반복할 때 이 책에서 나중에 중요할 것이다.

지금 우리의 엔진은 꽤 제한되어 있다. 그것은 오직 고립되 파티클들만을 다룰 수 있다. 그리고 그것들은 그것들의 환경과 상호작용을 어느 방식으로든 할 수 없다. 이러한 것들이 책의 다음 파트에서 다뤄질 심각한 제한일지라도, 우리는 여전히 우리가 가진 것으로 실용적인 것들을 할 수 있다.

이 챕터에서, 우리는 탄도학을 처리하기 위해 어떻게 엔진을 설정할지를 볼 것이다: bullets(총알), shells(포탄), and the like. 우리는 또한 불꽃놀이 디스플레이를 만드는 엔진을 사용할 것이다. 이러한 프로그램들 둘 다 skeleton form으로 여기에서 보여진다. 어떠한 렌더링 코드도 없이. 그것들을 CD에서 완전한 소스코드로 발견되어질 수 있다.

4.1 Ballistics
게임에서 물리 시뮬레이션 중의 가장 흔한 프로그램들 중의 하나는 탄도학을 모델링하는 것이다. 이것은 이십년 동안 사실이었다. 그리고 이것은 물리엔진의 현재의 유행보다 먼저온다.

우리의 탄도학 시뮬레이션에서, 각 무기는 파티클을 발사한다. 파티클들은 총알부터 대포포탄까지, fireballs부터 laser-bolts까지 어떤 것이든 타나낼 수 있다. 발사되는 오브젝트와 상관없이, 우리는 이것을 "projectile(발사체)"라고 부를 것이다.

각 무기는 특징적인 muzzle(총구) 속도를 가진다: 발사체가 무기에서 뿜어져 나오는 속도이다. 이것은 laser-bolt에 대해서는 매우 빠르고, fireball에 대해서는 상당히 더 느릴 것이다. 각 무기에 대해, 게임에서 사용되는 muzzle velocity는 그것의 실세계에서 동일한 것과 같지 않을 것이다.

4.1.1 Setting Projectile properties
가장 느린 실세계 총들에 대해 muzzle velocity는 250m/s인데, 반면에 순수 속도로 armor plate를 뚫도록 설계된 탱크 포탄은 1,800m/s로 움직일 수 있다. 레이저와 같은 에너지 무기의 muzzle velocity는 빛의 속도가 될 것이다: 300,000,000 m/s. 심지어 상대적으로 큰 게임 수준에서, 이러한 값들 어떤 것이든 너무 높다. 제곱 킬로미터를 나타내는 한 수즌(level)은 현대 게임 표준에는 거대하다 : 명백히 이것을 0.5초만에 가르지는 총알은 플레이어에게 실제로 보이지 않을 것이다. 만약 이것이 목표라면, 그러면 물리 시뮬레이션을 쓰지않고, 무기가 발사되는 순간 그 level을 통해서 ray를 간단히 cast하는게 더 좋다. 그리고 그것이 그 타겟과 충돌했는지 체크하는게 더 좋다.

대신에, 만약 우리가 발사체의 움직임이 보이도록 하고 싶다면, 우리는 human-scale game에 대해 5 ~ 25m/s의 영역에 있는 muzzle velocities를 사용한다. (만약 너의 게임이 한 대류의 적발을 나타내고, 각 unit이 도이 크기라면, 그러면 그것은 그것에 대응하여 더 클 것이다.) 이것은 우리가 처리할 두 가지 결과를 야기시킨다.

첫 째로, 파티클의 질량은 실제 세계에 있는 것 보다 더 커야만 한다, 특히 만약 너가 이 책에서 나중에 full physics engine으로 작업하려 하고, 너가 impacts들이 인상적으로 보이길 원한다면 (상자에 쏴서 그것을 넘어뜨릴 수 있다, 예를들어). 그것이 충돌할 때 발사체가 가지는 효과는 그것의 질량과 속도에 의존한다: 만약 우리가 속도를 떨어뜨린다면, 우리는 보상하기 위해 질량을 증가시켜야 한다. 에너지, 질량, 그리고 속도를 연결시키는 방정식은

e = m s^2

e는 에너지이고, s는 발사체의 속도이다 (이 방정식은 벡터로 작동하지 안흔다, 그래서 우리는 속도를 사용할 수 없다). 만약 우리가 같은 에너지를 유지하고 싶다면, 우리는 속도에서의 알려진 변화에 대해 질량의 변화를 처리할 수 있다:



실 세계 탄약은 질량에서의 그램에서부터 무거운 포탄에 대해 몇 킬로그램까지 범위에 걸치고, 다른 전략 무기들은 더 나간다 (2차 걸프만 전쟁에서 사용된 bunker-busting shells은 1000kg 이상의 무게가 나간다). 일반적으로 500m/s로 이동하는 전형적인 5g bullet은 25m/s로 느려질 것이다. 이것은 20의 Δs이다. 같은 에너지를 얻기 위해서, 우리는 그것에게 400배의 무게를 줄 필요가 있다: 2kg.

둘 째로, 우리는 발사체의 중력을 줄여야만 한다. 대부분의 발사체들은 비행중에 너무 느려선 안된다. 그래서 damping parameter는 1에 가까워야 한다. 포탄과 박격포는 중력의 힘 아래에서 arch형을 그릴지도 모르지만, 다른 발사체 타입들은 거의 그 효과를 느껴서는 안된다. 만약 그것들이 매우 빠른 속도로 이동하고 있다면, 그러면 그것들은 중력에 의해 크게 당겨질 시간을 갖지 못할 것이지만, 우리가 그것들을 느리게 했기 때문에, 중력은 더 길게 그것의 일을 할 것이다. 마찬가지로, 만약 우리가 게임에서 더 높은 gravity coefficient를 사용한다면, 그것은 탄도학 궤적을 너무 심하게 만들 것이다: 잘 조준된 발사체는 캐릭터 앞 몇 미터에서 땅을 칠 것이다. 이것을 피하기 위해, 우리는 gravity를 낮춘다. 속도에서의 알려진 변화때문에, 우리는 공식을 이용하여 "현실적인" 중력 값을 처리할 수 있다:



여기에서 g_normal은 너가 재현해야 할 중력이다. 이것은 대부분의 게임에서 10m/s^2일 것이다 (그리고 이것은 earth gravity이다. 시뮬레이션 다른 곳에서 사용되어질 일반 중력이 아니다. 그리고 그 일반 중력은 일반적으로 더 높다). 우리의 bullet example에 대해, 우리는 0.5 m/s^2의 g_bullet을 갖는다.

4.1.2 Implementation
ballistic demo는 너에게 4가지 무기의 선택을 준다. a pistol, artillery piece, fireball, 그리고 laser gun (스크린의 마지막에 이름으로 알려지는) 너가 클릭을 할 때, 새로운 공이 발사된다. 새로운 공을 만들고 발사하는 코드는 이것처럼 보인다:

각 무기가 파티클을 다른 값의 세트로 설정하는 것에 유의해라. 그 주변 코드는 여기에서 간결함을 위해 생략된다?: 너는 변수들과 데이터 유형들이 어떻게 그리고 어디에서 정의됐는지를 보기위해 소스코드를 참고할 수 있다.

물리 업데이트 코드는 이렇게 보인다:

그것은 차례로 각 파티클에서 적분기를 호출한다. 그것이 그 파티클을 업데이트 한 후에, 그것은 파티클이 높이가 0보다 아래인지를 보기위해 체크한다. 그 경우에 그것은 제거된다. 그 파티클은 또한 만약 그것이 발사 지점으로부터 긴 거리에 있다면 (100m) 또는 그것이 5초 이상 비행 중에 있다면 제거 되어질 것이다.

실제 게임에서, 너는 그 발사체가 어떤 것과 충돌했는지를 보기 위해 collision detection system의 어떤 종류를 사용할 것이다. 부가적인 게임 로직이 타겟 캐릭터의 hit points를 줄이기 위해 또는 표면에 bullet-hole graphic을 더하기 위해 사용되어질 수 있다.

우리는 이 단계에서 세부 충돌 모델이 없기 때문에, 각 발사체에서 에너지의 효과를 보여주는 것은 어렵다. 이 책의 나중 파트에서 충돌과 접촉과 결합 될 때, 이것은 명백하다. 나는 오브젝트들에 쏘는 것을 포함하는 bigballistics라고 불리는 demo 버전을 제공한다. 그리고 이것은 full physics engine을 사용하여 재현된다. 너는 명백히 이 버전에서 다른 유형의 발사체로 impacts의 현실감을 볼 수 있다.




#include "GPED_Projectiles.h"
#include <glfw3.h>

GPEDProjectile::GPEDProjectile()
{
 for (unsigned int i = 0; i < MAX_PROJECTILE_NUM; ++i)
  ammo[i].type = UNUSED;

 currentShotType = PROJECTILE_BULLET;
}

void GPEDProjectile::fire()
{
 if (ammo_cursor >= MAX_PROJECTILE_NUM)
  ammo_cursor = 0;

 AmmoRound& proj = ammo[ammo_cursor];
 switch (currentShotType)
 {
 case PROJECTILE_BULLET:
  proj.particle.setMass(2.f); // 2.0kg
  proj.particle.setVelocity(glm::vec3(0.f, 0.f, 35.f)); // 35m/s
  proj.particle.setAcceleration(glm::vec3(0.f, -1.f, 0.0f));
  proj.particle.setDamping(0.99f);
  break;
 case PROJECTILE_ARTILLERY:
  proj.particle.setMass(200.0f); // 200.0kg
  proj.particle.setVelocity(0.0f, 30.0f, 40.0f); // 50m/s
  proj.particle.setAcceleration(0.0f, -20.0f, 0.0f);
  proj.particle.setDamping(0.99f);
  break;
 case PROJECTILE_FIREBALL:
  proj.particle.setMass(1.0f); // 1.0kg - mostly blast damage
  proj.particle.setVelocity(0.0f, 0.0f, 10.0f); // 5m/s
  proj.particle.setAcceleration(0.0f, 0.6f, 0.0f); // Floats up
  proj.particle.setDamping(0.9f);
  break;
 case PROJECTILE_LASER:
  // Note that this is the kind of laser bolt seen in films,
  // not a realistic laser beam!
  proj.particle.setMass(0.1f); // 0.1kg - almost no weight
  proj.particle.setVelocity(0.0f, 0.0f, 100.0f); // 100m/s
  proj.particle.setAcceleration(0.0f, 0.0f, 0.0f); // No gravity
  proj.particle.setDamping(0.99f);
  break;
 }

 proj.particle.setPosition(0.f, 1.5f, 0.f);
 proj.type = currentShotType;
 proj.startTime = glfwGetTime();

 // Clear the force accumulators
 proj.particle.clearAccumulator();

 ++ammo_cursor;
}

void GPEDProjectile::update(float duration)
{
 for (unsigned int i = 0; i < MAX_PROJECTILE_NUM; ++i)
 {
  if (ammo[i].type == UNUSED) continue;

  ammo[i].particle.integrate(duration);

  // manual check for world collision 
  glm::vec3 position = ammo[i].particle.getPosition();
  position = glm::min(glm::max(position, glm::vec3(-50.f, 0.f, -50.f)), glm::vec3(50.f, 50.f, 50.f));
  ammo[i].particle.setPosition(position);

  if (ammo[i].startTime + 5.f < glfwGetTime())
   ammo[i].type = UNUSED;
 }
}
(잘 되고 있는지 보기 위해, world 밖으로 나가지 않게 manual check를 해주었다.)

4.2 Fireworks
(개인적으로, 이 파트를 잘 끝내야 물리 엔진 구조를 잘 읽힐 수 있을거라고 생각한다. 예전에 여기하다가, 여러가지 C++ 깊은 내용을 알아야 해서 대충하고 넘어간거 같았다)

우리의 두 번째 예시는 덜 실용적으로 보일지도 모르지만, 대다수의 게임에서 사용되는 파티클 물리의 보통의 프로그램을 보여준다. 불꽃놀이는 폭발, 흐느는 물, 심지어 연기와 불을 보여주기 위해 사용될 수 있는 파티클 시스템의 과시하는 프로그램이다.

firworks demo는 너가 interactive fireworks display를 만들도록 허용해준다. 그림 4.3에서 진행중인 display를 볼 수 있다.

4.2.1 The Fireworks Data
우리의 firworks display에서, 우리는 basic particle structure에 추가 데이터를 더할 필요가 있다. 첫 째로, 우리는 그것이 무슨 종류의 파티클을 나타내는지를 알 필요가 있다. 불꽃놀이는 많은 payload들로 구성된다 : 초기 rocket은 짧은 delay 후에 또 다시 폭발하는 몇 가지 가벼운 mini-firworks로 터뜨려질지도 모른다.

둘 째로, 우리는 파티클의 나이를 알 필요가 있다. 불꽃놀이는 pyrotechnis(불꽃놀이)의 연쇄반응으로 구성된다. 세심하게 시간이 있는 fuse와 함께. 한 로켓은 처음에 그것의 rocket motor를 점화시킬 것이다; 그러고나서, 짧은 비행 시간 후에, 폭발 단계가 폭발함에 따라 그 모터는 타들어 갈것이다. 이것은 추가적인 units을 흩뿌릴지도 모른다. 그리고 이것은 모두 같은 길이의 fuse를 가진다. 그리고 최종 bursts가 대강 동시에 발생하도록 한다 (정확히 같은 시간은 아니다.) 이것을 지원하기 위해, 우리는 각 파티클에 대한 age를 유지하고, 그것을 매 프레임마다 업데이트 시킨다.


class GPEDFirework : public GPEDParticle
{
public:
 unsigned type;
 real age;
};

나는 여기에서 객체지향 방법을 사용하고, Firework structure를 파티클 structure의 subclass로 만들었다. 이것은 내가 원래의 particle definition을 바꾸지 않고 새로운 데이터를 추가하게 해준다.

4.2.2 The Fireworks Rules
전체 fireworks display 효과를 정의하기 위해, 우리는 한 파티클 타입이 다른 것으로 어떻게 변할ㅈ리를 명시할 수 있을 필요가 있다. 우리는 규칙들의 한 세트로서 이것을 한다: 각 firework type에 대해, 우리는 age가 다했을 때 발생할 추가 fireworks를 위해 age와 데이터 셋을 저장한다. 이것은 이 형태를 가진 Rules data structure에서 보유된다:


struct FireworkRule
{
 unsigned type;
 real minAge;
 real maxAge;
 glm::vec3 minVelocity;
 glm::vec3 maxVelocity;
 real damping;

 struct Payload
 {
  unsigned type;
  unsigned count;
 };
 unsigned payloadCount;
 Payload* payloads;
};

규칙들이 코드에서 제공된다. 그래서 그것들은 모든 가능한 fireworks의 행동을 통제하는 한 함수에서 정의된다. 이것은 그 함수의 한 샘플이다:

게임 개발 스튜디오에서, 한 게임에서 파티클들이 어떻게 행동하는지를 결정할 필요가 있는 사람은 종종 art staff이다. 이 경우에, 코드에서 그 규칙들이 정의되도록 하는 것은 불편하다. 많은 개발자들은 텍스트 파일 포맷의 종류를 그들의 엔진에 통합한다. 이것은 level designers 또는 artists들에 의해 만들어진 easy-to-edit particle rules를 읽어들일 수 있다. 몇 몇 개발자들은 한 아티스트가 상호적으로 파티클의 행동을 WYSIWYG 환경에서 조절할 수 있는 특정한 도구들을 만들기도 한다. 이 도구는 그러고나서 그 규칙들을 정의하는 몇 가지 파일 포맷을 저장하고, 그 엔진에 의해 나중에 읽혀진다.

4.2.3 The Implementation
매 프레임에서, 각 firework는 그것의 나이를 업데이트 시키고, 규칙에 의해 점검되어진다. 만약 그것의 나이가 threshold를 넘어간다면, 그러면 그것은 제거될 것이고, 좀 더 많은 fireworks가 그것의 자리에서 만들어질 것이다. (그 연쇄 반응의 마지막 단계는 더 많은 불꽃놀이를 만들지 않는다).

physics를 수행하기 위해 particle update function을 사용하여, 그 firework update function은 이제 이것처럼 보인다:

firework가 그것의 components들로 폭발 할 때 만약 우리가 어떤 여분의 firework slots이 없다면,  모든 새로운 fireworks가 계산될 것은 아니라는 것을 유의해라. 다시 말해서, 리소스들이 tight할 때, 더 늙은 fireworks는 우선순위를 얻는다.

이것은 처리 될 불꽃놀이의 개수에 hard limit을 넣게 해준다. 그리고 이것은 상황이 바쁠 때 physics가 느려지게 하는 것을 피할 수 있다. 많은 개발자들은 그들의 엔진에서 다른 전략을 사용한다: 그들은 새롭게 생성된 파티클에 우선순위를 주고, 오래된 파티클들을 제거한다. 이것은 fireworks demo에서 다소 덜 기쁜 효과를 준다. 그래서 나는 그것을 피했다.

실제로 새로운 불꽃놀이를 만드는 코드는 이것처럼 보인다:


void create(GPEDFirework* firework, const GPEDFirework* parent = NULL) const
{
 firework->type = type;
 firework->age = crandom.randomReal(minAge, maxAge);

 glm::vec3 vel(real(0.0));
 if (parent)
 {
  // The position and velocity are based on the parent
  firework->setPosition(parent->getPosition());
  vel += parent->getVelocity();
 }
 else
 {
  glm::vec3 start(real(0.0));
  int x = (int)crandom.randomInt(3) - 1;
  start.x = real(5.0) * real(x);
  firework->setPosition(start);
 }

 vel += crandom.randomVector(minVelocity, maxVelocity);
 firework->setVelocity(vel);
 firework->setMass(1);
 firework->setDamping(damping);
 firework->setAcceleration(glm::vec3(0, -10, 0)); // gravity
 firework->clearAccumulator();
}


void GPED::FireworksDemo::create(unsigned type, const GPEDFirework * parent)
{
 // Get the rule needed to create this firework.
 FireworkRule* rule = rules + (type - 1);

 // Create the firework.
 rule->create(fireworks + nextFirework, parent);

 // Increment the index for the next firework
 nextFirework = (nextFirework + 1) % maxFireworks;
}

fireworks가 스폰될 때, 그것들은 그것들의 파티클 properties set을 갖는다. 그리고 랜덤 컴포넌트로 속도가 결정된다.

내가 몇 가지 firework types에 대해 높은 damping values를 사용한 것에 유의해라. 이것은 그것들이 땅에서 느리게 표류하도록 한다. 그리고 이것은 특히 폭발하기전에 공중에서 걸려있을 필요가 있는 fireworks에 대해 중요하다.

매 프레임 마다, 현재 활성화된 모든 fireworks는 업데이트된다. 이것은 처음에 firework가 처리되었는지를 보기위해 체크하는 간단한 loop에 의해 수행된다. (0인 fireworks는 비활성화된 것으로 정의된다).


void GPED::FireworksDemo::update(float duration)
{
 // Find the duration of the last frame in seconds
 for (GPEDFirework* firework = fireworks; firework < fireworks + maxFireworks; ++firework)
 {
  // Check if we need to process this firework
  if (firework->type > 0)
  {
   // Does it need removing?
   if (firework->update(duration))
   {
    // Find the appropriate rule
    FireworkRule* rule = rules + (firework->type - 1);

    // Delete the current firework (this doens't affect its
    // position and velocity for passing to the create function
    // just whether or not it is processed for rendering or
    // physics
    firework->type = 0;

    // Add the payload
    for (unsigned i = 0; i < rule->payloadCount; ++i)
    {
     FireworkRule::Payload* payload = rule->payloads + i;
     create(payload->type, payload->count, firework);
    }
   }
  }
 }
}

이러한 코드 fragments들은 fireworks demo에서 취해진다. 너는 새로운 fireworks를 발시시키기 위해 숫자 키를 사용하여 너만의 fireworks display를 만들 수 있다.

정확히 같은 종류의 파티클 시스템이 많은 게임 엔진에서 사용된다. 파티클의 중력을 매우 낮은 값으로 설정하거나, 중력이 어떤 파티클의 종류를 위로 잡아당기게 하여, 우리는 smoke, fire, flowing water, explosions, sparks, rain, 그리고 많은 많은 효과들을 만들 수 있다.

각 파티클의 유형 사이의 차이점은 간단히 렌더링 중의 하나이다. 파티클들은 스크린에서 3차원 모델로서라기 보다 보통 평평한 bitmap으로 그려진다. 이것은 내가 데모에서 사용한 접근법이다.

대부분의 production particle system은 또한 파티클들이 회전하는 것을 허용한다. 우리가 이 책에서 full 3차원 회전을 다루지는 않을 것이지만 스크린 rotation을 다룰 것이다. 각 파티클 bitmap이 스크린에서 같은 방형으로 그려지지 않게하기 위해서. 시간에 대해 이 회전변화를 갖는 것은 유용할 수 있다. 나는 이 책에서 그 기법을 구현하려고 하지 않을 것이다. 파티클에 상수 속도 회전을 추가하는 것은 상대적으로 쉬운 tweak이다. 그래서 나는 그것을 그것이 필요한 사람들을 위한 exercise로 남길 것이다.

4.3 Summary
파티클 물리엔진은 주로 특수 효과에 적합하다 - 이름 그대로, 발사체 무기의 탄도학과 파티클 시스템 그리고 폭발을 위한 시각적 효과들.

이 챕터에서, 우리는 fireworks를 렌더링하기 위해 파티클 시스템을 사용했다. 또한 수십 가지의 다른 사용법들이 있다. 대부분의 게임들은 작동하는 몇 가지 종류의 파티클 시스템을 갖는다 (종종 main physics engine과는 완전히 별개로). 중력, 저항, 그리고 초기속도에 대해 다른 특성으로 파티클들을 설정하여, 흐르는 물부터 연기까지, 파이어볼부터, fireworks까지 모든 것을 재현하는 것이 가능하다.

그러나, 결국, 단일의 파티클들은 충분하지 않을 것이다. 우리는 full 3차원 오브젝트가 필요할 것이다. 이 책의 part 2에서, 우리는 오브젝트를 재현하는 한 가지 방법을 볼 것이다: 스프링, 막대, 그리고 케이블로 연결된 파티클들의 구조를 구성함으로써. 이러한 구조를 다루기 위해서, 우리는 파티클에 대한 중력보다 좀 더 많은 힘들을 고려할 필요가 있다. Chapter 5는 이것을 소개한다.


{
파티클시스템, 그리고 firework 시스템은 무언가 내게 조금 어려웠었다.
오브젝트들 간의 연쇄작용을 고려해서 코딩을 짜는 것이기 때문이다.
사실 코딩을 짠 것도 아닌, 이 책의 저작자가 쓴 것을 거의 카피하고 이해하고 활용하는
건데도 구현하기 까지 조금 애를 먹었다. 그래서 이참에 구현 로직에 대해서 정리를 해보자.

firework를 만들려면 한 particle이 터졌을 때 다른 particle도 터지게끔 해야 한다.

그래서 particle의 age를 설정하여, age가 0보다 아래가 된다면
그 파티클의 위치를 기반으로 또 다른 파티클을 만들어야 한다.
여기에서 저작자는 payload라는 structure를 통하여 그것을 달성했다.
payload는 어떤 유형의 particle이, 그리고 몇 개만큼 터질지 알려주는 거였다.
그리고, 그 parent particle의 위치를 기반으로 터지게 했다. 그래야
그 폭죽이 가다가 거기서 터진 것처럼 보이기 때문이다.

그래서
Initial particle Spawn
-> Initial particle age 0
-> Payload System work (payload type, count, parent[initial particle])
-> create additional particle
이 부가적인 파티클을 만드는 것은 업데이트 루프에서 활성화된다.

처음에 만들어지는 것은 create(type, number, NULL(parent없음)) 으로 만들어지고
이 만들어진 것중에서 age가 0으로 떨어진 것 만이 위의 부가적인 파티클 생성로직에 들어간다.


void GPED::FireworksDemo::render(Shader * shader, glm::mat4& view, glm::mat4& proj)
{
 shader->use();
 shader->setMat4("view", view);
 shader->setMat4("projection", proj);

 glm::mat4 model(1.0);
 for (GPEDFirework* firework = fireworks; firework < fireworks + maxFireworks; ++firework)
 {
  if (firework->type > 0)
  {
   model = glm::mat4(1.0);
   model = glm::translate(model, firework->getPosition());
   model = glm::scale(model, glm::vec3(0.5f));
   shader->setMat4("model", model);

   switch (firework->type)
   {
   case 1: shader->setVec3("particleColor", glm::vec3(0.5f, 0, 0)); break;
   case 2: shader->setVec3("particleColor", glm::vec3(0.65f, 0.32f, 0.71f)); break;
   case 3: shader->setVec3("particleColor", glm::vec3(0.1868f, 0.32f, 0.68f)); break;
   case 4: shader->setVec3("particleColor", glm::vec3(0.08f, 0.698f, 0.1695f)); break;
   case 5: shader->setVec3("particleColor", glm::vec3(0.42f, 0.77f, 0.348f)); break;
   case 6: shader->setVec3("particleColor", glm::vec3(0.42f, 0.88f, 0.92f)); break;
   case 7: shader->setVec3("particleColor", glm::vec3(0.63f, 0.21f, 0.1f)); break;
   case 8: shader->setVec3("particleColor", glm::vec3(0.0f, 0.58f, 0.42f)); break;
   case 9: shader->setVec3("particleColor", glm::vec3(0.98f, 0.88f, 0.f)); break;
   }

   
   renderQuad();
  }
 }
}
Shader는 간단하게, type에다 quad의 particleColor를 설정해서 그리게 했다.

particle system을 이용해서,
smoke, fire, flowing water, explosions, sparks, rain 의 system을 따로 구현하도록 해야겠다.
이것은 게임의 디테일을 살려주며 여러가지 쓸 곳이 많을 것이다.
각각에 대해 smokeParticleSystem, fireParticleSystem 등등 을 만들어서
게임 제작시에 쉽게 클래스를 이용하게끔 하면 될 것 같다.
그래서 뭐 회전이나, 텍스쳐를 입히거나 그런것들을 이용해서
좀 더 그럴듯한 파티클 시스템을 만드는 것에 대해 알아볼 필요가 있다.
특히 텍스쳐를 입혀서 그럴듯 하게 만드는게 중요하다.

}







댓글 없음:

댓글 쓰기