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:
-
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
-
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