6. Wyświetlanie figur w przestrzeni 3D

Szczawik

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

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.

d3dkurs6.jpg
Rys. 4. Okno aplikacji z bryłami w przestrzeni oraz ilustracja poglądowa ustawienia sceny

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.

Kod źródłowy

Całość kodu źródłowego projektu: d3dkurs6.zip

0 komentarzy