OpenGL Game Tutorial
Visual C++ 6.0



Sky Box - vytváření oblohy a krajiny


Příklad a zdroják ke stažení (678k)

Princip skyboxu je jednoduchý. Je to obyčejná krychle, složená ze šesti textur, které na sebe navzájem navazují.

Při vytváření skyboxu jsem přišel na nepříjemný efekt. Tam, kde textury na sebe navazovaly, se vytvořila čára. To se mi nelíbilo a proto jsem předělal původní obrázky tak, že jsem kolem každého vytvořil černý rámeček o velikosti jednoho pixelu. Každý čtverec skyboxu jsem pak posunul blíž ke kameře tak, aby černé rámečky zmizely.

Skybox musí být dostatečně veliký, aby se do něj vešla rozlehlá scéna. Pokud se však zadá větší hodnota mezi nejbližším (near=1) a nejvzdálenějším (far=500) bodem, které jsou definované ve funkci gluPerspective(), tak Depth Buffer začne zlobit a rozvlní textury opět tam, kde jsou spojeny. To znamená, že skybox musí být veliký maximálně 500x500x500 jednotek.

Další fígl. Skybox chodí s kamerou, to znamená, že se nemůžeme dostat blízko ke stěně skyboxu, nebo dokonce mimo něj.

No dost řečí a mrknem se na zdroják. Všechny funkce jsou obsaženy ve třídě KObject. Nejdříve si načtem soubor skybox.txt pomocí funkcí LoadObject() a ReadLine()

//----------------------------------------------------------------------------------------------------//

void ReadLine(FILE *f,char *s)                      // Čti řádek ze souboru f a ukládej do řetězce s
{
    do                                              // Cykluj
      {
        fgets(s, 255, f);                           // Ćti znak po znaku maximálně však 255
      } while ((s[0] == '/') || (s[0] == '\n'));    // Dokud se nedostaneš na konec řádky

    return;                                         // Vrať funkci hodnotu načteného řádku
}


//----------------------------------------------------------------------------------------------------//

TGLObject LoadObject(char *name, TGLObject *k)            // Načti objekt se souboru (name)
{
    int     ver;                                          // Pomocná proměnná pro počet vrcholů objektu
    GLfloat  rx,ry,rz;                                    // Pomocná proměnná pro X, Y, Z pozici vrcholu objektu
    FILE  *filein;                                        // Definice souboru, ktery budeme číst
    char  oneline[255];                                   // Definice řádku souboru s maximálně 255 znaky

    if ((filein = fopen(name, "rt"))== NULL)              // Zkus otevřít soubor pro čtení v textovem módu "rt"
    {
                                                          // Jestli se to nezdaří, vyhoď chybovou hlášku
          MessageBox(NULL,"Cannot open input file","Error",MB_OK | MB_ICONSTOP);
     }
    else                                                  // Jinak pokračuj v práci s načteným souborem
    {
      ReadLine(filein,oneline);                           // Volej funkci pro načtení řádky ze souboru
      sscanf(oneline, "Vertices: %d\n", &ver);            // Přečti z první řádky počet vrcholů a výsledek předej proměnné ver
      k->points = new TPoint3d[ver];                      // Dynamicky alokuj paměť pro počet vrcholů objektu
      k->verts = ver;                                     // Načtenou hodnotu počtu vrcholů předej vrcholům objektu

      for (int i=0;i<ver;i++)                             // Cykluj podle počtu vrcholů
        {
           ReadLine(filein,oneline);                       // Čti řádek po řádku
           sscanf(oneline, "%f %f %f", &rx, &ry, &rz);    // Najdi v řádce tři čísla typu float a předej je hodnotám rx, ry, rz
           k->points[i].x = rx;                           // Předej bodu X (aktuálniho vrcholu) hodnotu rx
           k->points[i].y = ry;                           // Předej bodu Y (aktuálního vrcholu) hodnotu ry
           k->points[i].z = rz;                           // Předej bodu Z (aktuálního vrcholu) hodnotu rz
        }
      fclose(filein);                                     // Zavři soubor
    }
    return *k;                                            // Vrať funkci hodnoty objektu
}

//----------------------------------------------------------------------------------------------------//

Funkce InitSkyBox() nám pak z načtených hodnot vytvoří skybox (krychli) a posune každý čtverec o hodnotu aprox ke středu (kde bude kamera), aby zmizely čáry mezi texturama na přechodu z jedné textury do druhé.

// Inicializuj Sky Box
void KObject::InitSkyBox(TPoint3d point0,                // Texturová koordinace a pozice bodu 0
                         TPoint3d point1,                // Texturová koordinace a pozice bodu 1
                         TPoint3d point2,                // Texturová koordinace a pozice bodu 2
                         TPoint3d point3,                // Texturová koordinace a pozice bodu 3
                         TPoint3d point4,                // Texturová koordinace a pozice bodu 4
                         TPoint3d point5,                // Texturová koordinace a pozice bodu 5
                         TPoint3d point6,                // Texturová koordinace a pozice bodu 6
                         TPoint3d point7,                // Texturová koordinace a pozice bodu 7
                         int      TextureListNumber0,    // Číslo Listu textury 0
                         int      TextureListNumber1,    // Číslo Listu textury 1
                         int      TextureListNumber2,    // Číslo Listu textury 2
                         int      TextureListNumber3,    // Číslo Listu textury 3
                         int      TextureListNumber4,    // Číslo Listu textury 4
                         int      TextureListNumber5,    // Číslo Listu textury 5
                         int      ObjectListNumber)      // Číslo Listu objektu

{
       float aprox = 4.0f;     // Hodnota přiblížení čtverců skyboxu. Čtverce se přibližují, aby zmizely spáry mezi nimi

       // Nastav původní nastavení pozice bodů 
       TPoint3d origpoint0 , origpoint1, origpoint2, origpoint3, origpoint4, origpoint5, origpoint6, origpoint7;
       origpoint0 = point0;
       origpoint1 = point1;
       origpoint2 = point2;
       origpoint3 = point3;
       origpoint4 = point4;
       origpoint5 = point5;
       origpoint6 = point6;
       origpoint7 = point7;


       // Nastav texturové koordinace
       TPoint2d texcoord0, texcoord1, texcoord2, texcoord3;
       texcoord0.x = 1.0f;                                             // Texturová koordinace bodu 0
       texcoord0.y = 1.0f;
       texcoord1.x = 1.0f;                                             // Texturová koordinace bodu 1
       texcoord1.y = 0.0f;
       texcoord2.x = 0.0f;                                             // Texturová koordinace bodu 2
       texcoord2.y = 0.0f;
       texcoord3.x = 0.0f;                                             // Texturová koordinace bodu 3
       texcoord3.y = 1.0f;



       glNewList(ObjectListNumber,GL_COMPILE);                                        // Vytvoř Display List podle daného Object Display Listu


          // Čelní stěna krychle
          // Posuň celý čtverec blíž ke kameře, aby zmizely spáry
          glBindTexture(GL_TEXTURE_2D, Texture1.TextureList[TextureListNumber0]);     // Nastav texturu podle daného Texture Display Listu
          point0.z -= aprox;
          point1.z -= aprox;
          point2.z -= aprox;
          point3.z -= aprox;
          // Vykresli čtverec
          DrawTriangleFan(texcoord0, point0,          // Texturová koordinace a pozice bodu 0
                          texcoord1, point1,          // Texturová koordinace a pozice bodu 1
                          texcoord2, point2,          // Texturová koordinace a pozice bodu 2
                          texcoord3, point3);         // Texturová koordinace a pozice bodu 3
          // Resetuj na původní hodnoty pozice bodů
          point0 = origpoint0;
          point1 = origpoint1;
          point2 = origpoint2;
          point3 = origpoint3;



          // Pravá stěna krychle
          // Posuň celý čtverec blíž ke kameře, aby zmizely spáry
          glBindTexture(GL_TEXTURE_2D, Texture1.TextureList[TextureListNumber1]);     // Nastav texturu podle daného Texture Display Listu
          point3.x -= aprox;
          point2.x -= aprox;
          point4.x -= aprox;
          point5.x -= aprox;
          DrawTriangleFan(texcoord0, point3,          // Texturová koordinace a pozice bodu 3
                          texcoord1, point2,          // Texturová koordinace a pozice bodu 2
                          texcoord2, point4,          // Texturová koordinace a pozice bodu 4
                          texcoord3, point5);         // Texturová koordinace a pozice bodu 5
          // Resetuj na původní hodnoty pozice bodů
          point3 = origpoint3;
          point2 = origpoint2;
          point4 = origpoint4;
          point5 = origpoint5;



          // Zadní stěna krychle
          // Posuň celý čtverec blíž ke kameře, aby zmizely spáry
          point5.z += aprox;
          point4.z += aprox;
          point6.z += aprox;
          point7.z += aprox;
          glBindTexture(GL_TEXTURE_2D, Texture1.TextureList[TextureListNumber2]);     // Nastav texturu podle daného Texture Display Listu
          DrawTriangleFan(texcoord0, point5,          // Texturová koordinace a pozice bodu 5
                          texcoord1, point4,          // Texturová koordinace a pozice bodu 4
                          texcoord2, point6,          // Texturová koordinace a pozice bodu 6
                          texcoord3, point7);         // Texturová koordinace a pozice bodu 7
          // Resetuj na původní hodnoty pozice bodů
          point5 = origpoint5;
          point4 = origpoint4;
          point6 = origpoint6;
          point7 = origpoint7;




          // Levá stěna krychle
          // Posuň celý čtverec blíž ke kameře, aby zmizely spáry
          point7.x += aprox;
          point6.x += aprox;
          point1.x += aprox;
          point0.x += aprox;
          glBindTexture(GL_TEXTURE_2D, Texture1.TextureList[TextureListNumber3]);     // Nastav texturu podle daného Texture Display Listu
          DrawTriangleFan(texcoord0, point7,          // Texturová koordinace a pozice bodu 6
                          texcoord1, point6,          // Texturová koordinace a pozice bodu 7
                          texcoord2, point1,          // Texturová koordinace a pozice bodu 0
                          texcoord3, point0);         // Texturová koordinace a pozice bodu 1
          // Resetuj na původní hodnoty pozice bodů
          point7 = origpoint7;
          point6 = origpoint6;
          point1 = origpoint1;
          point0 = origpoint0;




          // Horní stěna krychle
          // Posuň celý čtverec blíž ke kameře, aby zmizely spáry
          point7.y -= aprox;
          point0.y -= aprox;
          point3.y -= aprox;
          point5.y -= aprox;
          glBindTexture(GL_TEXTURE_2D, Texture1.TextureList[TextureListNumber4]);     // Nastav texturu podle daného Texture Display Listu
          DrawTriangleFan(texcoord0, point7,          // Texturová koordinace a pozice bodu 7
                          texcoord1, point0,          // Texturová koordinace a pozice bodu 0
                          texcoord2, point3,          // Texturová koordinace a pozice bodu 3
                          texcoord3, point5);         // Texturová koordinace a pozice bodu 5
          // Resetuj na původní hodnoty pozice bodů
          point7 = origpoint7;
          point0 = origpoint0;
          point3 = origpoint3;
          point5 = origpoint5;




          // Dolní stěna krychle
          // Posuň celý čtverec blíž ke kameře, aby zmizely spáry
          point1.y += aprox;
          point6.y += aprox;
          point4.y += aprox;
          point2.y += aprox;
          glBindTexture(GL_TEXTURE_2D, Texture1.TextureList[TextureListNumber5]);     // Nastav texturu podle daného Texture Display Listu
          DrawTriangleFan(texcoord0, point1,          // Texturová koordinace a pozice bodu 1
                          texcoord1, point6,          // Texturová koordinace a pozice bodu 2
                          texcoord2, point4,          // Texturová koordinace a pozice bodu 4
                          texcoord3, point2);         // Texturová koordinace a pozice bodu 6
          // Resetuj na původní hodnoty pozice bodů
          point1 = origpoint1;
          point6 = origpoint6;
          point4 = origpoint4;
          point2 = origpoint2;


       glEndList();                                        // Konec Display Listu 1
}

Funkce pro načítání a iniciaci skyboxu provádí funkce LoadSkyBox()

int KObject::LoadSkyBox( char *name,                      // Cesta k souboru *.txt obsahující pozice bodů krychle
                         int   TextureListNumber0,        // Číslo Listu textury 0
                         int   TextureListNumber1,        // Číslo Listu textury 1
                         int   TextureListNumber2,        // Číslo Listu textury 2
                         int   TextureListNumber3,        // Číslo Listu textury 3
                         int   TextureListNumber4,        // Číslo Listu textury 4
                         int   TextureListNumber5,        // Číslo Listu textury 5
                         int   ObjectListNumber)          // Číslo Listu objektu
{
     int Status=FALSE;                     // Kontrolu stavu načítaní nastav na FALSE

     if (LoadObject(name))                 // Pokud pude načíst objekt se souboru (name) tak
     {
       Status=TRUE;                        // Kontrolu stavu načítání nastav na TRUE
       InitSkyBox( point[0],               // Texturová koordinace a pozice bodu 0
                   point[1],               // Texturová koordinace a pozice bodu 1
                   point[2],               // Texturová koordinace a pozice bodu 2
                   point[3],               // Texturová koordinace a pozice bodu 3
                   point[4],               // Texturová koordinace a pozice bodu 4
                   point[5],               // Texturová koordinace a pozice bodu 5
                   point[6],               // Texturová koordinace a pozice bodu 6
                   point[7],               // Texturová koordinace a pozice bodu 7
                   TextureListNumber0,     // Číslo Listu textury 0
                   TextureListNumber1,     // Číslo Listu textury 1
                   TextureListNumber2,     // Číslo Listu textury 2
                   TextureListNumber3,     // Číslo Listu textury 3
                   TextureListNumber4,     // Číslo Listu textury 4
                   TextureListNumber5,     // Číslo Listu textury 5
                   ObjectListNumber);      // Číslo Listu objektu
     }

     return Status;                         // Vrať funkci stav načítání 
}

Funkce DrawSkyBox() vykresluje vytvořený Display List objektu a nastaví skybox, aby se pohyboval s kamerou.

void KObject::DrawSkyBox(int ObjectListNumber)          // Vykresli skybox
{
     glPushMatrix();                                    // Začátek prostorové matice
       glTranslatef(Camera1.Position.x, Camera1.Position.y, Camera1.Position.z);   // Pohybuj skyboxem podle kamery
       glCallList(ObjectListNumber);                    // Vykresli List
     glPopMatrix();                                     // Konec prostorové matice
}

Funkce DrawSkyBox() je volána v nekonečné smyčce funkcí DrawScene()

void DrawScene(void)                    // Vykresli scénu
{
       Camera1.Look();                  // Pohybuj a rotuj kamerou
       SkyBox1.DrawSkyBox(3);           // Vykresli Sky Box (3 = ObjectList3)
       Cube1.DrawRotateCube(1);         // Vykresli rotující krychli (1 = ObjectList1)
       Cube2.DrawCube(2);               // Vykresli statickou krychli (2 = ObjectList2)
       DrawFps();                       // Vykresli FPS
}

To je vše.
Děkuji DigiBen ovi, od kterého jsem si vypůjčil skybox obrázky.

Příště zkusíme načíst objekty, nebo scénu z formátu výborného 3D editoru MilkShape




Home