Grafika hibakezelés és tipikus hibák
A VIK Wikiből
FAQ (avagy a grafika listán a legtöbb levelet generáló "misztikus" hibák)
- Fordításkor ilyen hibát kapok:
error: stray '\357' in program...
- UTF-8 BOM hiba - Az osztást tartalmazó számításom rossz eredményt ad - Egészosztási hiba
- A 3D-s kép zajos, az élek cikk-cakkosak - Z-Fighting
- Sugárkövetéskor a testen fura fekete részek jelennek meg - Önárnyékolás
- Sugárkövetéskor az egész képem fekete/fehér - Tone Map használata
glQuad
használatakor négyszög helyett két egymásba csúszott háromszög jelenik meg - QUAD
Tipikus C++ hibák
- Ha stabil C++ tudással rendelkezel, ezt a részt nyugodtan ugord át
- UTF8 BOM problémák:
- Ha a kódodat UTF-8 kódolással mented, sok szerkesztő a fájl elejére biggyeszt három nem látható karaktert, az úgy nevezett UTF-8 Byte Order Mark-ot. Ez a Gordiuson fordítási hibát fog eredményezni, ezért a kódunkat feltöltés előtt mindenképpen konvertáljuk sima ASCII/ANSI szövegfájllá (Még a Jegyzettömb is képes erre, Mentés másként - ANSI)
- Lokális változók scope-on kívüli használata:
- Figyeljünk rá hogy a lokális változók csak addig léteznek, amíg a scope-on belül járunk
- Ennek megfelelően ne adjuk át őket cím szerint, ha mégis referenciát akarunk készíteni ilyen változóról, használjunk const referenciát!
- Egészosztás véletlen használata:
- A C++-ban az operandusok típusától függ, hogy egészosztás (div) vagy rendes osztás történik
- Ez a működés nehezen detektálható hibákat okozhat, ezért mindig figyeljünk rá! Például ha egész típusú változót osztunk egy számmal, az eredmény egész lesz, hiába akarjuk egy lebegőpontos változóban tárolni:
int a=5; float b=a/10; //értéke nem 0.5 lesz, hanem 0!
- A hiba elkerülése végett használjunk cast-olást vagy típusos konstansokat:
float b=(float)a/10; float b=a/10.0f;
- Hiányzó break a switch szerkezetben
- A C++-ban a switch szerkezetének egyszerre több ága is lefuthat! Ha ezt nem szeretnénk, figyeljünk rá hogy minden ágat zárjunk le a break; paranccsal!
- T&-át váró függvénynek nem lehet egy 'T' temporális változót odadni, de a VisualC++ fordító nem szól emiatt. Pl:
class Foo { Foo(Bar&) {} }; Foo(Bar()) <- Ez nem szabályos, a beadón fordítási hibát ad!! A megoldás: a függvény várjon const Bar&-et, vagy Bar b; Foo(b);
OpenGL hibák
- Az OpenGL sajnos nem túl típusos, így ha egy függvényt rossz paraméterekkel hívunk meg, akkor az általában nem generál fordítási hibát, hanem szimplán csak nem működik.
- Az ilyen hibák megtalálása nagyon nehéz, mert csak annyit látsz, a program nem működik, de nem szól, hogy mivel van gondja.
- Szerencsére az OpenGL készítői gondoltak erre, és az ilyen hibák beállítanak egy flaget amit le lehet kérdezni. Ezt a glGetError() függvénnyel lehet megtenni. A függvény ömagában csak egy számot ad vissza, amit nehéz értelmezni, de itt van egy wrapper, ami emberileg is fogyaszható formába írja ki, hogy mi történt. Ezt persze a beadón nem lehet használni, de az otthoni változatba debuggoláshoz sokat segíthet.
#include <iostream> #include <cstring> #define CheckError() __CheckError(__FILE__, __PRETTY_FUNCTION__, __LINE__) inline void __CheckError(const char *file, const char *func, int line) { GLenum error = glGetError(); if(error != GL_NO_ERROR) { std::cout << "\n---------========={[ "; int err_len; switch(error) { case GL_INVALID_ENUM: std::cout << "GL_INVALID_ENUM"; err_len = strlen("GL_INVALID_ENUM"); break; case GL_INVALID_VALUE: std::cout << "GL_INVALID_VALUE"; err_len = strlen("GL_INVALID_VALUE"); break; case GL_INVALID_OPERATION: std::cout << "GL_INVALID_OPERATION"; err_len = strlen("GL_INVALID_OPERATION"); break; case GL_STACK_OVERFLOW: std::cout << "GL_STACK_OVERFLOW"; err_len = strlen("GL_STACK_OVERFLOW"); break; case GL_STACK_UNDERFLOW: std::cout << "GL_STACK_UNDERFLOW"; err_len = strlen("GL_STACK_UNDERFLOW"); break; case GL_OUT_OF_MEMORY: std::cout << "GL_OUT_OF_MEMORY"; err_len = strlen("GL_OUT_OF_MEMORY"); break; case GL_INVALID_FRAMEBUFFER_OPERATION: std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION"; err_len = strlen("GL_INVALID_FRAMEBUFFER_OPERATION"); break; }; std::cout << " ]}=========---------\n" << std::endl; std::cout << "A '" << func << "' függvenyben" << std::endl; std::cout << "A '" << file << "' fajl " << line << ". soraban\n" << std::endl; int ending_line_length = err_len + 42; for(int i = 0; i < ending_line_length; i++) { std::cout << "="; } std::cout << std::endl; } }
- A használata:
- A kódba ha leírod, hogy CheckError(); akkor az kiirja, hogy addig volt-e hiba.
- Példa:
1 void SetProjectionMatrix() { 2 CheckError(); 3 glMatrixMode(GL_PROJECTION); 4 glLoadIdentity(); 5 gluPerspective(60, 1, 1, 50); 6 glMatrixMode(GL_MODELVIEW_MATRIX); 7 CheckError(); 8 }
- A futtatás eredménye:
---------========={[ GL_INVALID_ENUM ]}=========--------- A 'void SetProjectionMatrix()' függvenyben A 'main.cpp' fajl 7. soraban =========================================================
- Ebből megtudod, hogy a hiba a 2. sorban még nem lépett fel, de a 7. már igen. Tehát a 4 OpenGL függvényhívás egyike a rossz, és az a gondja, hogy rossz enumot használsz (GL_INVALID_ENUM). A 4 függvényből csak kettőben van enum, a két glMatrixMode() hívásban. A kettő közül a 6. sor a rossz, az helyesen glMatrixMode(GL_MODELVIEW) lenne, mert a GL_MODELVIEW és a GL_MODELVIEW_MATRIX enumok nem ugyan arra valók. Ezt a hibát ennek a trükknek a használata nélkül nagyon nehéz lenne megtalálni, hiszen a kód jónak látszik, viszont a képernyőn nem látszódna semmi, vagy ha látszódna is, akkor annak semmi köze nem lenne ahhoz, amit rajzolni akartál. Egy ilyen hiba megtalálása még a legrutinosabbaknak is valószínűleg több órába telne. Ez az egyszerű függvény viszont szinte pontosan megmondta, hogy hol és mi a hiba.
Nálam működik, a beadón viszont nem hibák
- Szinte biztos, hogy NEM a beadó rossz, csak a kódodba van valamilyen rejtett hiba, ami nem definiált, implementáció függő viselkedést okoz, ami a te gépeden véletlenül pont azt csinálja, amit akartál, hogy csináljon, de egy másik gépen - mint pl. a beadón - már nem.
- Ilyen hibát c++-ba is tudsz véteni, pl : pow() negatív számokra:
pow(-2, 2) == 4; // A kitevő egész, ez ok De: pow(-2, 2.1f) = UNDEFINED // A kitevő valós, nem ok. A (-2)^2.1 egy komplex szám.
- Az utóbbi kódra előfordulhat, hogy a pow függvény nálad a komplex eredmény valós részét adja vissza, a beadón viszont NAN-t, mondva, hogy komplex számot nem tud valóssá konvertálni helyesen.
- Az OpenGL-be rengeteg ilyen van. És sajnos nincs ezek ellen nincs más ellenszer, mint hogy az általad használt függvények [dokumentációját] elolvasod, ahol leírják, hogy milyen esetben okoz implementáció függő viselkedést. Szerencsére az esetek több mint 90%-ába ilyenkor a függvény generál egy hibát, amit az előbb bemutatott CheckError()-al el lehet kapni, de nem mindig.