DirectX (26.)

Je tu další pokračování kurzu o Direct3D! V první části dnešní lekce se budeme podrobněji věnovat texturování objektů v 3D scéně. V druhé sekci uděláme takovou menší přípravu pro příští lekci, kde se budu věnovat optimalizacím při vykreslování. Přidáme tedy do projektu nový projekt Input, který využijeme pro ovládání kamery použité v příští lekci.

26.1. Texturování 2

26.1.2. Adresovací režimy

Ve 21. lekci DirectX jsme si něco málo o texturování pověděli. Řekli jsme si, že objekt, na který chceme nanést texturu musí být složen z vertexů obsahující alespoň jednu sadu texturových souřadnic. Také jsme si pověděli, jak tyto souřadnice fungují. Rozsah hodnot je obyčejně od 0.0 do 1.0. Hodnotami mimo tento rozsah můžeme vytvořit některé speciální efekty. Tak zvaným adresovacím režimem (texture address mode) určíme, jakým způsobem se textura nanáší v těchto speciálních případech.
V D3D existují 4 adresovací režimy (AR):

  1. Wrap AR
  2. Mirror AR
  3. Clamp AR
  4. Border color AR

Adresovací režim nastavíme pomocí metody SetTextureStageState(), která má tři parametry: číslo stage (o stage už jsme si něco málo povídali a více se dovíte dále v této lekci), parametr, který chceme měnit (v našem případě D3DTSS_ADDRESSU, D3DTSS_ADDRESSV nebo D3DTSS_ADDRESSW - znamená to, že můžeme pro každý směr nastavit jiný adresovací režim) a nakonec zadáváme hodnotu:  D3DTADDRESS_WRAPD3DTADDRESS_MIRROR, D3DTADDRESS_CLAMP nebo D3DTADDRESS_BORDER.

1. Wrap AR

Pokud nastavíte tento adresovací režim, textura se nanese přesně tak jak bychom čekali. Pokud například zadáte souřadnice pro čtverec tak, že první vertex má (0.0, 0.0), druhý (0.0, 3.0), třetí (3.0, 0.0) a čtvrtý (3.0, 3.0), nanese se textura následovně:

Textura se tedy nanese třikrát v obou směrech a nepřevrací se.

2. Mirror AR

Předpokládejme stejný čtverec jako předcházejícím příkladě. Zde se první řada nanese stejně, druhá se zrcadlově otočí a třetí je zase stejná. Nastavili jsme zrcadlení i ve svislém směru, takže i sloupce jsou otočeny.

Kdybychom nastavili pouze vodorovné zrcadlení pomocí parametru D3DTSS_ADDRESSU, otočila by se jen prostředí řada a všechno ostatní by zůstalo jako v prvním případě.

3. Clamp AR

Nyní použijeme stejný čtverec, ale jinou texturu. Tento režim pracuje tak, že jednou se nanese textura, jak bychom čekali a na zbytek čtverce se roztáhnou okrajové pixely textury:

A opět lze nastavit tento režim v jednom směru, takže k roztažení pak dojde například jen ve svislém směru a ve vodorovném se může textura opakovat nebo zrcadlit.

4. Border color AR

A máme tu poslední možný režim, který má mnoho společného s předcházejícím, proto použijeme stejnou texturu. Opět se nanese textura na pozice (0.0,0.0) až (1.0,1.0) (jako bychom použili rozsah 0.0 až 1.0) a na zbytek se nanese požadovaná barva! Na obrázku vidíte tento režim v akci:

Barvu, která se použije v tomto případě nastavíme opět pomocí metody SetTextureStageState(), ale použijeme parametr D3DTSS_BORDERCOLOR, poslední parametr je samotná barva v RGBA formátu.

Pokud nenastavíte explicitně žádný adresovací režim, je nastaven wrap AR! Všechny popsané režimy si ukážeme v příkladu na závěr lekce.

26.1.2. Míchání textur

Princip

V další části se budeme věnovat technice zvané míchání textur (angl. texture blending), která se často používá pro vytvoření zajímavých efektů. Zde se také konečně dozvíte, k čemu je potřeba více sad texturových souřadnic. V Direct3D můžete smíchat až 8 textur najednou, zároveň tím tedy zoptimalizujete vykreslování scény - pro každou texturu se použije jiná sada souřadnic. Mícháním textur navíc můžete docílit zajímavých efektů jako jsou stíny, různé světla atd. D3D používá pro míchání tzv. stupně (angl. stages). O těch už tu byla také řeč. Při výpočtu se do každého stupně posílají dva parametry (například ze zdrojové textury) a na těchto argumentech se provede požadovaná operace. Výsledek se uloží do následujícího stupně, takže vzniká kaskáda:

Barevné šipky znamenají vstupní parametry každého stupně, černá šipka je výstup. Na obrázku je vidět použití čtyř stupňů, ale v D3D jich můžete použít až osm (samozřejmě to záleží také na tom, co dovede váš hardware). Takto původní textura probublává přes stage 0 až 3. Na každé stage se míchá s texturou nebo s barvou vertexu (zde se bere buď difuzní nebo specular složka viz. Argumenty).

V DirectX SDK 8.1 je příklad MFC Tex, který demonstruje všechny možné kombinace míchání textur. V programu můžete nastavit libovolné argumenty a libovolný operátor pro každý stupeň, zvolit texturu, barvu atd. Proto míchání textur nezahrnu do příkladu z dnešní lekce, ale v budoucnu to jistě použijeme.

Argumenty

Každému stupni  lze přiřadit dva argumenty, které ovlivňují barevný nebo alpha kanál výsledné textury (to znamená, že se bude pracovat s barvou textury nebo s alpha kanálem). Uvedu zde argumenty, které budeme později používat v našem programu:

D3DTA_CURRENT
- použije se výsledek operace z předešlého stupně. Stupeň 0 nemá žádný předchozí, tudíž má tento argument stejný význam jako D3DTA_DIFFUSE.

D3DTA_DIFFUSE
- použije se difuzní barva interpolovaná od okolních vertexů (viz. první lekce, kde jsme měli trojúhelník, jehož vertexy měly každý jinou barvu, pak byla barva pixelů mezi interpolována). Pokud vertexy neobsahují difuzní složku, použije se hodnota 0xFFFFFFFF.

D3DTA_SPECULAR
- funguje stejně jako předcházející parametr, jen se pracuje se specular barvou vertexů

D3DTA_TEXTURE
- jako argument se použije barva textury, která je nastavena pro tento stupeň pomocí metody SetTexture()

D3DTA_TFACTOR
- jako argument se použije  vlastní hodnota (texture factor) předem zadaná pomocí metody SetRenderState() s parametrem  D3DRS_TEXTUREFACTOR.

D3DTA_TEMP
- pomocný registr, kam můžete uložit výsledek nějaké operace a použít ho například v následující stupni nebo i později! Pokud se rozhodnete pomocný registr používat, měli byste zjistit, zda-li ho cílový hardware podporuje pomocí příznaku D3DPMISCCAPS_TSSARGTEMP.

Argumenty nastavíme pomocí metody SetTextureStageState() s parametrem stupně a hodnotou určující, který argument chceme změnit. Tyto hodnoty jsou následující: D3DTSS_COLORARG0, D3DTSS_COLORARG1, D3DTSS_COLORARG2, D3DTSS_ALPHAARG0, D3DTSS_ALPHAARG1, D3DTSS_ALPHAARG2 a D3DTSS_RESULTARG. Před chvilkou jsem říkal, že se do každého stupně posílají pouze dva parametry a tady máme pro barevnou i pro alpha složku tři argumenty!  Třetí argument se nastavuje jen pro speciální operace, které vyžadují tři parametry (drtivá většina vyžaduje pouze dva). Posledním parametrem D3DTSS_RESULTARG určíme, zda-li se výsledek operace uloží pro následující stupeň (D3DTA_CURRENT) nebo do pomocného registru (D3RTA_TEMP). Poslední stupeň nesmí mít nastaven výstup do pomocného registru.
Nastavený argument můžete zjistit metodou GetTextureStateState(). Tato metoda má stejné parametry, jen poslední parametr je ukazatel, na jehož hodnotu se uloží výsledek metody.

Příklad:

Chceme-li například nastavit, že výsledek 2. stupně se má uložit do pomocného registru, zavoláme metodu s parametry:

lpDevice->SetTextureStageState(2, D3DTSS_RESULTARG, D3RTA_TEMP);

Nebo, chceme-li smíchat texturu s barvou vertexů, použijeme příkazy:

lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3RTA_TEXTURE);
lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3RTA_DIFFUSE);

Zkrátka nastavíme jako vstupní parametry texturu a difuzní barvu vertexů. Ještě jsme samozřejmě nenastavili operaci, o těch se dozvíte v další části.

Doplňující příznaky argumentů
- tyto parametry se mohou kombinovat s výše uvedenými hodnotami:

D3DTA_ALPHAREPLICATE
- zkopíruje se hodnota alpha kanálu na všechny barevné kanály.

D3DTA_COMPLEMENT
- použije se doplňková hodnota argumentu. Například je-li argument hodnota X, při výpočtu se použije hodnota 1 - X.

Operátory

Implicitně jsou všechny stupně kromě prvního vypnuty tzn., že nemají nastavenou operaci. Pokud používáme pro různé objekty různý počet stupňů, musíme nepotřebné stupně vypnout (viz. dále). Nemusíme ovšem vypínat všechny, stačí jen první nepoužitý, ostatní se pak rovněž nepoužijí.

Pokud nastavíte operaci pro určitý stupeň, musíte také zajistit správné parametry pro tento stupeň. Chcete-li nastavit operaci (můžete pro barevný nebo pro alpha kanál), opět použijete metodu SetTextureStageState(), ale s parametrem D3DTSS_COLOROP nebo D3DTSS_ALPHAOP. Následuje výčet operací (opět zde uvedu pouze ty, které budeme používat):

D3DTOP_DISABLE
- tato hodnota samozřejmě nevyjadřuje žádnou operaci, jen určí, že výstup tohoto stupně je vypnut. Jak bylo zmíněno výše, pokud je například 3. stupeň vypnut, následující stupně se rovněž nepoužijí.

D3DTOP_SELECTARG1
- operace pouze zkopíruje hodnotu argumentu 1 na výstup. Vhodné například pro 0. stupeň, když vstupní texturu nechceme upravovat, jen ji poslat do dalšího stupně a tam například smíchat s jinou texturou.

D3DTOP_SELECTARG2
- analogicky stejné jako předchozí parametr, jen pracujeme s druhým parametrem. Následují trochu zajímavější operace.

D3DTOP_MODULATE
- modulace spočívá v jednoduchém vztahu:

S = Arg1 x Arg2

Je to prosté násobení vstupních parametrů.

D3DTOP_MODULATE2X
- modulace s dvojitým zesvětlením:

S = (Arg1 x Arg2) << 1

Výsledek modulace je ještě vynásoben dvěma.

D3DTOP_MODULATE4X
- modulace se čtyřnásobným zesvětlením:

S = (Arg1 x Arg2) << 2

Výsledek modulace je vynásoben čtyřmi.

D3DTOP_ADD
- sčítání vstupních parametrů

S = Arg1 + Arg2

D3DTOP_ADDSIGNED
- sčítání vstupních parametrů se zajištěním výstupu v rozsahu -0,5 až 0,5

S = Arg1 + Arg2 - 0,5

D3DTOP_ADDSIGNED2X
- výsledek je stejný jako předchozí hodnota, navíc zde dojde k bitovému posunu doleva čili k násobení dvěma

S = (Arg1 + Arg2 - 0,5) << 1

D3DTOP_SUBTRACT
- odečítání druhého parametru od prvního

S = Arg1 - Arg2

D3DTOP_ADDSMOOTH
- sečtení obou parametrů a poté odečtení jejich násobku

S = Arg1 + Arg2 - Arg1 x Arg2 = Arg1 + Arg2(1 - Arg1)

D3DTOP_BLENDDIFFUSEALPHA,D3DTOP_BLENDTEXTUREALPHA, D3DTOP_BLENDFACTORALPHA a D3DTOP_BLENDCURRENTALPHA
- tyto parametry jsou velice užitečné! Míchání textury s použitím alpha informace buď z alpha složek difuzní barvy vertexů (D3DTOP_BLENDDIFFUSEALPHA) nebo se bere alpha z textury nastavené v tomto stupni (D3DTOP_BLENDTEXTUREALPHA) nebo se vezme skalární hodnota (D3DTOP_BLENDFACTORALPHA - použití viz. výše) anebo se vezme alpha z předešlého stupně (D3DTOP_BLENDCURRENTALPHA).

S = Arg1 x Alpha + Arg2 x (1 - Alpha)

Pomocí poslední operace můžeme bez problému řídit prolínání dvou textur buď pomocí faktoru nebo pomocí alpha informace z vertexů, takže můžete docílit postupného prolínání a podobně.

Příklad:

Pokračujme ve výše uvedeném příkladu a přidejme operaci:

lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3RTA_TEXTURE);
lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3RTA_DIFFUSE);

lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE2X);

Takto vytvoříme efekt, kde se nastavená textura a barva vertexů moduluje tzn. násobí! Doporučuji si zkusit výše uvedený příklad obsažený v DirectX SDK 8.0 i 9.0. K problému texturování se ještě jednou vrátíme v některé z příštích lekcí.

26.2. Projekt Input a rozhraní ID3DXSprite

V druhé části dnešní lekce vložíme nový projekt Input. S tímto projektem jsme se již několikrát setkali v minulých lekcích. Nyní ho použijeme pro ovládání kamery, ale to až v příští lekci.. Většina projektu zůstane nezměněná, pouze pro vykreslování kurzoru pochopitelně nemůžeme použít DirectDraw. Proto použijeme rozhraní ID3DXSprite, které nahrazuje 2D sprite z DirectDraw.

Tento sprite vytvoříme funkcí knihovny D3DX D3DXCreateSprite(), která má pouze dva parametry a to ukazatel na zařízení a výstupní parametr - samotný ukazatel na rozhraní spritu. Dále použijeme členské funkce Begin(), End() a Draw(). Metodou Begin() připravíme zařízení pro vykreslení spritu. Metodou End() naopak vykreslování ukončíme. Poslední uvedená metoda Draw() má řadu parametrů (podobně jako funkce Blt() v DirectDraw), které si podrobněji rozebereme:

  1. Ukazatel na zdrojovou texturu (IDirect3D8Texture*)

  2. Ukazatel na zdrojový obdélník, ze kterého se vykresluje

  3. Ukazatel na 2D vektor určující měřítko v x a y ose. Pokud je NULL, použije se vektor (1.0, 1.0), což znamená, že měřítko výsledného obrázku se nemění.

  4. Ukazatel na 2D vektor, kterým můžeme určit střed otáčení (pokud použijeme následující parametr). Střed je brán vůči bodu 0.0, 0.0 samotného spritu, což je také implicitní nastavení, pokud zadáte NULL.

  5. Reálná hodnota úhlu v radiánech, o který se sprite otočí (podle středu určeného předcházejícím parametrem).

  6. Ukazatel na 2D vektor určující polohu spritu na monitoru.

  7. Barva spritu, kterou je zdrojová textura modulována (viz. předcházející část lekce). Můžete tak docílit různých efektů.

Jak jsem zmínil, my použijeme tento objekt pro kurzor. V předešlé verzi knihovny Input, jsme vytvořili třídu CCursor, která měla vlastní povrch CSurface. Nyní to bude sprite ID3DXSprite a textura XTexture. Zde nastává následující problém. Velikost textury je vždy násobkem dvou (16, 32, 64, 256 atd), takže i když máme bitmapu o velikosti 320x200, bude vytvořena textura o velikosti 512x256 (nejbližší vyšší formát). Pokud se pak z této textury snažíme vykreslovat například animovaný kurzor, musíme pečlivě přepočítat zdrojové souřadnice na souřadnice v textuře a poté výsledný obrázek správně zvětšit či zmenšit, aby měl rozměr jako zdrojová bitmapa. To není v některých případech vůbec možné, neboť přesnost integeru je omezena a zdrojový obdélník je bohužel zadáván čtyřmi integer hodnotami. Z toho plynou docela vysoké požadavky na velikost zdrojové bitmapy a na počet animačních kroků. Ideální je například případ, kdy velikosti bitmapy a textury jsou stejné (například pokud má bitmapa rozměry 512x32 bude mít i textura z této bitmapy vytvořená stejné rozměry) a pak šířka této textury je dělitelná počtem animačních kroků. Zdá se to trochu složité, že? Zkrátka si zkuste vydělit skutečnou šířku textury počtem animačních kroků a měla by vám vyjít šířka políčka (například 32 pixelů) nebo alespoň celé číslo. Je-li tedy textura široká 1024 pixelů a chceme políčko 32 pixelů, je vhodné vytvořit 32 animačních kroků (32 x 32  = 1024).

Podívejme se na upravenou třídu CCursor:

class INPUT_API CCursor
{
    BOOL m_bInit;
    int m_iWidth;
    int m_iHeight;

    LPD3DXSPRITE m_d3dCursor;
    LPDIRECT3DDEVICE8 m_lpDevice;
    XTexture * m_lpTexture;

    int m_iPhase;
    int m_iSpeed;
    int m_nPhases;
    DWORD m_dwOldTime;

public:
    HRESULT Create(LPCSTR csIntPath, int cx, int cy, LPDIRECT3DDEVICE8 lpDevice);
    HRESULT Create(LPCSTR csIntPath, int cx, int cy, int nSpeed, int iPhases, LPDIRECT3DDEVICE8 lpDevice);
    HRESULT Update(D3DXVECTOR2* ptCursor);

public:
    CCursor();
    ~CCursor();
};

Zde se téměř nic nezměnilo. Jen přibyl atribut spritu a CSurface je vyměněn za XTexture. Nakonec se trochu změnily parametry u metod.

Zajímavější budou metody Create() a Update():

HRESULT CCursor::Create(LPCSTR csIntPath, int cx, int cy, LPDIRECT3DDEVICE8 lpDevice)
{
    DWORD dwRet = 1;
    if(!m_bInit && lpDevice) {

        m_iWidth = cx;
        m_iHeight = cy;
        m_lpDevice = lpDevice;

      
 //
        // Try to create the sprite

        dwRet = D3DXCreateSprite(m_lpDevice, &m_d3dCursor);
        if(dwRet != ERROR_SUCCESS) {
            TRACE("Cannot create the sprite due", dwRet);
            return dwRet;
        }
      
 //        
      
 // Create texture
        m_lpTexture = new XTexture;
        dwRet = m_lpTexture->LoadTextureFromFile(csIntPath, lpDevice);
        if(dwRet != ERROR_SUCCESS) {
            ERR("Cannot create surface for cursor due", dwRet);
            return dwRet;
        }
        m_bInit = true;
        dwRet = ERROR_SUCCESS;
    }
    return dwRet;
}

Zde za prvé vytváříme objekt D3DXSprite a za druhé nahráváme textury z bitmapy. Zbytek metody je stejný.

HRESULT CCursor::Create(LPCSTR csIntPath, int cx, int cy, int iSpeed, int iPhases, LPDIRECT3DDEVICE8 lpDevice)
{
    DWORD dwRet = 1;
    if(!m_bInit) {
      
 //
        // Call standard creation

        dwRet = Create(csIntPath, cx, cy, lpDevice);
        if(dwRet == ERROR_SUCCESS) {
            m_iPhase = 0;
            m_iSpeed = iSpeed;
            m_nPhases = iPhases;
           
//
            // Ok

            m_bInit = true;
        }
    }
    return dwRet;
}

Druhá verze metody Create() pro vytváření animovaných kurzorů je trochu zjednodušená, protože má jeden nový parametr, který se dřív počítal uvnitř metody. Jedná se o parametr iPhases, který představuje počet animačních kroků. Nejsme schopni totiž toto číslo spočítat ze šířky textury, protože toto číslo se může odlišovat od skutečné šířky bitmapy.

HRESULT CCursor::Update(D3DXVECTOR2* ptCursor)
{
    CRect rcDest, rcSrc;
    DWORD dwRet = 1, newTime;
    int iHeight;
    D3DXVECTOR2 vecScaling(1.0f, 1.0f);
    if(m_bInit) {
  
     //
        // For static cursors

        if(m_nPhases == 0) {
       
    //
            // One static place

            rcSrc.top = 0;
            rcSrc.left = 0;
            rcSrc.right = m_iWidth;
            rcSrc.bottom = m_iHeight;
        }
  
     //
        // Compute move source rectangle

        else {
     
      //
            // Compute phase, increment phase if is time

            newTime = GetTickCount();
       
    //
            // Get time from last update

            if((newTime - m_dwOldTime) > (DWORD)m_iSpeed) {
               
//
                // If it right time, increment phase

                m_iPhase++;
                //
                // Check to overflow phases
                if(m_iPhase == m_nPhases) {
                    m_iPhase = 0;
                }
                //
                // Save time of last update
                m_dwOldTime = newTime;
            }
            // Compute width and height of each field on the texture
            iHeight = m_lpTexture->Height();
            double fWidth = double(m_lpTexture->Width()) / double(m_nPhases);
            //
            // Compute rectangle from current phase
            //
            // Static vertical postion
            rcSrc.top = 0;
            rcSrc.bottom = iHeight;
            // Variable horizontal pos.
            rcSrc.left = m_iPhase * fWidth;
            rcSrc.right = rcSrc.left + fWidth;
            vecScaling.x = double(m_nPhases * m_iWidth) / double(m_lpTexture->Width());
        }
        //
        // Blit cursor to back surface
        m_d3dCursor->Begin();
        dwRet = m_d3dCursor->Draw(m_lpTexture->GetTexture(), rcSrc, &vecScaling, NULL, NULL, ptCursor, -1);
        m_d3dCursor->End();
    }
    return dwRet;
}

Nakonec tu máme metodu Update(), která kurzor vykreslí. Metoda se opět liší jen v několika maličkostech. Za prvé je třeba spočítat šířku políčka na textuře. Tato hodnota se může lišit od šířky kurzoru, protože šířka bitmapy a textury nemusí být stejná (viz. problém výše). Poté můžeme spočítat zdrojový obdélník a použijeme právě tuto šířku. Nakonec je potřeba určit měřítko výsledného obrázku tak, aby byl vykreslený kurzor správně veliký (v našem případě široký). Jednoduše podělíme šířku bitmapy a šířku textury. Pro obě varianty kurzoru platí poslední část, samotné vykreslení pomocí metody Draw(). Metody Begin() a End() bychom zde volat ani nemuseli, neboť jsou tyto metody volány interně v metodě Draw().

Zbytek projektu Input zůstane stejný.

26.3. Příklad

Na závěr lekce trochu upravíme projekt Tester a vyzkoušíme si adresovací režimy. Zahodíme trávu z minulé lekce a vytvoříme trochu zajímavější texturu:

Vytvoříme globální proměnnou, do které si budeme ukládat aktuální adresovací režim:

//
// Address mode
DWORD g_dwAddressMode;
 

Inicializace čtverce bude vypadat takto:

g_Quad.Init(g_theDisplay.GetDevice(), "smile.bmp", 5, g_theDisplay.GetTextureFormat(), 4 * sizeof(VERTEX), 6 * sizeof(WORD), IB_WRITEONLY|VB_WRITEONLY);
g_Quad.Visible(FALSE);
g_dwAddressMode = D3DTADDRESS_WRAP;
FillBuffers();

Dále musíme upravit metodu FillBuffers(), aby generovala správné texturové souřadnice:

pVertices[0].tu1 = 0.0f;
pVertices[0].tv1 = 0.0f;

pVertices[1].tu1 = 3.0f;
pVertices[1].tv1 = 0.0f;

pVertices[2].tu1 = 0.0f;
pVertices[2].tv1 = 3.0f;

pVertices[3].tu1 = 3.0f;
pVertices[3].tv1 = 3.0f;

Takto se textura bude opakovat 3x v obou směrech. Na úplný závěr upravíme metodu UpdateFrame():

// Get old addr mode
g_theDisplay.GetDevice()->GetTextureStageState(0, D3DTSS_ADDRESSU, &dwOldAdrMode);
//
// Change address mode
if(g_theInput.IsKeyDown(DIK_Q))
{
    g_dwAddressMode = D3DTADDRESS_WRAP;
}
if(g_theInput.IsKeyDown(DIK_W))
{
    g_dwAddressMode = D3DTADDRESS_MIRROR;
}
if(g_theInput.IsKeyDown(DIK_E))
{
    g_dwAddressMode = D3DTADDRESS_CLAMP;
}
if(g_theInput.IsKeyDown(DIK_R))
{
    g_dwAddressMode = D3DTADDRESS_BORDER;
    g_theDisplay.GetDevice()->SetTextureStageState(0, D3DTSS_BORDERCOLOR, 0x00FF0000); // red color
}
// Change address mode
g_theDisplay.GetDevice()->SetTextureStageState(0, D3DTSS_ADDRESSU, g_dwAddressMode);
g_theDisplay.GetDevice()->SetTextureStageState(0, D3DTSS_ADDRESSV, g_dwAddressMode);
g_Quad.Draw(D3DPT_TRIANGLELIST, 0, 4, 0, 2);
g_theDisplay.GetDevice()->SetRenderState(D3DRS_LIGHTING, TRUE);
// Restore addr modes
g_theDisplay.GetDevice()->SetTextureStageState(0, D3DTSS_ADDRESSU, dwOldAdrMode);
g_theDisplay.GetDevice()->SetTextureStageState(0, D3DTSS_ADDRESSV, dwOldAdrMode);

Za prvé je třeba zjistit aktuální texturovací režim, který na konci zase obnovíme. Dále reagujeme na čtveřici kláves Q, W, E a R, pomocí nichž přepínáme adresovací režimy. Nezapomeňte vždy definovat barvu u adresovacího režimu D3DTADDRESS_BORDER. Těsně před vykreslením čtverce nastavíme nový adresovací režim, čtverec vykreslíme a obnovíme původní nastavení.

26.4. Závěr

Jak bylo nakousnuto v úvodu, v příští lekci se budeme zabývat kamerou. Vytvoříme novou třídu XCamera v projektu Display, která se bude starat o všechno kolem pohybu kamery v prostoru. Až budeme mít kameru, budeme se moci pustit do složitějších vykreslovacích operací. Mám na mysli například terén apod.

Jiří Formánek