OpenGL (15.) - Ucelený částicový systém - pokračování

V minulém článku jsem popsal, jak by měl vypadat takový manager částicového systému. Dnes budu pokračovat v tom, jak vytvořit samotný systém.

Particle System

Tak jsem napsal první článek o částicovém systému, ale ještě jsem se ani nezmínil co to částice (particles) vůbec jsou. Jedná se vlastně o čtverec nebo jiný tvar, na který naneseme černobílou texturu hvězdy, kapky deště nebo, zapneme průhlednost tak, aby černé kraje textury byly průhledné a prostředek, kde je textura bílá necháme neprůhledný, popř. ho dobarvíme.

Částicový systém je systém, který se stará o částice. Vytváří je, počítá jejich pozici, barvu a po uplynutí jejich životnosti je vymaže.

Manager se stará o částicové systémy. Je-li potřeba, vytvoří nový nebo uvolní starý. K jednotlivým systémům přistupujeme přes ParticleManager.

Samotný částicový systém má trochu složitější strukturu než jeho manager. Jednotlivé parametry jsou snadno pochopitelné podle názvu: Jméno systému, typ, max. počet částic, aktivita, směr a síla větru, velikost gravitace, pozice systému, směr emitování částic, jejich rychlost a doba jejich viditelnosti, barvy, velikosti a materiál, ze kterého jsou částice složeny. Aby se částice neemitovaly jedním směrem v řadě za sebou, je třeba nastavit také maximální odchylku od zvoleného směru a dobu života částic. Samotné částice jsou uchovány v poli particles.

Takovéto částice si můžete jednoduše vytvořit třeba v Adobe Photoshopu.
type
  ParticleSystemC = class
    private
    public
      Name: string;
      SystemType: TParticleSystemType;
      MaxParticlesInSystem: integer;
      Active: boolean;
      Wind: TAffineVector;
      Gravity: single;

      //Particle physical features:
      Direction: TAffineVector;
      Speed: single;
      LifeTime: single;
      Position: TAffineVector;
      StartColor, EndColor: TColorRGBAs;
      StartSize, EndSize: single;
      Material: MaterialC;

      // K hodnotám direction a lifetime připočítáváme také náhodnou odchylku která
      // je v rozsahu 0..127 - 127=max.
      DirectionError, LifeTimeError: byte;

      Particles: Array of TParticle;
      procedure Render(Camera: CameraC);
      constructor Create;
  end;

Snad bych se jen pozastavil u vlastnosti SystemType. Tou definujeme základní vlastnost částicového systému. psSmoke znamená, že částice budou vždy tvaru čtverce-budou mít stejnou víšku i šířku. psSpark znamená, že budou tvaru obdelníku, kde začátek bude v aktuální pozici částice a konec bude v předešlé pozici částice. To vytvoří efekt odlétajících jisker.

type
  TParticleSystemType = (
    psSpark,                //Jiskry za sebou zanechávají stopu
    psSmoke,                //Částice mají stejnou šířku i výšku
    {..});
Takto vypadá systém bez použití blendingu. Jeho největší výhodou je velká rychlost.

Také už dnes budu pracovat se strukturou částic, tak ji pro jistotu uvedu zde:

type
  TParticle = record
    Position, Direction, OldPosition: TAffineVector;
    EndTime: single;
  end;
Takto může vypadat třeba oheň nebo plyn, který uniká z barelu.

Protože třída ParticleSystemC má jen konstruktor a jednu proceduru pro vykreslení systému, nebudu se dlouho zdržovat a uvádím zde jejich výpis:

constructor KParticleSystemC.Create;
begin
  Active := true;
  SystemType := psSmoke;
  Wind := AffineVectorMake(0,0,0);
  Gravity := 0.01;
  Speed := 2;
  Direction := VectorNormalize(AffineVectorMake(0,1,0.6));
  DirectionError := 30;
  LifeTime := 4;
  LifeTimeError := 127;
  Position := AffineVectorMake(2,0,2);
  StartColor := clrOrange;
  EndColor := clrBlack;
  StartSize := 2;
  EndSize := 2;

  MaxParticlesInSystem := 50;
  setlength(Particles,MaxParticlesInSystem-1);
end;

procedure KParticleSystemC.Render(Camera: KCameraC);
var
  i: integer;
  Time: single;
  u, v, c: TAffineVector;
  W, X, Y, Z: TAffineVector;
  AmbientStart, AmbientEnd, Ambient: TAffineVector;
  size: single;
begin
  Time := GetTickCount/100;
  if (length(Particles) <> MaxParticlesInSystem) then
    SetLength(Particles, MaxParticlesInSystem-1);

  AmbientStart := AffineVectorMake(StartColor.r,StartColor.g,StartColor.b);
  AmbientEnd := AffineVectorMake(EndColor.r,EndColor.g,EndColor.b);



  if (SystemType=psSpark) then begin
    for i:=0 to length(Particles)-1 do
      if (Particles[i].EndTime >= Time) then begin
        size := lerp(StartSize, EndSize, 1-(Particles[i].EndTime-Time)/LifeTime);

        // Získáme směrový vektor
        u := VectorNormalize(
               VectorSubstract(
                 Particles[i].Position,
                 Particles[i].OldPosition));

        // Získáme vektor kamera-částice
        v := VectorNormalize(VectorAdd(
               Particles[i].Position,
               AffineVectorMake(
                 Camera.Position.X,
                 Camera.Position.Y,
                 Camera.Position.Z)));

        // Získáme vektor kolmý k předešlým dvěma vektorům
        c := VectorCrossProduct(u,v);

        NormalizeVector(c);
        ScaleVector(c,size);

        W := VectorAdd(Particles[i].Position, c);
        X := VectorAdd(Particles[i].OldPosition, c);
        Y := VectorSubstract(Particles[i].Position, c);
        Z := VectorSubstract(Particles[i].OldPosition, c);

        Ambient := VectorLerp(AmbientStart, AmbientEnd,1-(Particles[i].EndTime-Time)/LifeTime);
        glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,@Ambient);

        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);

        glBegin(GL_TRIANGLE_STRIP);
          glColor3f(255,0,0);
          glTexCoord2f(0,1);
          glVertex3f(W[0], W[1], W[2]);
          glTexCoord2f(1,1);
          glVertex3f(X[0], X[1], X[2]);
          glTexCoord2f(0,0);
          glVertex3f(Y[0], Y[1], Y[2]);
          glTexCoord2f(0,1);
          glVertex3f(Z[0], Z[1], Z[2]);
        glEnd;
      end;
  end else
  if (SystemType=psSmoke) then begin
    for i:=0 to length(Particles)-1 do
      if Particles[i].EndTime >= Time then begin
        size := lerp(StartSize, EndSize, 1-(Particles[i].EndTime-Time)/LifeTime);

        // Získáme směrový vektor
        u := VectorNormalize(
              VectorSubstract(
                Particles[i].Position,
                Particles[i].OldPosition));

        // Získáme vektor kamera-částice
        v := VectorNormalize(
               VectorAdd(
                 Particles[i].Position,
                 AffineVectorMake(
                   Camera.Position.X,
                   Camera.Position.Y,
                   Camera.Position.Z)));

        // Získáme vektor kolmý k předešlým dvěma vektorům
        c := VectorCrossProduct(u,v);

        NormalizeVector(c);
        ScaleVector(c,size);
        ScaleVector(u,size);

        W := VectorAdd(VectorAdd(Particles[i].Position, c),u);
        X := VectorSubstract(VectorAdd(Particles[i].Position, c),u);
        Y := VectorAdd(VectorSubstract(Particles[i].Position, c),u);
        Z := VectorSubstract(VectorSubstract(Particles[i].Position, c),u);

        Ambient := VectorLerp(AmbientStart, AmbientEnd,1-(Particles[i].EndTime-Time)/LifeTime);
        glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,@Ambient);

        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ZERO);

        glBegin(GL_TRIANGLE_STRIP);
          glColor3f(255,0,0);
          glTexCoord2f(0,1);
          glVertex3f(W[0], W[1], W[2]);
          glTexCoord2f(1,1);
          glVertex3f(X[0], X[1], X[2]);
          glTexCoord2f(0,0);
          glVertex3f(Y[0], Y[1], Y[2]);
          glTexCoord2f(0,1);
          glVertex3f(Z[0], Z[1], Z[2]);
        glEnd;

        glDisable(GL_BLEND);
      end;
  end;
end;

Funkce Lerp(start, stop, t) vypočítá lineární interpolaci mezi body start a stop v bodě 0<t<1. Tuto funkci potřebujeme pro vypočítání velikosti a barvy každé částice. Jak získáme všechny čtyři body částice? To je velmi jednoduché. Stačí vypočítat směrový vektor u (nemůžeme použít vlastnost částice direction, protože v ní není započítáno působení gravitace a větru) odečtením staré pozici částice od pozice nové. Poté vypočítáme vektor v mezi kamerou a danou částicí (Pro zvýšení rychlosti bychom mohli počítat vektor mezi kamerou a pozicí celého systému - dopouštěli bychom se ale toho, že by vzdálenější částice nebyly natočeny kolmo ke kameře) a nakonec vypočítáme (vektorovým součinem) vektor c kolmý k předešlým dvěma vektorům. Jeho využijeme při počítání rohů částice. Všechny potřebné funkce pro práci s vektory jsou k disposici v knihovně geometry.pas.

Teď, když přičteme nebo odečteme vektory c nebo u k souřadnici částice, dostaneme 4 rohy částice. Vynásobíme-li tyto vektory c a u proměnnou size, dostaneme částici buď zvětšenou nebo zmenšenou parametrem size. Tímto docílíme efektu psSmoke. Chceme-li, aby částice byly protáhlé ve směru, kam letí, nepotřebujeme parametr u, ale využijeme staré pozice částice. Změna je pouze v tom, že k pozici a ke staré pozici přičteme a odečteme vektor c.

Zde je vidět, že použití většího počtu menších částic je také velmi pěkné (... ale náročné).

Tento článek má sloužit jako poradce při návrhu vlastního částicového systému. Je napsán tak, aby byla popsána hlavně struktura a základní chování částicového systému. Zbytek si už musí napsat každý sám. Pokud vám nebude něco jasné nebo najdete větší či menší chybu, napište mi to prosím do diskuse nebo na mejl

.

Vyšlo 09.08.2002, v blogu: 0 1 2 3 4 5 6 7 8

Děkuji, že jste se rozhodl(a) přečíst tento článek. Budu rád i za komentář. Pokud Vás tento článek zaujal a rádi byste jej doporučili ostatním, podpořte mně prosím tím, že věnujete minutku svého času a uděláte mi reklamu na linkuj.cz, vybrali.sme.sk či jagg.cz. Přeji příjemné čtení

Poslední články

Diskuse k blogu

licenční klíč 
Nazdárek kámoši,schánim licenční klíč pro NAVIGÁTOR 8,1-TRUCK v creku ho nemohu najit.Nebo spíš neumím ho najít ja jsem totiž jenom blbej řidič kamionu.Proto prosím o pomoc,moc dekuji Mucha Michal 
Vložil: Mucha Michal (27.01.2010 16:15:55)
Přidání příspěvku
©PC-guru.cz 2000-2008 | Optimalizováno pro 1024*768