Lekce 07 - Formát TGA
Předmluva
Dnes si řekneme něco o dalším z mnoha formátů obrázkových souborů, konkrétně o formátu Targa (TGA). Nečekejte však naprosto vyčerpávající popis struktury, ten ostatně naleznete zde http://www.root.cz/clanky/opengl-27-tga/ doporučuji přečíst! TGA je pro nás zajímavé především tím že umožňuje ukládat kanál alfa (průhlednost) a také je možné ho komprimovat metodou RLE. Dále potom může (ale nemusí) obsahovat barevnou paletu, její typ atd.. . Vzhledem k různorodosti tohoto formátu jsem se zaměřil na jeho základní strukturu. Tzn. pouze data obrázku v komprimované nebo nekomprimované podobě, bez dalších rozšíření. Přiložené soubory jsou upraveny v programu Photoshop.
Vytváření textur z TGA je v podstatě stejné jako u formátu BMP, pouze si musíme data po načtení trochu přeorganizovat. TGA ukládá standartně barevnou složku v pořadí BGR (modrá, zelená, červená), ale OGL pracuje v režimu RGB. Musíme si tedy do programu dopsat cyklus na vzájemné prohození složek červené a modré. Pokud to neuděláte nic se nestane, pouze budete mít prohozené barvy.
Alfa kanál (pokud je v souboru uložen) nám umožňuje nastavit průhlednost jednotlivých barevných složek za pomoci "Blendingu". Může dosahovat hodnoty 0 až 1 (single) nebo 0 až 254 (byte). Pokud je nula znamená ůplnou průhlednost (přesneji neviditelnost) a jedna úplnou neprůhlednost. V případě typu Byte je to stejné. Takže hodnota 0,5 Single odpovídá hodnotě 127 Byte.
Využití alfa kanálu z TGA si popíšeme někdy příšte, zatím stačí že ho umíme načíst a víme o něm.
Nekomprimované TGA
Nejdříve si načteme hlavičku. Tzn. prvních 18 bytů souboru, z nichž nás teď bude zajímat jen 12. Ty porovnáme s následujícím polem ( 0,0,2,0,0,0,0,0,0,0,0,0) . třetí byte může mít hodnotu "2" (soubor není komprimován) nebo "10" (soubor je komprimován). Pokud porovnání nesouhlasí tak se nejedná o TGA soubor nebo má specifikaci se kterou ještě neumíme pracovat. Pokud se jedná o nekomp. soubor, pokračujeme dále.
Hlavičku uložíme třeba do pole Header(17) as Byte
Šířka obrázku je rozdělena do dvou bytů (12 a 13). Nižší byte může nabývat 256 hodnot (8 bitů), takže vynásobíme vyšší byte 256 a k němu přičteme nižší. Width = Header(13) * 256 + Header(12)
Výšku obrázku získáme podobně, ta je uložena v bytech (14 a 15) Height = Header(15) * 256 + Header(14)
Barevná hloubka v bitech na pixel je uložena v (16) Bpp = Header(16) a musí být 24 nebo 32. Pokud je 32 tak obrázek obsahuje alfa kanál.
A nakonec barevnou hloubku v bytech získáme dělením barevné hloubky v bitech na pixel osmi.
BytesPerPixel = Header(16) / 8
Ještě nám tu zůstal byte (17) ten obsahuje velikost identifikačního pole. Je to oblast na konci souboru kam si může libovolná aplikace ukládat svoje "poznámky" do max. počtu znaků 255 (1 byte). To jen pro úplnost.
Pokud vše proběhlo v pořádku (předpokládám že si ošetříte případné chyby, nulová šířka, výška apod.) načteme všechna data obrázku ovšem již bez hlavičky. Nejdříve si definujeme pole pro uložení. Potřebnou velikost zjistíme vynásobením šířky, výšky a barevnou hloubkou v bytech. Načteme data, prohodíme barevné složky a při generování textury budeme rozlišovat zda obrázek obsahuje alfa kanál nebo ne. Zdrojový kód je pro lepší přehlednost uveden až na konci článku jako celek.
Komprimované TGA
Zase si nejdříve načteme hlavičku. Pokud je třetí byte z prvních dvanácti roven deseti (a všechny ostatní nule) jedná se o soubor kompr. metodou RLE. Postup pro zjištění všech potřebných informací je stejný jako jako u nekomp. souboru. Při načtení dat obrázku ovšem musíme postupovat trochu jinak. Nejdříve definujeme pomocné pole pro uložení dat ze souboru. Potřebnou velikost ovšem musíme zjistit pomocí fce FileLen, protože načítáme kompr. data a ještě odečteme hlavičku tj. - 18 bytů. Dále si definujeme pole pro uložení dekomp. obrázku. Jeho velikost už spočítat umíme, šířka * výška * barevná hloubka v bytech.
Dim FileData() as Byte, MemData() as Byte, size as Long
size = FileLen(Soubor) - 18
ReDim FileData(size)
size = Width * Height * BytesPerPixel
ReDim MemData(size)
Open Soubor For Binary as #1
Get #1, 19 , FileData
Close #1
Data máme v paměti a jdeme dekomprimovat. Načteme první hlavičkový byte FileData(0) z našeho dočasného pole. Pozor neplést s hlavičkou souboru! Pokud je hodnota v něm uložená menší nebo rovna 127 jedná se o RAW hlavičku (nekomprimovaná část) a určuje počet pixelů plus jedna které následně načteme. Pokud je byte hlavičky větší než 127, potom se jedná o RLE data (komprimovaná část) a určuje počet pixelů mínus 127, které se budou v dekomp.obrázku opakovat. Po těchto datech následuje další hlavičkový byte atd.
Pozor!!!
1. Při dekompresi si musíte dát pozor zda je barevná hloubka v byte 3 nebo 4 a podle toho následně upravit cyklus.
2. Vždy si ošetřete případné chyby v obrázku. Muže se stát, že obrázek bude poškozen nebo špatně uložen vy se budete snažit číst za rozsah dočasného pole.
Stuktura zdrojového kódu:
1 Načteme hlavičku souboru
2 Jedná se o TGA? Pokud ano pokračujeme, jinak opustime fci
3 Zjistíme zda je soubor komprimován.
4 Spočítáme si roměry atd.
5.1 Pokud není komprimován
-> načteme data
-> prohodíme pořadí barevné složky - pokud obsahuje alfa kanál jdeme po 4 jinak po 3
-> vytvoříme texturu s ohledem na přítomnost alfa kanálu
5.2 Pokud je komprimován
-> načteme data
-> provedeme dekomp. a zároveň prohodíme pořadí barevné složky - pokud obsahuje alfa kanál jdeme po 4 jinak po 3
-> vytvoříme texturu s ohledem na přítomnost alfa kanálu
6. Otřeme si pot z čela.
'################################################################
'## Soubor = cesta k souboru s platnym obrazkem TGA
'## Textures_ID_TGA() = dynamicke pole Long, provede ReDim Preserve + 1 a na predposledni index vlozi texturu
'## pokud se texturu nepodari vytvorit vraci False
'#################################################################
Public Function LoadTexturesTGA(ByVal mFile As String, ByRef Textures_ID_TGA() As Long) As Boolean
Dim Header(17) As Byte 'pole pro ulozeni hlavicky
Dim Test_Header 'pomocne pole pro porovnani hlaicky
Dim FileData() As Byte 'pole pro ulozeni dat ze souboru
'nasleduji pomocne promenne pouzite pri dekompresi
Dim MemData() As Byte 'pomocne pole
Dim chHeader As Byte 'hlavickovy byte
Dim AF As Long 'aktualni pozice v komprimovanem poli
Dim AM As Long 'aktualni pozoce v docasnem poli
'tohle netreba popisovat
Dim Height As Long, Width As Long, Bpp As Byte, BytesPerPixel As Integer, ImageSize As Long
'jeste nejake pomocne promenne
Dim Nr As Long, M As Long, j As Long, mFileLen As Long, Compress As Boolean
On Error GoTo Chyba
'nacteme hlavicku
Nr = FreeFile
Open mFile For Binary As #Nr
Get #Nr, , Header
Close #Nr
' porovname prvnich 12 bytu a overi zda se jedna o platny soubor Targa a zda je komprimovan
Select Case Header(2)
Case 2 'RAW Header - nekomprimovana data
Test_Header = Array(0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0)
For j = 0 To 11
If Header(j) <> Test_Header(j) Then GoTo Chyba
Next
Compress = False
Case 10 'RLE Header - komprimovana data
Test_Header = Array(0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0)
For j = 0 To 11
If Header(j) <> Test_Header(j) Then GoTo Chyba
Next
Compress = True
Case Else 'unknown Header - neplatna nebo neznama data
GoTo Chyba
End Select
'provedeme vypocty
Width = Header(13) * 256 + Header(12)
Height = Header(15) * 256 + Header(14)
Bpp = Header(16)
BytesPerPixel = Bpp / 8
ImageSize = CLng(Width * Height * BytesPerPixel)
'overime platnost vypoctu
If Height <= 0 And Width <= 0 Then GoTo Chyba
If Bpp <> 24 And Bpp <> 32 Then GoTo Chyba
'pokud je vse v poradku pripravime si potrebna pole
'(nezapomente ze dolni mez pole je nula)
mFileLen = FileLen(mFile) - 18
ReDim FileData(mFileLen)
ReDim MemData(ImageSize)
'pokracujeme podle komrese
Select Case Compress
Case False 'RAW file - soubor neni komprimovan
'nacteme data obrazku, bez hlavicky, tzn. velikost minus 18 bytu, takze zaciname na 19
Nr = FreeFile
Open mFile For Binary As #Nr
Get #Nr, 19, FileData
Close #Nr
'prohodime poradi berevne slozky
'pokud neobsahule alfa kanal jdeme po 3 (R,G,B)
If BytesPerPixel = 3 Then 'No alpha
For j = 1 To ImageSize Step 3
MemData(j - 1) = FileData(j + 1)
MemData(j) = FileData(j)
MemData(j + 1) = FileData(j - 1)
Next
Else
'pokud ho obsahuje tak jdeme po 4 (R,G,B + Alpha)
For j = 1 To ImageSize Step 4 'With alpha
MemData(j - 1) = FileData(j + 1)
MemData(j) = FileData(j)
MemData(j + 1) = FileData(j - 1)
MemData(j + 2) = FileData(j + 2)
Next
End If
'mame nacteno a prohozeno, skocime na vytvoreni textury
'_______________________________________________________
'
Case True 'RLE file - soubor je komprimovan
'nacteme data
Nr = FreeFile
Open mFile For Binary As #Nr
Get #Nr, 19, FileData
Close #Nr
'nastavime si aktualni pozice v komp. a dekomp. poli
AM = 0: AF = 0
'dekomprimujeme a zaroven prohazujeme barevnou slozku, ovsem bez alfa kanalu
'takze jdeme zase po 3 (R,G,B)
If BytesPerPixel = 3 Then 'No alpha
Do While AM < ImageSize
'prvni hlavickovy byte
chHeader = FileData(AF)
AF = AF + 1
'pokud je mensi nebo roven 127, jedna se o nekomp. cast
'nacteme tedy pocet pixelu jenz odpovida hodnote v chHeader plus 1
'zaciname od nuly j=0 do hodnoty chHeader, takze mame vlastne chHeader + 1
If chHeader <= 127 Then 'RAW block
For j = 0 To chHeader
'prohazujeme barevnou slozku
MemData(AM) = FileData(AF + 2)
AM = AM + 1
MemData(AM) = FileData(AF + 1)
AM = AM + 1
MemData(AM) = FileData(AF)
AF = AF + 3: AM = AM + 1
Next
Else
'pokud je chHeader vetsi nez 127, snizime hodnotu v nem ulozenou o 127
'snizena hodnota urcuje kolikrat se bude nasledujici barevna slozka
'v obrazku opakovat
For j = 1 To chHeader - 127 'RLE block
'prohazujeme barevnou slozku
MemData(AM) = FileData(AF + 2)
AM = AM + 1
MemData(AM) = FileData(AF + 1)
AM = AM + 1
MemData(AM) = FileData(AF)
AM = AM + 1
Next
AF = AF + 3
End If
'pokracujeme dokud nenacteme cely obrazek
Loop
'_________________________________________________
's kanalem alfa je to stejne jako predchozi cast
'pouze postupujeme po 4 (R,G,B + Alpha)
Else 'with alpha
Do While AM < ImageSize
chHeader = FileData(AF)
AF = AF + 1
If chHeader <= 127 Then 'RAW block
For j = 0 To chHeader
MemData(AM) = FileData(AF + 2)
AM = AM + 1
MemData(AM) = FileData(AF + 1)
AM = AM + 1
MemData(AM) = FileData(AF)
AM = AM + 1
MemData(AM) = FileData(AF + 3)
AF = AF + 4: AM = AM + 1
Nextv
Else
For j = 1 To chHeader - 127 'RLE block
MemData(AM) = FileData(AF + 2)
AM = AM + 1
MemData(AM) = FileData(AF + 1)
AM = AM + 1
MemData(AM) = FileData(AF)
AM = AM + 1
MemData(AM) = FileData(AF + 3)
AM = AM + 1
Next
AF = AF + 4
End If
'pokracujeme dokud nenacteme cely obrazek
Loop
End If
End Select
'mame pripravena data a jdeme vytvaret techturu
'tato cast je stejna jako u BMP
j = UBound(Textures_ID_TGA)
ReDim Preserve Textures_ID_TGA(j + 1)
glGenTextures 1, Textures_ID_TGA(j)
glBindTexture glTexture2D, Textures_ID_TGA(j)
glTexParameteri glTexture2D, tpnTextureMagFilter, GL_LINEAR
glTexParameteri glTexture2D, tpnTextureMinFilter, GL_LINEAR
'zde oznamit OGL jako barevnou hloubku bytech chceme pouzivat 3 nebo 4
'a jaky barevny format predavame - bez alfa kanalu je to RGB a s alfa kanalem RGBA
glTexImage2D glTexture2D, 0, BytesPerPixel, Width, Height, 0, IIf(BytesPerPixel = 3, GL_RGB, GL_RGBA), GL_UNSIGNED_BYTE, MemData(0)
'pokud vse probehlo v poradku, opustime fci
LoadTexturesTGA = True
Exit Function
'sem bychom se nemeli dostat
Chyba:
LoadTexturesTGA = False
Exit Function
End Function
zdrojové kódy ke staženi ZDE
Po dnešku již umíme vytvářet textury nejen z BMP, ale také z TGA. V budoucnu se ještě naučíme používat již zmiňovaný alfa kanál. Přiště si řekneme co je to Display List a proč ho máme rádi :)
Dodatek
Vzhledem k časové náročnosti OpenGL a problematikou s tím spojenou, není v mojich silách vytvářet ke každému článku orig. projekt se zaměřením na dané téma. Takže si musíte vytvořit vlastní projekt a v něm použít získané informace. Přiložený kód je vytvořen jako třída (*.cls), jedná se o první verzi, ktrerou hodlám postupně dolaďovat a rozšiřovat.
Pokud chcete pomoci tak momentálně se snažím analyzovat formát souboru *.MAP, používaný v editorech Worldcraft, Valve Hammer Editor aj. což jsou editory levelů pro Quake3, HL, HL2 apod.. Pokud k němu máte nějaké informace v češtině tak se ozvěte.
V příprave:
Výpis textu na obrazovku
Načítání textur ze souboru zdrojů *.RES
Alpha blending
Vylepšená třída clsWindow
Vylepšená třída clsCamera - aneb jak na matice transformací
Bodové a prostorové osvětlení
Mlha (základní)
sid26@seznam.cz
|