Tworzenie gry w C# z użyciem silnika Ogre - cz.7

21.07.2011 - Mateusz Osowski
TrudnośćTrudność
Dotychczas zajmowaliśmy się techniczną stroną silnika, mechaniką całego systemu. Przyszedł czas na poznanie sposobów poprawiania graficznej strony gry. Przyjrzymy się systemowi materiałów silnika renderującego Ogre, a także napiszemy podstawowe programy cieniujące (shadery).

[Część 1] [Część 2] [Część 3] [Część 4] [Część 5] [Część 6]
[Część 7]

Materiały

Silnik Ogre posiada moduł renderujący, który możemy bardzo swobodnie konfigurować za pomocą specjalnych skryptów. Składnia, konstrukcja tych plików została inspirowana jest najprawdopodobniej formatem efektów DirectX (FX), lecz posiadają one znacznie szerszy zakres możliwości. Najważniejsze elementy materiału to:
  • Techniki
  • Przebiegi
  • Programy cieniujące
  • Jednostki teksturujące

Dzięki nim będziemy w stanie osiągnąć efekty jak na poniższym obrazku:

Techniki

Poprawny materiał musi zawierać co najmniej jedną technikę. Zapewne mieliście okazję oglądać pliki materiałów już wcześniej. Techniki mogą też posiadać nazwy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
material MaterialTestowy
{
  technique Domyslna
  {
    ...
    // fajne shadery
  }
        
  technique Awaryjna
  {
    ...
    // fixed-pipeline
  }
}


Pierwszym z zastosowań technik jest zapewnienie awaryjnych trybów renderowania grafiki. Silnik sprawdza, czy sprzęt na którym uruchamiana jest aplikacja posiada możliwości wystarczające do obsługi danej techniki. Jeżeli wybrana technika nie jest obsługiwana, silnik użyje następnej w kolejności.
Drugim ich zastosowaniem jest renderowanie z obliczeniami wykonywanymi na buforach danych z przestrzeni ekranu (deferred shading). W skrócie, idea polega na zapisywaniu do tekstury informacji takich jak wektory normalne, tekstury i wykonywaniu obliczeń w odniesieniu do tych danych. Wówczas tworzymy osobne techniki do renderowania różnych składowych.

Przebiegi

Przebieg to po prostu przetworzenie całej geometrii korzystającej z danego materiału. Skrypty dają nam możliwość renderowania wieloprzebiegowego i określenia kluczowej dla całej idei operacji łączenia kolejnych przebiegów.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
technique
{
  pass Przebieg0
  {
    ...
    // renderujemy tekstury
  }
 
  pass Przebieg1
  {
    ...
    // renderujemy cienie
    scene_blend modulate                
 
  }
}


Dostępne operacje mieszania przebiegów (scene_blend, blending modes) to:

  • add - wartości kolorów pikseli przebiegu są dodawane do poprzednich. Operacja znajduje szerokie zastosowanie, np. w sumowaniu natężenia oświetlenia.
  • modulate - mnożenie. Ta operacja może tylko ściemnić scenę, można użyć jej do aplikowania cieni za pomocą statycznych map oświetlenia (lightmaps).
  • colour_blend - koloruje nowy przebieg na podstawie kolorów wejściowych, tworzy wrażenie przezroczystości.
  • alpha_blend - używa wartości kanału alfa tekstury do określenia stopnia przezroczystości.

Wartości składowych kolorów są liczbami z przedziału [0,1] dla każdego kanału.
Istnieje też dwuargumentowy wariant scene_blend dający większą kontrolę nad sposobem łączenia przebiegów:

scene_blend src dst


Wówczas wynikowy kolor zapisany do bufora obliczany jest wg. formuły: wynik = (źródło * src) + (nowy_przebieg * dst). Poprawne wartości src i dst to:

  • one - wartość 1 na wszystkich kanałach
  • zero - wartość 0 na wszystkich kanałach
  • dest_colour - kolor docelowego przebiegu
  • src_colour - dotychczasowy kolor w buforze
  • one_minus_dest_colour - 1 - dest_colour
  • one_minus_src_colour - 1 - src_colour
  • dest_alpha - wartość kanału alfa docelowego przebiegu
  • src_alpha - wartość kanału alfa poprzednich przebiegów
  • one_minus_dest_alpha - 1 - dest_alpha
  • one_minus_src_alpha - 1 - src_alpha


Mieszanie w takiej samej formie jest dostępne w niskopoziomowych API graficznych, takich jak OpenGL i DirectX. Łatwo zauważyć, że parametry jednoargumentowej wersji scene_blend są odpowiednikami pewnych kombinacji argumentów wersji dwuargumentowej, np.
scene_blend add to scene_blend one one,
ponieważ dla tego ustawienia wynik = (żródło * 1) + (nowy_przebieg * 1) = źródło + nowy_przebieg.

Samą operację dodawania można zastąpić inną za pomocą parametru scene_blend_op (manual).

Przebiegi mogą być powtarzane. Liczba iteracji przebiegu może być z góry ustalona, lub też zależeć np. od liczby świateł na scenie. Do określania liczby iteracji służy parametr iteration, przykładowo:

1
2
3
4
5
6
7
8
9
10
technique
{
  pass
  {
    iteration once_per_light
    scene_blend add
    ...
 
  }
}

wyrenderuje geometrię tyle razy, ile świateł znajduje się na scenie sumując przebiegi, np:



Opis rozmaitych argumentów iteracji: http://www.ogre3d.org/docs/manual/manual_16.html#SEC71.

Programy cieniujące

Podstawą renderowania grafiki w dzisiejszych grach są programy cieniujące (shadery). Bez nich bylibyśmy skazani na tzw. fixed pipeline, czyli zestaw prostych, zalutowanych w układzie graficznym operacji na geometrii i teksturach. Programy cieniujące pozwalają nam zaprogramować na karcie graficznej bardziej złożone obliczenia. Istnieje kilka rodzajów programów cieniujących:

  • Vertex program - Program dostaje na wejściu pozycje wierzchołków w świecie, a zwraca pozycję w przestrzeni ekranu
  • Fragment program - Program przetwarzający pojedynczy fragment (coś, co możemy nazwać kandydatem na wynikowy piksel). Dostaje na wejściu to, co zwraca vertex-program za wyjątkiem pozycji wierzchołka. Wartości przekazywane z vertex-programu będą dostarczone do fragment-programu już po interpolacji.
  • Geometry program - Program posiadający możliwość tworzenia nowej geometrii (wierzchołków), która również jest poddawana działaniu programów.


Będziemy zajmować się jedynie pierwszymi dwoma typami programów.
Jaką postać ma taki program? Jest to po prostu funkcja, która przyjmuje pewne dane indywidualne dla każdego wierzchołka oraz posiada dostęp do pewnych danych globalnych. Do opisu tej funkcji służą języki programowania takie jak Cg, GLSL (OpenGL) i HLSL (DirectX). Wybierzemy język Cg rozwijany przez nVidię, ponieważ programów w nim napisanych będziemy mogli używać zarówno w trybie DirectX, jak i OpenGL. Ponadto język posiada składnię bardzo zbliżoną do składni HLSL, więc poznamy dwa języki za jednym razem.

Pierwszy program

Jako przykładem, posłużymy się najprostszym, klasycznym fragment-programem zwracającym zawsze kolor pomarańczowy na wyjściu. Na razie zadanie vertex-programu pozostawimy domyślnej implementacji.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct FragmentIn
{
  float2 TexCoord : TEXCOORD0;
};
 
 
 
struct FragmentOut
{
  float4 Color : COLOR;
};
 
 
 
FragmentOut fragment_func(FragmentIn In) 
{
  FragmentOut Out;              
  Out.Color.r = 1;
  Out.Color.g = 0.7;
  return Out;
}
OgarneFP.cg


Przyjrzyjmy się... Składnia języka jest C-podobna. Program jest funkcją, która przyjmuje pewne dane, niekoniecznie musi to być jedna zmienna, więc dobrym zwyczajem jest opakowywanie danych wejściowych, a także wyjściowych programów w struktury.

W Cg mamy do dyspozycji bardzo wiele nowych typów podstawowych, takich jak float2, float3, float4, będących po prostu wektorami typu float o odpowiedniej długości. Do składowych można się odnosić poprzez pola r, g, b, a, lub też x, y, z, w. Istnieją również typy macierzowe float3x3, float4x4, float4x3, ....

Dużą odmianą składniową w stosunku do C są tzw. semantyki - TEXCOORD0, COLOR. Ich głównym zadaniem jest usprawnienie wymiany danych między aplikacją i programem cieniującym. Twórca aplikacji lub silnika może odgórnie ustalić, jakie wejściowe będą zawierały określone semantyki. W przypadku Ogre dokumentacja dobrze opisuje używane semantyki. W przypadku powyższego programu oczekujemy, że pod semantyką TEXCOORD0 zapisane będą współrzędne mapowania tekstury wysłane przez vertex-program (w tym przypadku zastąpiony przez fixed-pipeline, który tam właśnie zapisuje współrzędne mapowania tekstur).

Wykorzystanie

Spróbujmy zastosować ten prosty program w grze. W tym celu musimy go zapisać do pliku, najlepiej z rozszerzeniem .cg, np. OrangeFP.cg.
Oprócz tego, musimy poinformować silnik o tym programie. Robimy to poprzez skrypt z rozszerzeniem .program. Tworzymy więc plik o nazwie Programs.program i wypełniamy go deklaracją:

1
2
3
4
5
6
fragment_program FragmentProgram/Orange cg
{
  source OrangeFP.cg
  entry_point fragment_func
  profiles ps_1_1 arbfp1
}
Programs.program


Nazwą programu rozpoznawaną w silniku Ogre jest FragmentProgram/Orange. Parametr entry_point określa nazwę funkcji głównej programu. W jednym pliku .cg może być zapisanych wiele programów. Parametr profiles ustala profile wymagane do kompilacji programu. W tym przypadku korzystamy z profilu Pixel Shader 1.1 dla DirectX i ARB Fragment Program 1 dla OpenGL. Lista wszystkich dostępnych profili i obsługujących je kart graficznych znajduje się pod adresem http://www.ogre3d.org/docs/manual/manual_18.html. Kompilacja programu z wyższym profilem zmniejsza ograniczenia nałożone na nas podczas pisania programu cieniującego, lecz zwiększa wymagania sprzętowe. Zalecane jest używanie najniższego profilu niezbędnego do napisania danego programu cieniującego.

Aby zobaczyć wynik działania programu w grze, podmieńmy jeden z istniejących materiałów. Zastąpmy materiał wazy w pliku Vase.material następującym:

1
2
3
4
5
6
7
8
9
10
11
12
material Vase/TEXFACE/vase.jpg
{       
  technique
  {
    pass
    {                                   
      fragment_program_ref FragmentProgram/Orange
      {
      }
    }
  }
}
Vase.material


Jak widać wykorzystanie fragment-programu jest bardzo proste, wystarczy podać jego nazwę jako parametr fragment_program_ref. Konieczne jest wstawień klamer, ponieważ składnia materiałów dopuszcza przekazywanie programom cieniującym dodatkowych parametrów, czym będziemy zajmować się za chwilę. Nazwa materiału Vase/TEXFACE/vase.jpg została wygenerowana przez Blendera podczas eksportowania modelu wazy.

Aby programy cieniujące w języku CG mogły zostać skompilowane, musimy dołączyć do programu odpowiedni plug-in:

Plugin_CgProgramManager

Skopiuj pliki z pobranego archiwum do katalogu roboczego gry, zastąp plik Plugins.cfg.



5
Twoja ocena: Brak Ocena: 5 (1 ocena)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com