Współpraca Cg i OpenGL

24.07.2008 13:56 in programowanie, grafika 3D

Gdy piszemy jakiś Vertex Shader, podstawowym działaniem, jakie w nim zawieramy, jest przemnożenie współrzędnych wierzchołka przez macierz projekcji i widoku. No cóż, nie jest to bardzo skomplikowane. Wymaga jednak, aby macierz była dostępna dla naszego programu. W Cg można ją przekazać w następujący sposób:

cgGLSetStateMatrixParameter(parameter,
    CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);

Pojawiają się jednak dwa problemy. Pierwszy: jeżeli korzystamy z operacji na macierzach OpenGL (np. ustawiamy nasze obiekty za pomocą glTranslatef), to taką instrukcję musimy powtórzyć dla każdego obiektu. OK -- możemy to zrobić, czemu nie? Pojawia się wtedy jednak drugi, dużo poważniejszy, problem.

Otóż funkcje ustawiania macierzy w Cg są strasznie wolne.

Było to dla mnie dużym zaskoczeniem, ale różnica FPS w moim programie była druzgocząca: 90 bez ustawiania macierzy dla każdego obiektu (oczywiście wszystkie obiekty były w kosmosie), 30 z ustawianiem. OK, obiektów było dużo, ale to nie usprawiedliwia tego, że głupie ustawianie macierzy zajmuje więcej czasu niż rysowanie skomplikowanej geometrii i przetwarzanie shaderów.

Nie wiedziałem, co z tym zrobić. Przekazywania macierzy nie dało się przecież uniknąć. Wymyśliłem sztuczkę: dla każdego obiektu wysyłałem nie macierz MVP, lecz macierz transformacji obiektu, a w shaderze mnożyłem MVP * macierz transformacji * współrzędne wierzchołka. Działało to trochę szybciej, ale nadal nie dość szybko. Z zazdrością patrzyłem na GLSL, gdzie mamy bezpośredni dostęp do macierzy MVP i poważnie rozmyślałem nad zmianą języka shaderów w moim programie.

Pomyślałem sobie jednak -- czemu tak potężne narzędzie jak Cg nie ma mieć opcji, które posiada GLSL? Przejrzałem dwa razy CG Reference Manual -- cisza. Ani słowa o innej metodzie przekazywania macierzy. W końcu, po poszukiwaniach na różnych forach znalazłem słowo-klucz.

glstate! Magiczna konstrukcja, której tak zazdrośnie strzegą w nVidia. Wszelakie informacje o tej strukturze znajdują się w podręczniku do Cg 1.5. Podręcznik do Cg 2, jak wspomniałem wcześniej, milczy.

W każdym razie, dzięki glstate mamy bezpośredni dostęp do wielu stanów OpenGL -- zupełnie jak w GLSL. Oczywiście w ten sposób tracimy kompatybilność naszych shaderów z DX (zgaduję, że to był powód "ukrycia" tej konstrukcji). Aby zdobyć MVP wystarczy użyć:

out_position = mul(glstate.matrix.mvp, in_position);

Cudowne, prawda? Dla potomności zamieszczę listę wszystkich zmiennych w glstate:

Typu float4x4

glstate.matrix.modelview[0]
glstate.matrix.projection
glstate.matrix.mvp
glstate.matrix.texture[0]
glstate.matrix.palette[0]
glstate.matrix.program[0]
glstate.matrix.inverse.modelview[0]
glstate.matrix.inverse.projection
glstate.matrix.inverse.mvp
glstate.matrix.inverse.texture[0]
glstate.matrix.inverse.palette[0]
glstate.matrix.inverse.program[0]
glstate.matrix.transpose.modelview[0]
glstate.matrix.transpose.projection
glstate.matrix.transpose.mvp
glstate.matrix.transpose.texture[0]
glstate.matrix.transpose.palette[0]
glstate.matrix.transpose.program[0]
glstate.matrix.invtrans.modelview[0]
glstate.matrix.invtrans.projection
glstate.matrix.invtrans.mvp
glstate.matrix.invtrans.texture[0]
glstate.matrix.invtrans.palette[0]
glstate.matrix.invtrans.program[0]

Typu float4

glstate.material.ambient
glstate.material.diffuse
glstate.material.specular
glstate.material.emission
glstate.material.shininess
glstate.material.front.ambient
glstate.material.front.diffuse
glstate.material.front.specular
glstate.material.front.emission
glstate.material.front.shininess
glstate.material.back.ambient
glstate.material.back.diffuse
glstate.material.back.specular
glstate.material.back.emission
glstate.material.back.shininess
glstate.light[0].ambient
glstate.light[0].diffuse
glstate.light[0].specular
glstate.light[0].position
glstate.light[0].attenuation
glstate.light[0].spot.direction
glstate.light[0].half
glstate.lightmodel.ambient
glstate.lightmodel.scenecolor
glstate.lightmodel.front.scenecolor
glstate.lightmodel.back.scenecolor
glstate.lightprod[0].ambient
glstate.lightprod[0].diffuse
glstate.lightprod[0].specular
glstate.lightprod[0].front.ambient
glstate.lightprod[0].front.diffuse
glstate.lightprod[0].front.specular
glstate.lightprod[0].back.ambient
glstate.lightprod[0].back.diffuse
glstate.lightprod[0].back.specular
glstate.texgen[0].eye.s
glstate.texgen[0].eye.t
glstate.texgen[0].eye.r
glstate.texgen[0].eye.q
glstate.texgen[0].object.s
glstate.texgen[0].object.t
glstate.texgen[0].object.r
glstate.texgen[0].object.q
glstate.fog.color
glstate.fog.params
glstate.clip[0].plane

Typu float

glstate.point.size
glstate.point.attenuation

Oczywiście wszędzie, gdzie mamy indeksy ([0]) możemy wstawić dowolną liczbę (aby np. dostać dane kolejnych świateł).

Comments:

  1. maxest

    maxest:

    Mowisz, ze obiekty byly w kosmosie? W takim razie ekran byl "czarny"? Jesli tak to nic dziwnego, ze FPSy byly duze - po prostu fragment shader nic nie robil :P
    btw: ja przesylam macierze przez cgGLSetMatrixParameter i nie to raczej jest waskim gardlem (tym bardziej ze w DX'owym rendererze jest *zmuszony* uzywac tego typu funkcji do przekazania macierzy).
    Jaki w ogole uzyskales przyrost predkosci?

    18.01.2009 13:27:41

  2. Tomasz Dąbrowski

    Tomasz Dąbrowski:

    cgGLSetMatrixParameter jest spoko. Działa z podobną prędkością co glLoadMatrix. Natomiast cgGLSetStateMatrixParameter jest zły, bo wewnętrznie wykonuje czytanie macierzy + SetMatrix, a jak wiadomo komunikacja GPU -> CPU nie jest (poza paroma wyjątkami) dobrym pomysłem. :)

    Obiekty nie były w kosmosie, obraz był w obydwu przypadkach taki sam. Nie pamiętam już dokładnie różnicy FPS-ów ale było to coś rzędu 100->200 przy ok. 100 obiektach na scenie.

    18.01.2009 15:10:58

Leave comment: