Faces Of Nature - Informatyka - OpenGL and DirectX
Faces of nature   »  

Informatyka  »   OpenGL & DirectX

(1) OpenGL and DirectX w C++

Poniższe materiały dotyczą prostego silnika graficznego obsługujące obie z bibliotek (OpenGL i DirectX). Wcześniej pisałem w OpenGL, ale powiadają, że trzeba poznać obie, aby móc świadomie wybrać dla siebie lepszą. Choć dlaczego by nie znać i nie używać obu.

OpenGL jest przenośny, DirectX umożliwia programistom naukę rozszerzeń, których nie obsługują ich karty graficzne (emulacja). Różnic jest trochę, ale mimo wszystko wiele wiele łączy te biblioteki. Zmienia się właściwie tylko API, idee pozostają te same.

(2) Jedno API, kilka interfejsów

Engine ma za zadanie uprościć jak najbardziej korzystanie z możliwości obu bibliotek. Bez warunków typu

	if (OpenGL)
	{ ... }
	else if (DirectX)
	{ ... }
	

Mamy natomiast jeden wskaźnik np. GUI, który zostaje przypisany na początku do obiektu OpenGL lub DirectX, a te dziedziczą po wspólnej klasie abstrakcyjnej OpenX. To samo dotyczy innych struktur, jak np. Materiały lub Tekstury. Tworzymy jedną klasę np. CMaterials, i dla niej oddzielnie pochodne COpenGLMaterials i CDirectXMaterials, które używają już odpowiedniego dla nich interfejsu, poleceń OpenGL czy to DirectXa.

To samo dotyczy obiektów 3D. Mamy jedną klasę wspólną CMesh i dwie pochodne dla każdego z interfejsów tj. COpenGLMesh i CDirectXMesh dziedziczące po niej. Obiekt typu CMesh ładujemy np. za pomocą importera plików OBJ, a następnie konwertujemy do odpowiedniego formatu (OpenGL, DirectX) np. tak:

	CMesh tmp_mesh;
	CLoadOBJ loader;
	loader.Import("file.obj", &tmp_mesh, &listObj,
		&listMaterial, false);
	
	// Conversion to OpenGL or DirectX format
	CMesh *Mesh;
	Mesh = GUI->New(&tmp_mesh);
	
	// Init buffers
	tmp_mesh -> InitBuffer();
	
	// Draw mesh
	tmp_mesh -> Render();
	

Można by spytać po co te komplikacje, moglibyśmy stworzyć od razu wersję ładowania pliku OBJ do odpowiedniego interfejsu (format OpenGL lub DirectX), ale chcemy mieć większą kontrolę nad tym procesem. W tej chwili możemy załadować wszystkie pliki obiektów przed wyborem interfejsu, a dopiero po tym wyborze konwersję do odpowiedniego formatu. Przydatna opcja gdy planujemy tworzyć własny format plików sceny.

Konwersję powyższą można by zamienić na te kilka linii:

	if (OpenGLInterface)
	{
		Mesh = COpenGLMesh(tmp_mesh);
	}
	else if (DirectXInterface)
	{
		Mesh = CDirectXMesh(tmp_mesh);
	}
	

Ale chcemy pozbyć się takich warunków, więc korzystając z wirtualności metody New obiektu COpenX (*GUI) robi to za nas. Można by się spierać czy to najszybsza z metod (polimorfizm zabiera czas procesora), ale podczas pisania złożonych projektów w początkowych fazach optymalizacja często powodowała więcej problemów.

(3) Kilka różnic między OpenGL a DirectX

Różnice w ideach, wymagające oddzielnego konwertowania danych. Spis jest rezultatem pisania silnika i odkrywania właśnie tych różnic między OpenGL a DirectX (oczywiście, niektóre z różnic mogą być wynikiem konkretnej implementacji, wersji posiadanego sprzętu - ale nie zaszkodzi zwrócić uwagę, gdy coś jednak nie będize identycznie wyglądać)

  • Skrętność osi XYZ, w OpenGL oś Z rośnie w stronę ekranu (nas), w DirectX za ekran. Wystarczy w jednym z interfejsów odwrócić współrzędną Z (UWAGA! nie tylko w współrzędnych obiektów, ale także wektorów translacji, rotacji itd.).
  • Współrzędne tekstur UV, w OpenGL punkt (0,0) jest w dolnym lewym rogu, w DirectX w górnym lewym. Osobiście wolę rozwiązanie OpenGL więc w DirectX: v'= 1-v; (3D Studio MAX używa domyślnie tego samego rozwiązania).
  • Kolejność bajtów w definicji koloru RGBA w DWORD. OpenGL:
    #define RGBA(r,g,b,a)  \
    ((((a)&0xff)<<24)|(((b)&0xff)<<16)|
     (((g)&0xff)<<8)|((r)&0xff))
    w DirectX:
    #define RGBA(r,g,b,a)  \
    ((((a)&0xff)<<24)|(((r)&0xff)<<16)|
     (((g)&0xff)<<8)|((b)&0xff));
    
  • Położenie oświetlenia, w DirectX dla punktowego światła podajemy raz współrzędne D3DTS_WORLD, w OpenGL musimy ręcznie w każdej klatce animacji ustawić glLightfv( LightNum, GL_POSITION, float [4] );
  • DirectX nie używa stosu własności/macierzy jak OpenGL (glPushMatrix, glPushAttrib itp.), o to sami musimy zadbać, ew. skorzystać z d3dx9.
  • Texture mapping filter - w OpenGL filtr nadajemy podczas tworzenia tekstury
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
    W DirectX jako stan maszyny podczas animacji:
    Device->SetSamplerState(0, D3DSAMP_MAGFILTER, filter);
  • OpenGL Lighting używa składowej Ambient globalnej dla całej sceny, ustawianej przez glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Ambient);, DirectX domyślnie ma tą wartość zerową, OpenGL nie zerową.

(4) Format VRML 97

Pliki VRML mogą pomóc nam wyeksportować obiekty statyczne w postaci hierarchii Bones oraz ich animację (translacja, rotacja, skalowanie), czego np. nie potrafił format OBJ.

Format dość popularny, więc nie powinno być problemu eksportu do niego. Co prawda VRML wypierany jest przez X3D, to soft, którego używam (sic!) niezbyt sobie z nim radzi. A i w samych VRML exporterach jest trochę rozbieżności, natury parserowania - i tak np. zamiast przecinków używa się znaków nowej linii itp itd, więc poniżej spis tego pod czym parser VRML 'działa'.

Zaimplementowany parser plików VRML jest zapudełkowany w klasę CVRMLImport, korzystający z klas CVRML_Node i jej pochodnych do rekurencyjnego parserowania pliku .WRL. A w efekcie końcowym potrafi zaimportować światła CLight, materiały CMaterial, oraz obiekty CObject3D, które dzielą się na statyczne CMesh oraz grupy obiektów połączonych hierarchią CGroup3D - których węzły umożliwiają animację w postaci klatek kluczowych CAnim_KeyFrame używających macierzy przekształceń.

Interpolacja animacji

Eksportując animację możemy zdefiniować tylko kilka klatek kluczowych, resztę robi za nas interpolacja - używamy przy tym SLERP (kwaternionów do interpolacji obrotów)

Parser VRML BETA

Parser formatu VRML jest dość roboczą wersją, testowaną na jednym eksporterze (podanym w wymaganiach) i w dalszym ciągu nie obsługującym wielu opcji VRMLa - nie były potrzebne jak narazie.

Obiekty VRML

Parser obsługuje jedynie obiekty najbardziej ogólne, tj. typu IndexedFaceSet, pomija Box, Sphere itp. W maxie wystarczy przekonwertować takie podstawowe do Mesh (nadać modyfikację Mesh Edit).

Błędy parsera

Dokumentacja pewnie nie powstanie tak szybko, więc o ile siedzi mi jeszcze w głowie, a coś się krzaczy bezustannie - pisać śmiało!

OpenGL and DirectX
Fig.1. Screen z enginu OpenX.
OpenGL and DirectX
Fig.2. Screen z enginu OpenX + VRML.

Wymagania

  1. GLEW - do obsługi rozszerzeń OpenGL
  2. D3DX9 - dodatkowe funkcje DirectX9
  3. Biblioteka CORONA - do obsługi plików graficznych JPG,TGA,GIF itp.
  4. Exporter VRML - 3DMAX VRML97 exporter, Version 9, Revision 1 - wymagany tylko przy ekporcie WRL (tylko na tym testowany)

Pliki do pobrania

  1. Source + Binary + DLLs (2008-10-29) - C++, projekt dev C++ (wersja obsługująca format VRML97)
  2. Source (2008-09-15) - w C++ , projekt w środowisku Dev C++
Faces of nature   »  

Informatyka  »   OpenGL & DirectX