Managed » Direct3D

2. Uruchamianie urządzenia i renderowanie

  • 2007-12-21 00:50
  • 6 komentarzy
  • 1877 odsłon
  • Oceń ten tekst jako pierwszy


Wstęp


Wykorzystanie Direct3D wymaga utworzenia tak zwanego urządzenia renderującego. Nie jest to fizyczne urządzenie a obiekt, który odpowiada za komunikację z kartą graficzną. Warto tu rozgraniczyć trzy podstawowe typy urządzenia:
  • referencyjne - wirtualne urządzenie, renderowanie dokonywane jest programowo przez sterowniki z użyciem pamięci RAM; wykorzystywane przy uruchamianiu aplikacji na przykład na wirtualnej maszynie,
  • software'owe - fizyczne urządzenie bez wsparcia akceleracji sprzętowej, renderowanie dokonywane jest przez sterowniki z możliwością użycia pamięci urządzenia,
  • hardware'owe - fizyczne urządzenie renderujące z akceleracją sprzętową, renderowanie dokonywane jest przez urządzenie renderujące z możliwością użycia pamięci urządzenia.

Utworzenie urządzenia wymaga jedynie wypełnienia pól obiektu, opisującego parametry renderowania, i wykorzystania go to tworzenia obiektu.

Tworzenie urządzenia renderującego


W kroku 1._Przygotowanie_aplikacji stworzyliśmy szkielet klasy obsługującej Direct3D. Zdefiniowano w nim dwa pola prywatne: device będzie obiektem urządzenia, natomiast parent jest oknem, które stanowi cel renderowania.

Rozbudujmy konstruktor tak, aby tworzył urządzenie:

        /// <summary>
        /// Konstruktor obiektu obsługującego DirectX
        /// </summary>
        /// <param name="parent">Okno, które będzie celem renderowania grafiki</param>
        /// <param name="windowed">Czy renderować w oknie (true) czy pełnoekranowo (false)?</param>
        public TDirectX(Control parent, bool windowed)
        {
                this.parent = parent;
 
                PresentParameters presentParameters = new PresentParameters();
                presentParameters.Windowed = windowed;
                presentParameters.SwapEffect = SwapEffect.Discard;
                if (!windowed)
                {
                        presentParameters.FullScreenRefreshRateInHz = 60;
                        presentParameters.BackBufferWidth = 800;
                        presentParameters.BackBufferHeight = 600;
                        presentParameters.BackBufferCount = 1;
                        presentParameters.BackBufferFormat = Format.X8R8G8B8;
                        presentParameters.PresentFlag = PresentFlag.None;
                }
 
                device=new Device(0, DeviceType.Hardware, parent, CreateFlags.HardwareVertexProcessing, presentParameters);
 
                Initialize();
        }


Po pierwsze, obiekt typu PresentParameters zostaje wypełniony kolejno informacją czy wykorzystać tryb pełnoekranowy, po drugie - co zrobić z zawartością bufora obrazu po podmienieniu klatek (działa tu mechanizm podobny do double buffering; tzw. back buffer swap chain). Jeśli wybrano tryb pełnoekranowy, należy określić również częstotliwość odświeżania ekranu, rozdzielczość poziomą i pionową, ilość buforów w łańcuchu renderowania, format buforów oraz tryb podmiany bufora. Po więcej informacji odsyłam do dokumentacji, dołączonej do SDK.

Gdy ustawienia są wypełnione, można utworzyć urządzenie (typ Direct3D: Device) odpowiadające domyślnej (pierwszy parametr równy 0) karcie graficznej jako urządzeniu hardware'owemu, podanemu oknu, z uwzględnieniem sprzętowego przetwarzania wierzchołków (wymagane minimalnie wsparcie w karcie dla technologii T&L lub nowszej; dla maszyny wirtualnej należy wybrać SoftwareVertexProcessing) i zgodnego w wcześniej wypełnionymi parametrami.

Renderowanie


Samą metodę renderującą naszego obiektu TDirectX uzupełnijmy w następujący sposób:

/// <summary>
/// Wykonanie renderowania
/// </summary>
/// <param name="time">Czas działania aplikacji</param>
public void Render(float time)
{
        device.Clear(ClearFlags.Target, Color.Blue, 1.0f, 0);
        device.BeginScene();
 
        //Miejsce na implementację renderowania konkretnych elementów sceny
 
        device.EndScene();
        device.Present();
}


Po pierwsze, na kolor niebieski czyścimy zawartość bufora klatki, do którego będziemy dokonywali renderowania (dwa ostatnie parametry dotyczą buforów głębokości i obrysu, które nie są przez nas obecnie wykorzystywane). Rozpoczynamy renderowanie, a gdy je zakończymy - dokonujemy podmiany klatek obrazu.

Utrata urządzenia


Aplikacja po dokonaniu tych zmian powinna się skompilować, ale jeszcze nie będzie działała poprawnie. Podczas minimalizacji aplikacji czy jej rozciągania, urządzenie graficzne będzie resetowane. Aby je zachować, należy napisać metodę obsługi tych zdarzeń, by przywrócić w nich odpowiednie obiekty sceny.

Po linii tworzącej urządzenie dodaj następujące przypisania obsługi zdarzeń:

device.DeviceLost     += new EventHandler(this.InvalidateDeviceObjects);
device.DeviceReset    += new EventHandler(this.RestoreDeviceObjects);
device.Disposing      += new EventHandler(this.DeleteDeviceObjects);
device.DeviceResizing += new CancelEventHandler(this.EnvironmentResizing);


Dodatkowo w klasie TDirectX dodaj ich definicje:

protected virtual void InvalidateDeviceObjects(object sender, EventArgs e)
{
}
 
protected virtual void RestoreDeviceObjects(object sender, EventArgs e)
{
}
 
protected virtual void DeleteDeviceObjects(object sender, EventArgs e)
{
}
 
protected virtual void EnvironmentResizing(object sender, CancelEventArgs e)
{
}


Całość kodu źródłowego klasy TDirectX


    public class TDirectX
    {
        private Device device;
        private Control parent;
 
        /// <summary>
        /// Konstruktor obiektu obsługującego DirectX
        /// </summary>
        /// <param name="parent">Okno, które będzie celem renderowania grafiki</param>
        /// <param name="windowed">Czy renderować w oknie (true) czy pełnoekranowo (false)?</param>
        public TDirectX(Control parent, bool windowed)
        {
            this.parent = parent;
 
            PresentParameters presentParameters = new PresentParameters();
            presentParameters.Windowed = windowed;
            presentParameters.SwapEffect = SwapEffect.Discard;
            if (!windowed)
            {
                presentParameters.FullScreenRefreshRateInHz = 60;
                presentParameters.BackBufferWidth = 800;
                presentParameters.BackBufferHeight = 600;
                presentParameters.BackBufferCount = 1;
                presentParameters.BackBufferFormat = Format.X8R8G8B8;
                presentParameters.PresentFlag = PresentFlag.None;
            }
 
            device = new Device(0, DeviceType.Hardware, parent, CreateFlags.HardwareVertexProcessing, presentParameters);
            device.DeviceLost += new EventHandler(this.InvalidateDeviceObjects);
            device.DeviceReset += new EventHandler(this.RestoreDeviceObjects);
            device.Disposing += new EventHandler(this.DeleteDeviceObjects);
            device.DeviceResizing += new CancelEventHandler(this.EnvironmentResizing);
            Initialize();
        }
 
        /// <summary>
        /// Tworzenie wszystkich elementów sceny
        /// </summary>
        private void Initialize()
        {
        }
 
        /// <summary>
        /// Wykonanie renderowania
        /// </summary>
        /// <param name="time">Czas działania aplikacji</param>
        public void Render(float time)
        {
            device.Clear(ClearFlags.Target, Color.Blue, 1.0f, 0);
            device.BeginScene();
            //Miejsce na implementację renderowania konkretnych elementów sceny
            device.EndScene();
            device.Present();
        }
 
        #region Obsługa zdarzeń
        /// <summary>
        /// Obsługa zdarzenia utraty dostępu do urządzenia
        /// </summary>
        protected virtual void InvalidateDeviceObjects(object sender, EventArgs e)
        {
        }
 
        /// <summary>
        /// Obsługa zdarzenia przywrócenia urządzenia
        /// </summary>
        protected virtual void RestoreDeviceObjects(object sender, EventArgs e)
        {
        }
 
        /// <summary>
        /// Obsługa zdarzenia usunięcia urządzenia
        /// </summary>
        protected virtual void DeleteDeviceObjects(object sender, EventArgs e)
        {
        }
 
        /// <summary>
        /// Obsługa zdarzenia zmiany wymiarów obrazu
        /// </summary>
        protected virtual void EnvironmentResizing(object sender, CancelEventArgs e)
        {
        }
        #endregion
    }

6 komentarzy

msm 2009-06-14 22:28

Źle mnie zrozumiałeś ->  zmiana położenia jest właśnie moim zamiarem, a problemem jest właśnie zmienianie vertexów za każdym razem. Chciałbym coś w stylu "podajesz lewy górny róg obrazu, oraz ewentualnie wysokość + szerokość". Co do pełnoekranowego - chyba masz rację, założę temat na forum jak znajdę czas.

Szczawik 2009-06-14 15:05

W celu wiekszych dyskusji, umieszczaj tematy na forum. Co do rysowania figur: jesli nie potrzebujesz czegos renderowac zawsze od nowa, pomysl o wyrenderowaniu czegos na jedna teksture (render to surface). Skoro zmienia polozenie, to widocznie robisz cos zle i nie jest to wina DX tylko Twojego kodu; renderowanie jest procesem przewidywalnym i za kazdym razem powinno wygladac tak samo. Co do ustawien urzadzenia, poszukaj na necie o autodetekcji.

msm 2009-06-14 10:21

I jeśli już jesteśmy przy directX- ie: Czy da się narysować prostokąt inaczej niż poprzez vertexy? Bo w moim programie zmienia on położenie (lekko) przy każdym odmalowaniu i brzydko wygląda takie zmienianie ich za każdym razem (choć nie zajmuje wiele mocy obliczeniowej - 0ms na 100 powtórzeń). Tzn mam zmienne X i Y które określają lewy górny róg obrazu, wielkość jest stała.

msm 2009-06-14 10:16

1) jak?;
2) Spróbowałem też z SoftwareVertexProcessing i działa tak samo;
3) A kartę, jeśli już o tym mowa, rzeczywiście mam nienajnowszą...

Szczawik 2009-06-14 06:08

Sprawdz czy twoja karta wspiera CreateFlags.HardwareVertexProcessing.

msm 2009-06-13 23:22

Ja cię... nie mogę :)
Podczas pracy w trybie okienkowym (windowed == true) wszystko działa dobrze, ale przy pełnoekranowym wyskakuje błąd (InvalidCall Exception przy tworzeniu device) Nie mam pomysłu :/
(oczywiście wszystko takie samo jak w kodzie Szczawika, w szczególności tyczy się to sekcji "tylko jeśli !windowed")