Spis treści
1 Wstęp
2 Włączenie bufora głębokości
3 Ustawienie parametrów renderowania
4 Definiowanie wierzchołków
4.1 Metoda grupowa
4.2 Metoda strumieniowa
5 Kod źródłowy
6. Wyświetlanie figur w przestrzeni 3D
Wstęp
Tym razem cofniemy się do lekcji 4. Wykorzystanie tekstur, by zamieszczony tam przykład zmodyfikować. Będziemy starali się osiągnąć efekt, przedstawiony na rys. 4.
<center>Rys. 4. Okno aplikacji z bryłami w przestrzeni oraz ilustracja poglądowa ustawienia sceny</center>
Scena będzie składała się z 4 elementów, opisanych dwiema tablicami wierzchołków. W pierwszej znajdują się wierzchołki dla teksturowanego kwadratu, rozpostartego między punktami (0.1, 0.1, 0) oraz (0.9, 0.9, 0), zatem leżącego na płaszczyźnie o współrzędnej Z równej 0. Warto tu podkreślić, że założymy leworęczny układ współrzędnych, co jest pokazane w prawej częsci rys. 4. Oś X oznacza, przy domyślnie ustawionej kamerze, przesuwanie obiektów wzdłuż szerokości ekranu, oś Y wzdłuż wysokości, natomiast oś Z wgłąb sceny.
W drugiej tablicy wierzchołków znajdzie się sześć wierzchołków, opisanych wyłącznie kolorem, które parami - pierwszy leży w punkcie (0, 0, 0), drugi w jednostkowej długości wzdłuż jednej z osi - wykreślają układ współrzędnych. Oś X przechodzi od bieli do czerwieni, oś Y do zieleni, a oś Z (niewidoczna przy domyślnej pozycji kamery) do koloru niebieskiego.
Ponieważ wykonamy przejście do przestrzeni trójwymiarowej, w kodzie pojawi się kilka dodatkowych elementów.
Włączenie bufora głębokości
Akceleratory graficzne mogą poświęcać dodatkową część pamięci i mocy obliczeniowej na wyliczanie, który fragment obrazu znajduje się bliżej kamery i zasłania inne. Opcja ta jest domyślnie wyłączona, przez co obiekty są rysowane w kolejności renderowania (ostatni jest renderowany zawsze z przodu), a nie w kolejności ustawienia w przestrzeni.Zatem należy włączyć bufor głębokości przez określenie dwóch dodatkowych parametrów (włączenia oraz ustawienia formatu tego bufora) podczas tworzenia urządzenia:
presentParameters.AutoDepthStencilFormat = DepthFormat.D16; presentParameters.EnableAutoDepthStencil = true;
Ostatnią rzeczą, o jakiej należy pamiętać, jest zmiana funkcji czyszczącej urządzenie przed renderowaniem, aby uwzględniać opróżnianie również bufora głębokości. W naszym przykładzie funkcję tą zmodyfikujemy w taki sposób:
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
Ustawienie parametrów renderowania
Po wywołaniu funkcji device.BeginScene warto ustawić parametry renderowania. Po pierwsze należy wyłączyć, domyślnie włączone, oświetlenie. Pozwoli to nam na zobaczenie obiektów, jeszcze bez dodawania świateł do sceny.device.RenderState.Lighting = false;
Bardzo ważnym mechanizmem, o którym warto pamiętać jest culling. Technika ta określa, czy płaszczyzny mają być ukrywane, w zależności od kolejności zdeklarowanych wierzchołków (zgodnie ze wskazówkami wskazówek zegara, przeciwnie, albo wcale). Tak na prawdę oznacza to, z której strony płaszczyzna jest widoczna (od przodu, od tyłu, z obu stron). W naszym przykładzie skorzystamy z ostatniego rozwiązania:
device.RenderState.CullMode = Cull.None;
W naszym przykładzie nie będziemy zmieniali techniki wypełniania (renderowanie pełne, sama siatka, same punkty). Zapraszam jednak do samodzielnego eksperymentowania. Przykład zmiany wypełniania na renderowanie siatki:
device.RenderState.FillMode = FillMode.WireFrame;
Definiowanie wierzchołków
Istnieją dwie oddziele metody na przesłanie opisu figur do karty graficznej: w sposób strumieniowy oraz grupowy. Pierwsza technika jest zdecydowanie szybsza i pozwala karcie graficznej na wykonanie pewnych optymalizacji na strumieniu wierzchołków, jeszcze zanim użytkownik wskaże, jakie z nich utworzyć bryły. Druga jest wygodniejsza dla programisty i polega na przesyłaniu do karty graficznej opisu pojedynczych brył wraz z je tworzącymi wierzchołkami.Obie na tym etapie, pozwalają na osiągnięcie takich samych efektów graficznych. W naszym przykładzie do narysowania osi wykorzystamy metodę grupową, do renderowania teksturowanej płaszczyzny - strumieniową.
Metoda grupowa
Metodę grupową stosowaliśmy już przy rysowaniu wierzchołków ekranowych. Teraz, w klasie TDirectX definiujemy pole prywatne:private CustomVertex.PositionColored[] vertex_axis;
W metodzie Initialize wypełniamy współrzędne sześciu wierzchołków dla osi, jak opisano we wstępie:
#region Osie układu współrzędnych vertex_axis = new CustomVertex.PositionColored[6]; //Oś X vertex_axis[0].X=0f; vertex_axis[0].Y=0f; vertex_axis[0].Z=0f; vertex_axis[0].Color = Color.White.ToArgb(); vertex_axis[1].X=1f; vertex_axis[1].Y=0f; vertex_axis[1].Z=0f; vertex_axis[1].Color = Color.Red.ToArgb(); //Oś Y vertex_axis[2].X=0f; vertex_axis[2].Y=0f; vertex_axis[2].Z=0f; vertex_axis[2].Color = Color.White.ToArgb(); vertex_axis[3].X=0f; vertex_axis[3].Y=1f; vertex_axis[3].Z=0f; vertex_axis[3].Color = Color.Green.ToArgb(); //Oś X vertex_axis[4].X=0f; vertex_axis[4].Y=0f; vertex_axis[4].Z=0f; vertex_axis[4].Color = Color.White.ToArgb(); vertex_axis[5].X=0f; vertex_axis[5].Y=0f; vertex_axis[5].Z=1f; vertex_axis[5].Color = Color.Blue.ToArgb(); #endregion
Renderowanie odbywa się w poznany już sposób:
device.VertexFormat = CustomVertex.PositionColored.Format; device.DrawUserPrimitives(PrimitiveType.LineList, 3, vertex_axis);
Wyrenderowane zostaną 3 oddzielne odcinki (LineList). Warto zauważyć, że są one również oteksturowane, a kolor pozwala na modyfikację wartości tekstury. Aby linie były jednolite, można samodzielnie przed ich wyrenderowaniem usunąć aktualną teksturę:
device.SetTexture(0, null);
Metoda strumieniowa
Metoda ta polega na przygotowaniu bufora, który zostaje samodzielnie wysłany do karty graficznej, a następnie określaniu tylko, od którego wierzchołka i ile figur określonego typu ma zostać wyrenderowanych. W klasie TDirectX będą potrzebne dwa pola prywatne:private CustomVertex.PositionTextured[] vertex; private VertexBuffer vertexbuffer; private Texture texture;
Ładowanie i przywracanie tekstury jest wykonywane tak jak poprzednio:
System.IO.Stream stream = new System.IO.FileStream(@"c:\windows\kawa.bmp", System.IO.FileMode.Open); texture = new Texture(device, stream, Usage.Dynamic, Pool.Default); stream.Dispose();
Natomiast bufor wierzchołków można wypełnić w następujący sposób:
#region Teksturowany prostokąt vertex = new CustomVertex.PositionTextured[4]; vertex[0].X=0.1f; vertex[0].Y=0.9f; vertex[0].Z=0f; vertex[0].Tu = 0; vertex[0].Tv = 0; vertex[1].X=0.9f; vertex[1].Y=0.9f; vertex[1].Z=0f; vertex[1].Tu = 1f; vertex[1].Tv = 0; vertex[2].X=0.1f; vertex[2].Y=0.1f; vertex[2].Z=0f; vertex[2].Tu = 0; vertex[2].Tv = 1f; vertex[3].X=0.9f; vertex[3].Y=0.1f; vertex[3].Z=0f; vertex[3].Tu = 1f; vertex[3].Tv = 1f; // Wypełnianie bufora vertexbuffer = new VertexBuffer(typeof(CustomVertex.PositionTextured), vertex.Length, device, Usage.Dynamic, CustomVertex.PositionTextured.Format, Pool.Default); vertexbuffer.SetData(vertex, 0, LockFlags.None); #endregion
Renderowanie jest dokonywane w odmienny, ale równie nieskomplikowany, sposób:
device.SetTexture(0, texture); device.VertexFormat = CustomVertex.PositionTextured.Format; device.SetStreamSource(0, vertexbuffer, 0); device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
Wyrenderowane, z użyciem ustawionej tekstury i strumienia wierzchołków, zostaną 2 trójkąty przylegające (TriangleStrip), od wierzchołka o indeksie 0.