Upload
phamthuy
View
265
Download
3
Embed Size (px)
Citation preview
Wstęp
2
Rendering w czasie rzeczywistym
Polega na generowaniu obrazów na tyle szybko, aby stworzyć wrażenie płynnej animacji i zagwarantować możliwość interaktywnej współpracy aplikacji z użytkownikiem 24 fps – 60 fps (120 fps dla stereo)
Ważne aspekty: Virtual reality (rzeczywistość wirtualna)
Augmented reality (rzeczywistość rozszerzona)
3
Zastosowania
Gry rozrywka
„Serious games” symulacje
edukacja
terapia
4
Co się zmienia?
Oczekiwania użytkowników
5
Pong
1972
6 "TeleGames-Atari-Pong" by Evan-Amos - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:TeleGames-Atari-Pong.png#/media/File:TeleGames-Atari-Pong.png
Donkey Kong
1981
7
"Donkey Kong Gameplay" by Source (WP:NFCC#4). Licensed under Fair use via Wikipedia - https://en.wikipedia.org/wiki/File:Donkey_Kong_Gameplay.png#/media/File:Donkey_Kong_Gameplay.png
Combat Zone
1983 https://www.youtube.com/watch?v=SGDvi6ydFHE
8
Doom
1993
9
Unreal
1998
10
Grand Theft Auto V
2013
11
"Grand Theft Auto V combat" by Source. Licensed under Fair use via Wikipedia - https://en.wikipedia.org/wiki/File:Grand_Theft_Auto_V_combat.jpg#/media/File:Grand_Theft_Auto_V_combat.jpg
Wiedźmin III
2015
12
Co się zmienia?
Rozdzielczość CGA (320x200),…,HD 1080 (1920x1080),…
Kolory 8, 16, 24, 32 bit/pixel, HDR (High Dynamic Range)
2D 3D
13
14 "Vector Video Standards2" by Original uploader was XXV at en.wikipedia Later version(s) were uploaded by Jjalocha, Aihtdikh at en.wikipedia. - Transferred from en.wikipedia. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:Vector_Video_Standards2.svg#/media/File:Vector_Video_Standards2.svg
Co się zmienia?
Moc obliczeniowa CPU
GPU
Pamięci (RAM, cache, SSD, itp.)
Przepustowość
15
Biblioteki graficzne
Kilkanaście lat temu grafika czasu rzeczywistego była generowana na CPU, przy pomocy renderera napisanego „od zera” (Wolfenstein, Doom, Quake,…)
Wszystkie współczesne aplikacje tego typu wymagają kart graficznych
Biblioteki graficzne zapewniają dostęp do funkcjonalności kart graficznych różnych producentów, oferując podstawowe procedury renderingu
16
Biblioteki graficzne, c.d.
Biblioteki te skupiają się tylko na jednym algorytmie renderingu – rasteryzacji
Obecnie żaden inny algorytm (np. śledzenie promieni) w czasie rzeczywistym nie jest w stanie stworzyć obrazu o lepszej jakości
17
Biblioteki graficzne, c.d.
Najczęściej stosowane biblioteki to OpenGL i DirectX
Mają one bardzo podobne możliwości i wydajność
18
OpenGL
Biblioteka niezależna od platformy (Windows, Unix, iOS, PS)
Opracowana przez SGI (Mark Segal i Kurt Akeley) w 1991/92
Powszechne stosowanie rozszerzeń przez producentów kart graficznych
Model programowania proceduralny
19
DirectX
Biblioteka Microsoftu
Z OpenGL’em konkuruje jedynie Direct3D (część DirectX)
Opracowana początkowo w 1992 przez Servana Keondjiana (założył firmę RenderMorphics), następnie w 1995 wcielona do DirectX 2.0
Niedostępna na systemach Unix, iOS, PS
Brak mechanizmu rozszerzeń (teoretycznie)
Obiektowy model programowania 20
Próba integracji
W 1997 SGI, Microsoft (i później HP) uruchomiły projekt mający na celu zintegrowanie OpenGL i Direct3D
Projekt został porzucony w 1999 Microsoft – zmiana strategii
SGI – kłopoty finansowe
21
Historia renderingu na kartach graficznych
Karty graficzne nieprogramowalne
OpenGL 1.x, DirectX do wersji 7
Tylko rendering trójkątów z pojedynczą teksturą
Sprzętowa transformacja i oświetlenie wierzchołków
Programowalne przetwarzanie wierzchołków
Rozszerzenia OpenGL 1.x, DirectX 8.x
22
Programowalne przetwarzanie fragmentów OpenGL 2.x, DirectX 9.x
Możliwość pisania coraz bardziej zaawansowanych programów
Nowe formaty tekstur, HDR (High Dynamic Range), rendering do tekstury
23
Historia renderingu na kartach graficznych, c.d.
Programowalne przetwarzanie geometrii OpenGL 3.x, DirectX 10.x
Operacje na prymitywach
Obliczenia na GPU (ogólnego przeznaczenia)
24
Historia renderingu na kartach graficznych, c.d.
Programowalna tesselacja OpenGL 4.x, DirectX 11
Nowe możliwości programów GPU
Zapis i odczyt z tekstury, operacje atomowe
25
Historia renderingu na kartach graficznych, c.d.
Wersje OpenGL
https://www.opengl.org/wiki/History_of_OpenGL
26
Rozszerzenia
GL_ : wszystkie platformy
GLX_ : Linux & Mac
WGL_ : Windows
EXT_ : rozszerzenie ogólne
ARB_ : zaakceptowane przez wszystkich członków OpenGL Architecture Review Board (EXT_ często stają się ARB_ w kolejnych wydaniach)
NV/AMD/INTEL/APPLE 27
Vulkan
https://en.wikipedia.org/wiki/Vulkan_(API)
next generation OpenGL initiative
higher performance and lower CPU usage
prekompilowane shadery
28
Vulkan, c.d. https://developer.nvidia.com/transitioning-opengl-vulkan
https://www.toptal.com/api-developers/a-brief-overview-of-vulkan-api
29
Vulkan vs OpenGL
In OpenGL getting something on the screen is by far easier. Even without classic fixed function, just rendering full-screen effects or image-processing takes only few lines of code. Vulkan’s level of verbosity to get to the first pixel on the screen is far higher. As hinted in the previous blog posts on resource bindings or memory management, these additional complexities will require more code to be written. Especially for people new to graphics, it may be better to use OpenGL or rendering middleware that hides this complexity and focus on the actual task.
30
Grafika 3D - przypomnienie
Modele budowane z wierzchołków Pozycja
Normalna
Współrzędne tekstury
I wiele, wiele, wiele innych
Wybrane trójki wierzchołków tworzą trójkąty, stanowiące brzeg modelu
Najnowsze karty obsługują bardziej złożone powierzchnie (sprzętowa teselacja)
31
Wierzchołki są transformowane z przestrzeni obiektu, przez przestrzeń świata i widoku do przestrzeni ekranu przy pomocy macierzy
W przestrzeni ekranu wykonywana jest rasteryzacja trójkątów
Liczone jest oświetlenie i teksturowanie
32
Grafika 3D - przypomnienie, c.d.
Wykonywane jest zasłanianie (z-bufor), wtapianie (blending) itp.
Wymienione powyżej elementy są bardzo podstawowe, obecnie stosuje się o wiele więcej zaawansowanych technik
33
Grafika 3D - przypomnienie, c.d.
OpenGL 3.0 rendering pipeline
34
OpenGL 4.3 rendering pipeline
35
OpenGL 4.5 rendering pipeline
36
Skrócona wersja obrazkowa
37
Pojedyncze podawanie wierzchołków
glBegin(GL_TRIANGLES);
glVertexf(...); glVertexf(...); glVertexf(...); glVertexf(...); glVertexf(...); glVertexf(...); glEnd();
DEPRECATED 38
Stare style programowania OpenGL
Stare style programowania OpenGL, c.d.
Bufory z wierzchołkami
DEPRECATED
39
Stare style programowania OpenGL, c.d.
Display lists
glNewList(index, GL_COMPILE); glBegin(GL_TRIANGLES); glVertex3fv(v0); glVertex3fv(v1); glVertex3fv(v2); glEnd(); glEndList(); glCallList(index);
DEPRECATED
40
A zatem jak?
Shaders
Vertex shader
Tesselation shaders (2x)
Geometry shadrer
Fragment shader
…
Bufory: VBO, VAO, EBO, textures,...
41
Literatura
42
Online
http://www.learnopengl.com
https://www.opengl.org/sdk/docs/man/html
https://www.opengl.org/registry
https://www.opengl.org/wiki/History_of_OpenGL
http://www.lighthouse3d.com/tutorials/glsl-tutorial
43
44
Książki
45
Książki, c.d.
Zaczynamy
46
Wersje OpenGL i GLSL
https://en.wikipedia.org/wiki/OpenGL_Shading_Language
47
Sprawdzenie wersji
glGetString(...) GL_RENDERER
karta graficzna
GL_VERSION wersja OpenGL
GL_SHADING_LANGUAGE_VERSION wersja GLSL
GL_EXTENSIONS obsługiwane rozszerzenia
48
Przykład OpenGL01
49
NVIDIA was proud to introduce programmable shading with Cg, which supported dozens of different OpenGL and DirectX profile targets. It allowed developers to incorporate interactive effects within 3D applications and share them among other Cg applications, across graphics APIs, and most operating systems (Windows XP, Vista and Windows 7, Mac OS X for Leopard, Snow Leopard & Lion, Linux 32-bit & 64-bit) as well as balance effect complexities with client GPU capabilities.
Going forward, we recommend new development with GLSL, or HLSL for Windows applications, rather than Cg. (2012 Cg DEPRECATED)
„… via Cg compiler”
50
Nie ma lekko…
In Modern OpenGL we are required to define at least a vertex and fragment shader of our own (there are no default vertex/fragment shaders on the GPU). For this reason it is often quite difficult to start learning Modern OpenGL since a great deal of knowledge is required before being able to render your first triangle…
51
Przykład OpenGL02
52
Shaders #version 450
in vec3 position;
in vec3 color;
uniform float zoom;
out vec3 fragColor;
void main()
{
gl_Position = vec4(position, 1.0)*zoom;
fragColor = color;
} 53
Typy zmiennych
Skalary bool, int, float, double, uint
Wektory {b|i||d|u|}vec[2|3|4], np. vec3, uvec2
Macierze {d}mat[2|3|4|2x2|2x3|2x4|3x3|3x3|3x4|4x2|4x3|4x4] (kolumn x wierszy),
np. mat4, dmat2x3
Samplers, Images, Opaque types, Atomic counters
void
54
Dostęp do składowych wektorów i macierzy
vec4 v v[0], v[1], v[2], v[3]
v.xyzw, v.x, v.xy, v.zyx, v.xxx, v.xxxx
v.rgba (kolory)
v.stpq (współrzędne tekstur)
mat4 m m[2][1]
m[3].xyz
55
Typy zmiennych, c.d.
Struktury struct {
…
} nazwa;
Tablice typ nazwa[rozmiar]
Po szczegóły dotyczące tablic odsyłam do specyfikacji GLSL
56
Przesyłanie danych
57
Vertex Buffer Object (VBO)
Służy do przekazania danych wierzchołków Zwykle: współrzędne, normalna, kolor, współrzędne tekstur
Ale może zawierać praktycznie dowolne dane
Dane przygotowujemy na CPU, a następnie: GLuint VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
58
VBO dla wielu obiektów
Lepiej (wydajniej) jest operować na pojedynczym VBO Przełączanie pomiędzy VBO zajmuje czas
Ale są wyjątki np. dla obiektów, dla których liczba wierzchołków znacząco się zmienia, lepiej
przydzielić oddzielne VBO
www.opengl.org/wiki/Vertex_Specification_Best_Practices
59
Aktualizacja fragmentu lub całości VBO
glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
60
Opisanie struktury VBO
Poprzez tzw. atrybuty: GLint attr = glGetAttribLocation(shaderProgram, "position");
glVertexAttribPointer(attr, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0);
glEnableVertexAttribArray(attr);
61
Vertex Array Object (VAO)
Pozwala zapamiętać VBO i jego atrybuty glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
GLint attr = glGetAttribLocation(shaderProgram, "position");
glVertexAttribPointer(attr, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0);
glEnableVertexAttribArray(attr);
glBindVertexArray(0); // ???
62
VAO
63
Uruchomienie rysowania VAO (=VBO+attr)
…jest już bardzo proste: glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, offset, count);
64
Przykład OpenGL03
65
Element Buffer Object (EBO)
66
Korzystanie wyłącznie z wierzchołków jest w większości przypadków nieefektywne
Sześcian – ile trójkątów, ile wierzchołków?
67
Sześcian – ile trójkątów, ile wierzchołków? c.d.
Sześcian ma 8 wierzchołków
Ściany zbudować można z 12 trójkątów
Daje to 36 wierzchołków – 4.5 x więcej!
68
Sześcian – ile trójkątów, ile wierzchołków? c.d.
Jeśli jednak dodamy normalne, to jest lepiej, bo: Wierzchołków jest 3x8 = 24
Czyli licząc 36 liczymy tylko 1.5 x więcej
Ale tak jest tylko dlatego, że wszystkie wierzchołki w sześcianie są „niegładkie”
Często jest całkiem inaczej -->
69
EBO
Reasumując: Często ten sam wierzchołek występuje w kilku trójkątach
Jego wielokrotne obliczanie w vertex shaderze jest zbędne!
70
EBO, c.d.
71
EBO, c.d.
Przekazanie EBO do OpenGL jest podobne do VBO: GLuint EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
72
EBO, c.d.
EBO może być zawarte w VAO
Rysowanie wg EBO: glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
73
Przykład OpenGL04
74
EBO i kilka VBO
75
Przykład OpenGL05
76
Przepływ danych
W wersji bardzo uproszczonej:
77
Zmienne wbudowane
Pełny wykaz i opis w „The OpenGL Shading Language”, rozdział „Built-in Variables” https://www.opengl.org/registry/doc/GLSLangSpec.4.40.pdf
Część zmiennych aktywna jedynie w określonych kontekstach lub po aktywacji glEnable(…) np. glEnable(GL_VERTEX_PROGRAM_POINT_SIZE) i glDrawArrays(GL_POINTS, …)
pozwala ustawiać gl_PointSize
Należy unikać używania własnych zmiennych z nazwą zaczynającą się od „gl_”
78
Interpolacja dla fragment shadera
Zmienne trafiają do fragment shadera interpolowane
Trzy metody interpolacji (dla zmiennych typu float i pochodnych): smooth – liniowa z uwzględnieniem perspektywy (domyślna metoda)
flat – brak interpolacji (brana jest wartość z ostatniego punktu)
noperspective – liniowa
definiuje się na wejściu do fragment shadera (np. in flat vec3 …)
Ograniczenie zmiennych wejściowych fragment shadera: Nie można używać bool i pochodnych
Zmienne typu int, double i pochodne muszą być oznaczone flat
79
smooth vs noperspective
80
Bufory framebuffera
W celu uniknięcia „migotania” i innych artefaktów: Rysowanie odbywa się do bufora GL_BACK
Po narysowaniu całej sceny, kiedy można ją pokazać, wykonywany jest transfer danych do właściwego widocznego bufora (GL_FRONT) – operacja ta jest zależna od platformy
Np. w Qt, przed wywołaniem paintGL() domyślnie ustawiany jest bufor GL_BACK, o po zakończeniu paintGL() wykonywany jest transfer (zamiana) buforów
GL_FRONT_LEFT, GL_FRONT_RIGHT, GL_BACK_LEFT, GL_BACK_RIGHT
glDrawBuffer(…) ustawia aktualnie używany do rysowania bufor (lub bufory)
81
Modele
82
Modele obiektów
Aby posunąć się dalej z materiałem, jeden trójkąt to za mało
Ręczne wpisywanie modeli bardziej skomplikowanych jest niewygodne
Modele tworzymy zatem w zewnętrznych programach do modelowania (z ew. pomocą skanerów 3D)
83
Modele obiektów, c.d.
Aby model czegoś bardziej skomplikowanego niż trójkąt wyglądał jako tako przyzwoicie, to potrzebne jest: Wczytywanie współrzędnych położenia, współrzędnych tekstur oraz
normalnych wierzchołków
Przekształcenia geometryczne
Oświetlenie
Dopiero potem ma sens dodawanie kolejnych efektów…
84
Demo prostego modelu…
85
Przykład OpenGL07
86
Skąd brać modele (dygresja)
Można zrobić samemu
Można wynająć kogoś z zacięciem plastycznym
Można skorzystać z gotowych modeli http://tf3dm.com
http://graphics.stanford.edu/data/3Dscanrep
87
Przykład OpenGL08
88
glEnable(GL_DEPTH_TEST);
Geometria
89
Przekształcanie wierzchołków
Odbywa się w vertex shader
Współrzędne do vertex shadera trafiają z VBO
Na wyjściu muszą się znaleźć współrzędne wpisane w gl_Position znormalizowane do (-1, -1, -1) – (1, 1, 1)
90
Przekształcanie wierzchołków, c.d.
91
Przestrzenie i przekształcenia
Podział przekształceń na: Modelu (obiektu)
Widoku
Projekcji
oraz przestrzeni na: Modelu
Świata (sceny)
Widoku
jest całkowicie umowny, jednak stosowany jest w wielu pakietach, programach, systemach, artykułach, tutorialach itp., że warto się go trzymać
92
Macierze 4x4
Najwygodniej zakodować przekształcenia za pomocą macierzy 4x4. Można dzięki temu wykonywać: Przesunięcia
Skalowania
Obroty
Rzuty perspektywiczne
Składać w/w przekształcenia
Odwracać w/w przekształcenia
…w sposób łatwy, prosty i przyjemny 93
Przesunięcie (translacja)
94
Skalowanie
95
Obroty
96
Perspektywa
http://www.songho.ca/opengl/gl_projectionmatrix.html 97
Perspektywa, c.d.
98
Rzut równoległy
99
Biblioteki
Tylko najbardziej zawzięci programiści tworzą macierze ręcznie bo to żadne wyzwanie
a pomylić się łatwo
Korzystamy z gotowych bibliotek Np. w Qt jest klasa Matrix4x4, która posiada metody:
• setToIdentity, translate, scale, rotate, ortho, perspective, frustum, inverted, transposed itd.
100
Przekazanie macierzy do vertex shadera
QMatrix4x4 pvm_matrix = p_matrix*v_matrix*m_matrix;
int attr = glGetUniformLocation(shaderProgram, "pvm_matrix");
glUniformMatrix4fv(attr, 1, GL_FALSE, pvm_matrix.data());
Uniformy nie są związane z VBO, więc i do VAO nie da się ich włączyć
101
Przykład OpenGL09/10/11
102
Przód i tył trójkąta
która strona trójkąta to przód? glFrontFace(…); GL_CCW (wartość domyślna)
GL_CW
glEnable(GL_CULL_FACE);
którą stronę trójkątów odrzucać? glCullFace(…); GL_FRONT
GL_BACK 103
Przód i tył trójkąta, c.d.
bool gl_FrontFacing dostępne w fragment shader
Badany jest znak wyrażenia:
104
Przekształcenie normalnych
Wektorów normalnych nie można przekształcać tak samo, jak współrzędnych wierzchołków:
105
Przekształcenie normalnych, c.d.
Macierz do przekształcania normalnych ma postać:
Wyprowadzenie: http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix
106
Przykład OpenGL12/13
107
Przekształcanie normalnych, c.d.
Vertex shader powinien dysponować dwoma macierzami:
in vec3 position;
in vec3 normal;
uniform mat4 pvm_matrix;
uniform mat3 norm_matrix;
out vec3 fragNormal;
void main()
{
gl_Position = pvm_matrix*vec4(position, 1.0);
fragNormal = normalize(norm_matrix*normal);
}
108
Przekształcanie normalnych, c.d.
Użycie normalnych w fragment shaderze: in vec3 fragNormal;
out vec4 color;
void main()
{
// przykładowe mapowanie normalnej na kolor:
color = vec4(abs(normalize(fragNormal)), 1.0);
}
109
Normalizacja normalnych
Czy jest potrzebna we fragment shaderze?
110
Normalizacja normalnych, c.d.
Czy jest potrzebna w vertex shaderze?
111
Normalizacja normalnych
Reasumując: Brak normalizacji w vertex shaderze może wprowadzać
błąd w kierunku normalnej
Brak interpolacji w fragment shaderze może wprowadzić błąd w długości normalnej
112
Przykład OpenGL14
113
Modele oświetlenia
114
Cel
Modele oświetlenia starają się oddać, jak najbardziej wiernie, zjawiska optyczne związane z postrzeganiem koloru obiektów
Oświetlenie znane z rzeczywistości jest zjawiskiem zbyt złożonym, aby udało się go odwzorować dokładnie, szczególnie w renderingu czasu rzeczywistego Konieczne są zatem uproszczenia
115
Oświetlenie lokalne i globalne
Lokalne: Brane jest pod uwagę jedynie źródło światła i oświetlany obiekt
Obliczane jest tylko jedno odbicie światła (od rozpatrywanego obiektu)
Globalne: Uwzględniane są optyczne interakcje pomiędzy obiektami
• rzucanie cienia
• wielokrotne odbicia
116
117
Oświetlenie lokalne i globalne, c.d.
Oświetlenie lokalne i globalne, c.d.
W grafice czasu rzeczywistego stosuje się oświetlenie lokalne
Dodatkowe efekty uzyskuje się poprzez zastosowanie pewnych elementów oświetlenia globalnego: Cienie
Mapowanie środowiskowe (odbicia, załamanie)
SSAO
itp.
118
Oko
Siatkówka oka
119
„Retina-diagram” autorstwa Fig_retine.png: Cajalderivative work Fig retine bended.png: Anka Friedrich (talk)derivative work: vectorisation by chris 論 - Fig_retine.pngFig retine bended.png. Licencja CC BY-SA 3.0 na podstawie Wikimedia Commons - https://commons.wikimedia.org/wiki/File:Retina-diagram.svg#/media/File:Retina-diagram.svg
Oko, c.d.
Widzenie kolorów zawdzięczamy czopkom, w percepcji barw uczestniczą barwniki: erythrolabe – reagujący z największą czułością na promieniowanie o λ = 590
nm (symbol D od długofalowe), wywołujące wrażenie czerwieni
chlorolabe – najbardziej czuły na promieniowanie o λ = 540 nm (wrażenie zieleni, symbol Śr)
cyanolabe – najbardziej czuły na promieniowanie o λ = 450 nm (wrażenie barwy niebieskiej, symbol K od krótkofalowe)
120
Oko, c.d.
121 "Cone-response PL". Licencja: CC BY-SA 3.0 na podstawie Wikimedia Commons - https://commons.wikimedia.org/wiki/File:Cone-response_PL.svg#/media/File:Cone-response_PL.svg
Model koloru
Dlatego model RGB jest bardzo popularny
Dostarczając do oka odpowiednią mieszankę kolorów czerwonego, zielonego i niebieskiego możemy zasymulować dowolny kolor
Niestety model ten nie jest odpowiedni przy rozpatrywaniu niektórych zjawisk optycznych (np. załamanie światła)
122
Modele źródeł światła
W grafice off-line można pozwolić sobie na lepsze przybliżenia źródeł światła
W grafice czasu rzeczywistego stosuje się kilka uproszczonych rodzajów źródeł światła Źródła punktowe
Źródła kierunkowe
Oświetlenie rozproszone (otoczenia)
123
Źródła punktowe
Zdefiniowane przez: Położenie (punkt w przestrzeni)
Kolor i natężenie emitowanego światła
Pewnym wariantem są źródła typu „spot light”, które dodatkowo charakteryzują się: Kierunkiem świecenia
Kątem oświetlania (ogólniej: funkcją jasności w zależności od kąta)
124
Zmiana jasności w zależności od odległości
Zgodnie z fizyką:
gdzie I – jasność, r – odległość od źródła światła, c – stała
Ze względu na pomijanie wielu zjawisk (głównie odbić) zastosowanie powyższego wzoru powoduje zbyt szybki zanik światła; stosuje się zatem wzór:
125
Źródła kierunkowe
Zdefiniowane przez: Kierunek padania
Kolor i natężenie światła
Jasność nie zmienia się
126
Źródła powierzchniowe
Kształt ma dużo mniejsze znaczenie niż powierzchnia
Najczęściej realizowane jako suma wielu źródeł punktowych
(obrazek obok został wygenerowany off-line)
127
Oświetlenie rozproszone
Zdefiniowane przez: Kolor (ale rzadko)
Natężenie
Pomaga uniknąć wysokich kontrastów i wrażenia ponurości…
Pomaga również na etapie modelowania obiektów i sceny
128
Oświetlenie Phonga
Połączenie światła odbitego (specular) i rozpraszanego (diffuse) od obiektu oraz światła rozproszonego (ambient)
129
Oświetlenie Phonga, c.d.
130
"Phong components version 4" by No machine-readable author provided. Rainwarrior~commonswiki assumed (based on copyright claims). - No machine-readable source provided. Own work assumed (based on copyright claims).. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:Phong_components_version_4.png#/media/File:Phong_components_version_4.png
Oświetlenie Phonga, c.d.
Oznaczenia: N – wektor normalny
L – wektor w kierunku źródła światła
R – wektor odbicia źródła światła
V – wektor w kierunku kamery
Wszystkie wektory są znormalizowane
131
Oświetlenie Phonga, c.d.
132
Inne modele
Model oświetlenia Phonga jest bardzo wygodny, ale nie zawsze doskonale się nadaje – najlepiej sprawdza się dla powierzchni „plastikowych”, dla innych materiałów czasem trudniej dobrać parametry
Model Orena-Nayara Inny model światła rozproszonego (zależny od położenia kamery)
Model BRDF (Bidirectional reflectance distribution function) Inny, bardziej dokładny model światła odbitego
np. Cook’a – Torrance’a http://www.codinglabs.net/article_physically_based_rendering_cook_torrance.aspx
133
Odbicie anizotropowe
134
Odbicie anizotropowe, c.d.
Model Ashikhmina – Shirleya http://www.cs.utah.edu/~shirley/papers/jgtbrdf.pdf
Wykorzystuje model Cook’a – Torrance’a z rozkładem:
135
Obliczenia
Obliczenia można rozłożyć pomiędzy vertex shaderem i fragment shaderem Na ogół więcej jest fragmentów niż wierzchołków
Jeżeli wszystkie obliczenia wykonamy dla wierzchołków (w vertex shaderze) i uzyskany wynik interpolujemy dla fragmentów, to uzyskamy cieniowanie Gourauda
Jeżeli jedynie wektory są jest interpolowane, a reszta obliczeń przeprowadzana jest dla fragmentów, to jest to cieniowanie Phonga
136
Gouraud vs Phong
137
Przykład OpenGL15
138
Materiał
Informacja o kolorze (obiektu lub jego fragmentu) nie jest informacją wystarczającą do realistycznego renedringu
W zależności od przyjętego modelu oświetlenia potrzebne są jeszcze inne atrybuty, np. dla Phonga.: Współczynniki ka, ks, dd, n (shininess)
139
Przykład OpenGL16
140
Uniform Data Block
Pozwala załadować dane typu uniform z bufora OpenGL
Pozwala na dzielenie danych pomiędzy programami
Deklaracja w shaderze: layout (std140) uniform Nazwa
{
// pola
};
141
Uniform Data Block, c.d.
Załadowanie do OpenGL: glGenBuffers(1, &MAT);
glBindBuffer(GL_UNIFORM_BUFFER, MAT);
glBufferData(GL_UNIFORM_BUFFER, data_size, data, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, MAT);
GLuint blockIndex = glGetUniformBlockIndex(shaderProgram, "Nazwa");
glUniformBlockBinding(shaderProgram, blockIndex, 0);
142
layout
Zgodnie z https://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt opcje layoutu, dotyczące upakowania, to: shared (domyślne) pozwala kompilatorowi zoptymalizować położenie pól, ale nie
pozwala ich usuwać, jeśli nie są używane; gwarantuje, że pakowanie będzie takie samo dla takich samych definicji bloku
packed podobnie jak share, jednak pola nieużywane mogą zostać usunięte
std140 położenie pól zgodne ze zdefiniowanymi regułami
143
layout std140
Dla: layout (std140) uniform Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
}
144
(1) If the member is a scalar consuming <N> basic machine units, the base alignment is <N>.
(2) If the member is a two- or four-component vector with components consuming <N> basic machine units, the base alignment is 2<N> or 4<N>, respectively.
(3) If the member is a three-component vector with components consuming <N> basic machine units, the base alignment is 4<N>.
(4) If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element, according to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The array may have padding at the end; the base offset of the member following the array is rounded up to the next multiple of the base alignment.
(5) If the member is a column-major matrix with <C> columns and <R> rows, the matrix is stored identically to an array of <C> column vectors with <R> components each, according to rule (4).
(6) If the member is an array of <S> column-major matrices with <C> columns and <R> rows, the matrix is stored identically to a row of <S>*<C> column vectors with <R> components each, according to rule (4).
(7) If the member is a row-major matrix with <C> columns and <R> rows, the matrix is stored identically to an array of <R> row vectors with <C> components each, according to rule (4).
(8) If the member is an array of <S> row-major matrices with <C> columns and <R> rows, the matrix is stored identically to a row of <S>*<R> row vectors with <C> components each, according to rule (4).
(9) If the member is a structure, the base alignment of the structure is <N>, where <N> is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4. The individual members of this sub-structure are then assigned offsets by applying this set of rules recursively, where the base offset of the first member of the sub-structure is equal to the aligned offset of the structure. The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure.
(10) If the member is an array of <S> structures, the <S> elements of the array are laid out in order, according to rule (9).
145
Przykład OpenGL17
146
Tekstury
147
Tekstura (mapa)
Tekstury generalnie służą do tego, aby zastąpić we fragment shaderze: Interpolowane wartości
poprzez: Wartości z tekstury dla interpolowanych współrzędnych
Istnieje wiele rodzajów tekstur: GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP, …
148
Tekstura jako kolor
149
Współrzędne tekstury
Współrzędne tekstur, podobnie jak np. wektory normalne są generowane przez programy do tworzenia modeli
Ręczne generowanie jest raczej niewygodne…
Oznaczane są jako u i v lub s i t
150
Współrzędne tekstury, c.d.
W OpenGL punkt (0, 0) tekstury to lewy dolny róg
W większości programów do modelowania punkt (0, 0) to lewy górny róg…
Trzeba zatem na jakimś etapie dokonać odbicia współrzędnej Y: Można na etapie wczytywania modelu/tekstur (najlepsze rozwiązanie)
Można odbić tekstury (słabe rozwiązanie)
Można w vertex shaderze (może być)
Można we fragment shaderze (nieefektywne rozwiązanie) 151
Zakres współrzędnych tekstury
Współrzędne tekstur są z zakresu <0, 1>, jeśli wyjdziemy poza, to zachowanie może być różne (domyślnie GL_REPEAT):
152
Mipmapping
Jeżeli „rozdzielczość” fragmentów jest mniejsza niż tekstury, to aby ustalić wartość tekstury należałoby uśrednić pewien obszar tekstury
153
Mipmapping, c.d.
Aby uniknąć uśredniania w czasie renderingu, wcześniej należy przygotować odpowiedni zestaw tekstur
Generowane są pomniejszone tekstury (za każdym razem dwukrotnie), tzw. poziomy (level)
Mipmapping może być wykorzystywany tylko kiedy zachodzi konieczność pomniejszenia tekstury, przy powiększaniu wykorzystywana jest zawsze tekstura poziomu 0
154
Mipmapping, c.d.
155
"MipMap Example STS101" by en:User:Mulad, based on a NASA image - Created by en:User:Mulad based on File:ISS from Atlantis - Sts101-714-016.jpg. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:MipMap_Example_STS101.jpg#/media/File:MipMap_Example_STS101.jpg
Mipmapping, c.d.
W zależności od ustawienia parametru GL_TEXTURE_MIN_FILTER: GL_NEAREST, GL_LINEAR
• mipmapping nie jest używany, stosowana jest tekstura poziomu 0
GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST • Wybierana jest jedna najlepsza mipmapa
GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR • Wybierane są dwie mipmapy i następuje interpolacja między nimi
156
Ładowanie tekstury
Qt ma klasę QImage do wczytywania i przetwarzania obrazków:
QImage tex(nazwa_pliku);
Następnie można wysłać teksturę do karty:
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
tex.width(), tex.height(),
0, GL_BRGA, GL_UNSIGNED_BYTE, tex.bits());
157
Ładowanie tekstury, c.d.
Można zlecić wykonanie mipmap:
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
Końcowe podłączenie do shadera:
int attr_tex = glGetUniformLocation(shaderProgram, "textureBitmap");
glUniform1i(attr_tex, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
158
Użycie tekstury we fragment shaderze
in vec2 fragTexCoor;
uniform sampler2D textureBitmap;
…
vec4 color = texture(textureBitmap, fragTexCoor);
159
Przykład OpenGL18
160
Inne zastosowania tekstur
Użycie tekstury do pokolorowania powierzchni było ich pierwszym zastosowaniem
161
Inne zastosowania tekstur, c.d.
Obecnie, kiedy mamy pełną kontrolę nad danymi z tekstury (przez program w shaderze) możemy wykorzystywać je w praktycznie dowolny sposób
W szczególności: Do mapowania parametrów cieniowania (np. specular, shininess)
Do mapowania normalnych
Do mapowania paralaksy
Do generowania cieni
162
Przykład OpenGL19
163
Mapowanie normalnych
Pozwala uzyskać efekt nierówności powierzchni
Wymaga niestety pewnych manipulacji geometrycznych
164
Mapa normalnych
165
W składowych RGB kodujemy wektory normalne do powierzchni
Przestrzeń stycznych
Wektor normalny pobrany z mapy musi zostać odpowiednio przetransformowany do współrzędnych powierzchni do której się odnosi
166
Przestrzeń stycznych, c.d.
Wektory T (tangent) i B (bitangent) można wyliczyć ze współrzędnych tekstury dla wierzchołków
167
Przestrzeń stycznych, c.d.
168
Macierz TBN
Rozszerzając macierz o wektor normalny uzyskujemy tzw. macierz TBN:
Która transformuje pomiędzy przestrzenią mapy normalnych a przestrzenią modelu i posiada użyteczną własność:
169
VBO
Obliczone wektory T i B dodajemy do VBO
Czyli obliczeń T i B możemy dokonać po wczytaniu modelu (jeśli model już ich nie zawiera)
170
Obliczenia w shaderach, wersja 1
Vertex shader: in vec3 normal;
in vec3 tgnU;
in vec3 tgnV;
out mat3 fragTBN;
…
vec3 T = normalize(norm_matrix * tgnU);
vec3 B = normalize(norm_matrix * tgnV);
vec3 N = normalize(norm_matrix * normal);
fragTBN = mat3(T, B, N);
171
Obliczenia w shaderach, wersja 1, c.d.
Fragment shader: in mat3 fragTBN;
…
vec3 texNormal = texture(textureNormal, fragTexCoor).rgb * 2.0 - 1.0;
texNormal = normalize(fragTBN*texNormal);
172
Przykład OpenGL20
173
Obliczenia w shaderach, wersja 2
Vertex shader: uniform Light light;
uniform vec3 eyePos;
out vec3 fragTanEyePos;
out vec3 fragTanLightPos;
out vec3 fragTanFragPos;
out vec3 fragTanNormal;
…
fragTanLightPos = TBN * light.pos;
fragTanEyePos = TBN * eyePos;
fragTanFragPos = TBN * fragPos;
fragTanNormal = TBN * N;
174
Obliczenia w shaderach, wersja 2, c.d.
Fragment shader: in vec3 fragTanEyePos;
in vec3 fragTanLightPos;
in vec3 fragTanFragPos;
in vec3 fragTanNormal;
…
vec3 texNormal = normalize(texture(textureNormal, fragTexCoor).rgb * 2.0 - 1.0);
…
vec3 lightDir = normalize(fragTanLightPos - fragTanFragPos);
175
Porównanie
Wersja 1: Prostsza koncepcyjnie
Wersja 2: Wydajniejsza (mnożenie macierzy dla wierzchołków, nie dla fragmentów)
176
Przykład OpenGL21
177
Mapowanie paralaksy
Chcemy, aby teksturowana powierzchnia wyglądała jakby miała elementy przestrzenne à la płaskorzeźba
178
Mapowanie paralaksy, c.d.
Ale użycie takiej tekstury nie daje w pełni zadowalających efektów…
179
Mapowanie paralaksy, c.d.
Aby uzyskać dobry wynik, należałoby obliczyć poprawny punkt przecięcia na powierzchni (pkt B zamiast A):
Mapa wysokości
Aby uzyskać taki efekt potrzebna jest tekstura z wysokością/głębokością
181
Przesunięcie tekstury, wersja 1
W wersji najbardziej prymitywnej przesuwamy współrzędne tekstury o wysokość tekstury odczytaną z mapy wysokości
182
Przesunięcie tekstury, wersja 1, c.d.
Fragment shader: uniform sampler2D textureDepth;
vec2 ParallaxMapping(vec2 texCoor, vec3 viewDir)
{
const float height_scale = 2.0/128.0; float height = texture(textureDepth, texCoor).r * height_scale;
vec2 p = viewDir.xy / viewDir.z * height;
return texCoor - p;
} 183
Przykład OpenGL22
184
Przesunięcie tekstury, wersja 2
Wersja 1 sprawdza się tylko dla kątów patrzenia niezbyt odchylonych od normalnej (do mniej więcej 45 stopni)
Ale przesunięcia tekstury można szukać iteracyjnie (steep parallax mapping)
185
Przesunięcie tekstury, wersja 3
Jeśli jeszcze uśrednimy wynik z dwóch końcowych poziomów, to unikniemy efektu schodków i efekt będzie znacznie lepszy…
186
Przykład OpenGL23
187
Uwagi praktyczne dot. mapowania paralaksy
Mapowanie paralaksy może być czasochłonnym procesem Dobór parametrów (np. liczba warstw)
Nie zawsze daje oczekiwany (łatwo zauważalny) wzrost realizmu Zależy od odległości od tekstury, wielkości szczegółów, różnic w głębokości
itp.
188
Skybox
Służy do renderowania statycznego tła
Wykorzystuje teksturę typu GL_TEXTURE_CUBE_MAP 6 tekstur jednakowej wielkości
(X+, X-, Y+, Y-, Z+, Z-)
Wartość pobierana dla dowolnego wektora 3D
189
Skybox, c.d.
190
SkyboxSet by Heiko Irrgang ( http://gamvas.com ) is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. Based on a work at http://93i.de.
Skybox, c.d.
Obiektem, na którym renderowane jest tło jest kwadrat wielkości ekranu, którego współrzędna Z gwarantuje, że faktycznie będzie w tle
Obliczenia Vertex shader oblicza jedynie współrzędne tekstury tła (na podstawie odwrotnej
macierzy złożenia widoku i perspektywy) • Macierz ta może być policzona wcześniej przez CPU
Fragment shader pobiera kolor fragmentu z tekstury • Może dokonać modyfikacji, np. przyciemnienia, rozjaśnienia itp.
Skybox renderujemy na końcu
191
Przykład OpenGL24
192
Dwa ciekawe efekty
Dysponując skyboxem możemy uzyskać w prosty sposób dwa efekty: Obiekty lustrzane (odbicie światła)
Obiekty przejrzyste (załamanie światła)
Wykorzystujemy tu fakt, że skybox zawiera widok świata, który jest niezmienny dla wszystkich punktów sceny
193
Odbicie lustrzane skyboxa
We fragment shaderze: vec3 I = normalize(fragPos.xyz - eyePos);
vec3 R = reflect(I, normalize(fragNormal));
…
vec3 mirror = texture(textureSkybox, R).rgb;
color = vec4(material.ambient + diffuse + specular + mirror, 1.0);
194
Przykład OpenGL25
195
Odbicie lustrzane skyboxa, c.d.
Jeżeli nie cały obiekt ma być lustrzany, można np. zastosować dodatkową teksturę obiektu definiującą, które fragmenty powierzchni są odbijające
196
Załamanie światła
We fragment shaderze: float ratio = 1.0 / 1.52;
vec3 I = normalize(fragPos.xyz - eyePos);
vec3 R = refract(I, normalize(fragNormal), ratio);
…
vec3 refracted = texture(textureSkybox, R).rgb;
color = vec4(material.ambient + diffuse + specular + refracted, 1.0);
197
Przykład OpenGL26
198
Dwie tekstury na płaski obiekt
Zastosowanie dwóch tekstur dla płaskich obiektów pozwala uzyskać efekt prześwitywania
199
Przykład OpenGL27
200
Cienie, cz. I
201
Cienie od kierunkowych źródeł światła
Mapowanie cieni
Podstawowym sposobem generowania cieni w OpenGL jest tzw. shadow mapping
Algorytm działa dwuprzebiegowo: W pierwszym przebiegu generowana jest mapa głębokości obiektów
widziana z perspektywy źródła światła
W drugim przebiegu generowany jest właściwy obraz
Jeżeli źródeł światła jest więcej, to pierwszy przebieg wykonywany jest dla każdego źródła światła, generując oddzielną mapę
202
Mapowanie cieni, c.d.
W obu przebiegach renderowana jest ta sama scena
Pierwszy przebieg jest znacznie szybszy: Nie wykonujemy cieniowania
Nie wykonujemy przesunięć normalnych, paralaksy itp.
Interesuje nas wyłącznie obliczenie współrzędnej Z każdego fragmentu
Pierwszy przebieg można jeszcze przyspieszyć: Stosując mniej szczegółowe modele obiektów
Pomijając obiekty, które nie rzucają cienia (np. ściany)
Pomijając obiekty, o których wiemy, że nie zostaną objęte mapą cienia 203
Framebuffer
Domyślnie rendering wykonywany jest do framebuffera, który jest następnie mapowany (w kooperacji z systemem okienkowym) na ekran
Można jednak utworzyć własny framebuffer
Framebuffer składa się z kilku buforów: Głębokości (jeden)
Kolorów (kilka, w domyślnym: GL_FRONT_LEFT, GL_FRONT_RIGHT, GL_BACK_LEFT, GL_BACK_RIGHT, we własnych 0…N)
Szablonu (jeden) 204
Nowy framebuffer
Aby utworzyć framebuffer należy stworzyć odpowiednie tekstury, utworzyć obiekt framebuffera (FBO) i podczepić do niego utworzone tekstury w roli odpowiednich buforów
205
Tekstury dla framebuffera (W x H)
Tekstura bufora koloru: glGenTextures(1, &tex_FBO_color);
glBindTexture(GL_TEXTURE_2D, tex_FBO_color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
W, H, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
Tekstura bufora głębokości: glGenTextures(1, &tex_FBO_depth);
glBindTexture(GL_TEXTURE_2D, tex_FBO_depth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
W, H, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
206
Framebuffer
Utworzenie samego framebuffera jest już proste: glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_FBO_color, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex_FBO_depth, 0);
207
Rendering do framebuffera
Aby rendering odbywał się do nowego framebuffera: glViewport(0, 0, W, H);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
A jak przywrócić stan pierwotny? Kiedyś było prościej:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Ale teraz: GLint FBO_screen;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &FBO_screen);
…
glBindFramebuffer(GL_FRAMEBUFFER, FBO_screen);
I jeszcze trzeba pamiętać o przywróceniu rozdzielczości: glViewport(0, 0, width(), height());
208
Przykład OpenGL28
209
Mapa cienia dla światła kierunkowego
Rendering z perspektywą równoległą zgodną z kierunkiem światła
Do ustalenia: Rozdzielczość framebuffera
Rozmiar framebuffera
Głębokości graniczne dla bufora głębokości (near plane, far plane)
210
Generacja mapy cienia
W tym przebiegu interesuje nas tylko bufor głębokości, zatem w utworzonym FBO nie będzie żadnego bufora kolorów
Aby dodatkowo zasygnalizować, że kolor ma nie być renderowany: glDrawBuffer(GL_NONE);
211
Macierz dla mapy cienia
W Qt (przykładowe dane): light_matrix.setToIdentity();
light_matrix.ortho(-5.0, 5.0, // x
-5.0, 5.0, // y
0.0, 20.0); // z
light_matrix.lookAt(
QVector3D(light.dir[0], light.dir[1], light.dir[2]).normalized()*5.0, // eye
QVector3D(0, 0, 0), // center
QVector3D(1, 1, 1)); // up 212
Shadery dla mapy cienia
Vertex shader (nie najbardziej optymalny): in vec3 position;
uniform mat4 light_matrix;
uniform mat4 m_matrix;
void main()
{
gl_Position = light_matrix*m_matrix*vec4(position, 1.0);
}
Fragment shader: void main() { }
213
Rendering z wykorzystaniem mapy cienia
Vertex shader wykonuje to samo obliczenie, co w przebiegu generacji mapy cienia (oprócz innych obliczeń)
Fragment shader sprawdza, czy dany fragment jest w cieniu, czy nie i odpowiednio modyfikuje kolor
214
Testowanie cienia bool inShadow(vec3 lightdir, vec3 normal)
{
vec3 p = (fragPosLight.xyz / fragPosLight.w)*0.5 + 0.5; if (p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0 || p.z > 1.0) return false;
float d = texture(textureShadow, p.xy).x;
float bias = max(0.01 * (1.0 - dot(normal, lightdir)), 0.001);
return p.z - bias > d;
}
215
Efekt moiré
216
bias
Aby uniknąć efektu moiré (samocieniowania) należy zastosować odpowiednie przesunięcie współrzędnej Z
Nie za małe, bo efekt nie zostanie usunięty
Nie za duże, bo powstanie nowy niepożądany efekt, tzw. efekt Piotrusia Pana (peter panning)
Może być adaptacyjne, jak w przykładzie na poprzednim slajdzie
Istnieją też inne, bardziej precyzyjne algorytmy 217
Przykład OpenGL29
218
PCF (Percentage-Closer Filtering)
Cienie ostre: Rzadko wyglądają dobrze
Ze względu na ograniczoną rozdzielczość mapy cienia widoczne są poszarpane krawędzie cieni
Cienie miękkie: Wyglądają lepiej
Rozmazują krawędzie cieni ukrywając poszarpanie
219
PCF, c.d.
Metoda PCF polega na próbkowaniu kilku/kilkunastu/kilkudziesięciu próbek mapy cienia zamiast jednej
float shadow = 0.0;
vec2 texel = 1.0/textureSize(textureShadow, 0);
for (float i = -1.5; i <= 1.51; i += 1.0)
for (float j = -1.5; j <= 1.51; j += 1.0)
{
float d = texture(textureShadow, p.xy + vec2(i, j)*texel).x;
shadow += (p.z - bias > d) ? 1.0 : 0.0;
}
return 1.0 - shadow/16.0; 220
PCF z ditheringiem
Zamiast próbkować 16 razy można 4 wykorzystując technikę ditheringu http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html
221
Przykład OpenGL30
222
Mapa cienia dla światła punktowego
Sprawa się komplikuje i, aby zrobić to efektywnie, będziemy potrzebowali…
223
Geometry shader
224
Nie tylko trójkąty
Do tej pory we wszystkich przykładach obiekty były rysowane jako zbiór trójkątów glDrawArrays…(GL_TRIANGLES, …);
albo
glDrawElements…(GL_TRIANGLES, …);
Są jednak także inne możliwości
225
Nie tylko trójkąty, c.d.
226
GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN
227
Nie tylko trójkąty, c.d.
228
Zastosowanie geometry shadera
Geometry shader dostaje na wejściu wierzchołki pojedynczego prymitywu, a następnie na ich podstawie może wygenerować dowolną liczbę dowolnych prymitywów (ale zawsze tych samych) i ich wierzchołki
229
OpenGL 4.5 rendering pipeline (przypomnienie)
230
Wejście do geometry shadera
Musimy określić, jakie prymitywy trafiają do geometry shadera z vertex shadera (tesselation shadera): layout (points) in;
• Dla: GL_POINTS
layout (lines) in; • Dla: GL_LINES i GL_LINE_STRIP
layout (triangles) in; • Dla: GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN
layout (lines_adjacency) in; • Dla: GL_LINES_ADJACENCY i GL_LINE_STRIP_ADJACENCY
layout (triangles_adjacency) in; • Dla: GL_TRIANGLES_ADJACENCY i GL_TRIANGLE_STRIP_ADJACENCY
231
Wejście do geometry shadera, c.d.
Za każdym razem geometry shader dostanie na wejściu tablicę wierzchołków: points: gl_in[1]
lines: gl_in[2]
triangles: gl_in[3]
lines_adjacency: gl_in[4]
triangles_adjacency: gl_in[6]
232
Zmienne wbudowane
W geometry shaderze mamy do dyspozycji tablicę gl_in[], której elementy zawierają pola: vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
float gl_CullDistance[];
Mamy też do dyspozycji licznik prymitywów: int gl_PrimitiveIDIn;
233
Wejście do geometry shadera, c.d.
Również każda wyjściowa dana z vertex shadera staje się automatycznie tablicą w geometry shaderze: Vertex shader:
out vec3 abc;
Geometry shader: in vec3 abc[];
Rozmiar tablicy określa rodzaj prymitywu (czyli 1, 2, 3, 4 lub 6)
234
Wyjście z geometry shadera
Musimy określić jakie prymitywy będzie generował geometry shader: layout (points, max_vertices = …) out;
layout (line_strip, max_vertices = …) out;
layout (triangle_strip, max_vertices = …) out;
W każdym przypadku musimy podać maksymalną liczbę wierzchołków jakie wygenerujemy
235
Przykład OpenGL31
236
Przykład OpenGL32
237
Cienie, cz. II
238
Cienie od punktowych źródeł światła
Cienie punktowych źródeł światła
Po pierwsze należy rozważyć, czy cienie światła punktowego nie mogą być mapowane tak, jak w przypadku światła kierunkowego
Może się tak stać, jeśli: Źródło światła jest „poza” sceną
Źródło światła oświetla mniej niż połowę sfery (np. światło typu spotlight)
Inne przypadki
239
Cienie punktowych źródeł światła, c.d.
Wówczas można jedynie zastosować tylko inne przekształcenie (perspektywiczne zamiast równoległego)
240
Mapa cienia typu CUBE
241
W pozostałych przypadkach, aby objąć całą przestrzeń otaczającą źródło światła, można zastosować teksturę typu CUBE
Mapa cienia typu CUBE, c.d.
Problem polega na tym, że nie można do framebufora podczepić textury typu CUBE jako bufora głębokości i liczyć, że to od razu zadziała…
Każdą ze ścian skyboxa należy potraktować oddzielnie i co za tym idzie dokonać mapowania cieni sześciokrotnie
Można to zrobić na 2 sposoby: W paintGL() najpierw 6 razy generujemy mapy cieni odpowiednio
zmieniając framebufory
Wykorzystać do tego geometry shader 242
Co należy przygotować na CPU?
6 macierzy widoku sceny z punktu źródła światła w kierunku wszystkich sześciu ścian
Teksturę typu CUBE
Framebufor
243
gl_Layer
Specjalna zmienna gl_Layer służy do wyboru ściany tekstury CUBE, która zostanie wykorzystana
244
gl_Layer tekstura
0 GL_TEXTURE_CUBEMAP_POSITIVE_X
1 GL_TEXTURE_CUBEMAP_NEGATIVE_X
2 GL_TEXTURE_CUBEMAP_POSITIVE_Y
3 GL_TEXTURE_CUBEMAP_NEGATIVE_Y
4 GL_TEXTURE_CUBEMAP_POSITIVE_Z
5 GL_TEXTURE_CUBEMAP_NEGATIVE_Z
Działanie geometry shadera
Geometry shader „rozmnaża” każdy trójkąt: for (int face = 0; face < 6; face++) {
gl_Layer = face;
for (int i = 0; i < 3; i++) {
fragPos = gl_in[i].gl_Position;
gl_Position = light_matrix[face] * fragPos;
EmitVertex();
}
EndPrimitive();
}
245
Przykład OpenGL33
246
PCF dla cieni od punktowych źródeł światła
Próbkowanie z tekstury typu CUBE odbywa się na bazie wektora 3D, a nie 2D
Może to powodować nadmierną liczbę próbkowań
Rozwiązaniem jest wykorzystanie tablicy z predefiniowanymi punktami w 3D
247
Przykład OpenGL34
248
HDR
249
High Dynamic Range
Historia
HDR (High Dynamic Range) ma swój rodowód w fotografii 1850, Gustave Le Gray
250
"Gustave Le Gray - Brig upon the Water - Google Art Project" by Gustave Le Gray - dwEJBbTOxE61pQ at Google Cultural Institute, zoom level maximum. Licensed under Public Domain via Commons - https://commons.wikimedia.org/wiki/File:Gustave_Le_Gray_-_Brig_upon_the_Water_-_Google_Art_Project.jpg#/media/File:Gustave_Le_Gray_-_Brig_upon_the_Water_-_Google_Art_Project.jpg
251
"StLouisArchMultExpEV-4.72" by Kevin McCoy - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:StLouisArchMultExpEV-4.72.JPG#/media/File:StLouisArchMultExpEV-4.72.JPG
-4
252
"StLouisArchMultExpEV-4.72" by Kevin McCoy - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:StLouisArchMultExpEV-4.72.JPG#/media/File:StLouisArchMultExpEV-4.72.JPG
-2
253
"StLouisArchMultExpEV-4.72" by Kevin McCoy - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:StLouisArchMultExpEV-4.72.JPG#/media/File:StLouisArchMultExpEV-4.72.JPG
+2
254
"StLouisArchMultExpEV-4.72" by Kevin McCoy - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:StLouisArchMultExpEV-4.72.JPG#/media/File:StLouisArchMultExpEV-4.72.JPG
+4
255
"StLouisArchMultExpEV-4.72" by Kevin McCoy - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:StLouisArchMultExpEV-4.72.JPG#/media/File:StLouisArchMultExpEV-4.72.JPG
Simple contrast reduction
256
"StLouisArchMultExpEV-4.72" by Kevin McCoy - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:StLouisArchMultExpEV-4.72.JPG#/media/File:StLouisArchMultExpEV-4.72.JPG
Local tone mapping
I jeszcze jeden przykład
257
by Dean S. Pemberton, 6 exposures illustrating the Steps to take an HDR Image
258
LDR
Standardowo bufory kolorów przechodują dane w formacie LDR
Każdy komponent jest z zakresu [0.0…1.0], z dokładnością 1/256
Problemy: Sumowanie oświetlenia z kilku źródeł może doprowadzić, do przekroczenia
wartości 1.0 (nastąpi wówczas przycięcie do tej wartości)
Brak możliwości elastycznego różnicowania jasności źródeł światła
Itp. 259
LDR, dodawanie kolorów
260
HDR
Rendering wykonujemy do bufora, który ma więcej niż 8 bitów na kolor i nie przycina wartości do zakresu [0.0…1.0] GL_RGB16F, GL_RGBA16F
GL_RGB32F, GL_RGBA32F
Następnie renderujemy ten bufor na prostokąt ekranu (podobnie jak w przypadku skyboxa) zmieniając wartości HDR na LDR
261
HDR output? http://images.nvidia.com/content/pdf/quadro/data-
sheets/NV_DS_Quadro_M6000_FEB15_NV_US_FNL_HR.pdf
262
HDR output? c.d. http://www.firstshowing.net/2015/dolby-impresses-cinemacon-with-
10000001-hdr-projection-demo
263
Typy zmiennoprzecinkowe w OpenGL
264
https://www.opengl.org/wiki/Small_Float_Formats
https://en.wikipedia.org/wiki/IEEE_754-1985
Tone mapping
Istnieje wiele metod na mapowanie HDR LDR, które przy okazji pozwalają uzyskać dodatkowe efekty
Mapowanie Reinharda Bardzo proste
Zachowuje większy kontrast dla ciemnych kolorów
265
Tone mapping, c.d.
Ekspozycja
266
Tone mapping, c.d.
W http://filmicgames.com/archives/75 można znaleźć kilka ciekawych wzorów
Np. Jim Hejl i Richard Burgess-Dawson:
267
Adaptacyjny tone mapping
Próbkowanie pobliskich 25 punktów, określenie ich jasności („luminancji”)
Na ich podstawie określenie lokalnej jasności i dopasowanie ekspozycji
268
Skala szarości
Niektórzy uważają, że jak coś jest czarno-białe, to robi się bardziej „artystyczne”
Wzór dla nich wygląda tak:
A jak ma być cieplej, to:
269
Tekstury HDR
Format RADIANCE (*.hdr) 1985 by Greg Ward, format RGBE
32 bit/pixel
Kod C do dekodowania: http://www.graphics.cornell.edu/~bjw/rgbe.html
Format OpenEXR (*.exr) 2003 by Industrial Light and Magic
16 lub 32 bit/kolor
Lib do dekodowania: http://www.openexr.com
Format KTX (*.ktx) Khronos
https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec 270
Korekcja gamma
271
Oko ludzkie nie odbiera jasności kolorów w sposób liniowy
Stosuje się tzw. korekcję gamma, która powoduje, że skala kolorów jest bardziej naturalna (pokazać obrazek gamma-test.png!)
Wartości parametru gamma wahają się w okolicy 2 CRT: 2.2
LCD: ~ 1.8
Projektory: ???
Korekcja gamma, c.d.
Niestety, fakt że kolory są przesunięte zgodnie ze krzywą gamma powoduje, że dodawanie kolorów nie jest do końca poprawne nawet w HDR
Można to oczywiście zignorować, albo pracować na kolorach „zlinearyzowanych”, a następnie nałożyć korektę gamma
W OpenGL możemy wykorzystać specjalny format tekstury GL_SRGB (GL_SRGB_ALPHA), oczywiście tylko do tekstur z informacją o kolorach (tekstury z normalnymi, mapami głębokości itp. nie podlegają korekcji gamma!)
272
Porównanie
273
Osłabianie światła (attenuation)
Stosowanie korekcji gamma pozwala na użycie liniowej zależności pomiędzy odległością od punktowego źródła światła, a jego intensywnością
Normalnie zależność ta jest kwadratowa, ale skoro stosujemy gammę w okolicach 2.0, to jest to akceptowalne…
274
Przykład OpenGL35
275
Rozmycie gaussowskie
Służy do miękkiego rozmycia obrazu
Wzór: określa wartość współczynnika w odległości (x, y) od środka
276
Rozmycie gaussowskie, c.d.
Tabela współczynników może wyglądać np. tak:
Jest symetryczna, współczynniki na końcu są pomijalnie małe
277
Rozmycie gaussowskie, c.d.
Gdyby bezpośrednio zastosować tabelkę z poprzedniego slajdu, to dla każdego pixela trzeba by wykonać 49 (7x7) sumowań
Na szczęście rozmycie gaussowskie ma tę ciekawą własność, że można je obliczyć jako złożenie rozmycia jednowymiarowego w kierunku X i kierunku Y
Każde z rozmyć jednowymiarowych to 7 składników, zatem ostatecznie będzie tylko 14 (7+7)
278
Rozmycie gaussowskie, c.d.
279
W celu uzyskania większego rozmycia można: Zwiększyć promień rozmycia
Rozmyć powtórnie już rozmyty obraz
(Rysunek poglądowy…)
Współczynniki
Współczynniki można albo obliczyć bezpośrednio z rozkładu Gaussa, albo wykorzystać trójkąt Pascala
280
Współczynniki, c.d.
281
Fragment shader dla rozmycia gaussowskiego uniform float horizontal;
…
float w[5] = {0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216};
vec2 texel = 1.0/textureSize(textureIn, 0);
texel.x *= horizontal;
texel.y *= (1.0 - horizontal);
color = texture(textureIn, fragTexCoor)*w[0];
for (int i = 1; i < w.length; i++)
color += texture(textureIn, fragTexCoor + vec2(texel.x*i, texel.y*i))*w[i] + texture(textureIn, fragTexCoor - vec2(texel.x*i, texel.y*i))*w[i];
282
Efekt poświaty
Aby uwidocznić w scenie obiekty o większej jasności stosuje się tzw. poświatę (glow, bloom), bo w większości przypadków nie ma fizycznej możliwości, aby zaświecić piksele mocniej niż (255, 255, 255)…
283
Efekt poświaty
Do uzyskania efektu poświaty należy: Wykryć fragmenty o jasności większej niż zadany próg (co jest możliwe
dzięki HDR)
Wykryte fragmenty zapisać do oddzielnej tekstury
Rozmyć teksturę z jasnymi fragmentami
Połączyć (dodać) obraz oryginalny i rozmyte jasne fragmenty
284
Efekt poświaty, c.d.
285
Przykład OpenGL36
286
Przetwarzanie w OpenGL36
287
Opóźnione cieniowanie
288
Deferred shading
Cel opóźnienia cieniowania
Proces cieniowania staje się coraz bardziej czasochłonny
Dla każdego fragmentu należy obliczyć: Światło odbite (diffuse)
Światło rozproszone (specular)
Cienie, na bazie map cieni
PCF cieni
Mapowanie normalnych
Mapowanie paralaksy
Mapowanie środowiskowe
itp. 289
Cel opóźnienia cieniowania, c.d.
Te wszystkie obliczenia wykonuje się także dla fragmentów, które potem są zasłaniane przez inne fragmenty, a więc de facto te obliczenia są całkiem zbędne
Problem pogłębia się wraz ze złożonością sceny oraz komplikacją shaderów obliczających cieniowanie 290
Wizualizacja 60 000 sfer
291
Rozwiązanie
Stąd pomysł, aby cieniowanie wykonać dopiero wtedy, kiedy będzie dokładnie wiadomo, które fragmenty są widoczne
Wykonywany jest zatem najpierw szybki przebieg renderingu, który: Zapamiętuje wszystkie potrzebne do cieniowania dane (np. pozycje, normalne,
kolory tekstur)
Stara się jak najmniej obliczać…
A następnie wykonywany jest przebieg cieniujący, który korzysta z przygotowanych danych, i który oblicza cieniowanie tylko dla widocznych fragmentów
292
G-buffer
Framebuffer, do którego w pierwszym przebiegu wykonywany jest rendering nosi nazwę „g-buffer”
„g” od geometry, gdyż zawiera także dane związane z geometrią fragmentów, takie jak położenie i normalne
G-buffer z przykładu, który zaraz nastąpi, zawiera dla każdego fragmentu: Pozycję
Informację, że fragment istnieje w danym miejscu ekranu
Wektor normalny
Kolor
Natężenie światła rozproszonego (ambient) 293
Tekstury g-bufora Pozycje i istnienie:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width(), height(), 0, GL_RGBA, GL_FLOAT, NULL);
Normale:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width(), height(), 0, GL_RGB, GL_FLOAT, NULL);
Kolor i ambient (tu: LDR):
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width(), height(), 0, GL_RGBA, GL_FLOAT, NULL);
Bufor głębokości:
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width(), height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); 294
Fragment shader do generacji g-bufora in vec3 fragNormal;
in vec3 fragPos;
in vec2 fragTexCoor;
uniform sampler2D textureBitmap;
uniform float ambient;
layout (location = 0) out vec4 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gColor;
void main() {
gPosition = vec4(fragPos, 0.0); // pozycja + istnienie
gNormal = normalize(fragNormal); // normalna
gColor = vec4(texture(textureBitmap, fragTexCoor).rgb, ambient); // kolor + ambient
}
295
Przykład OpenGL37
296
Przetwarzanie w OpenGL37
297
Zawartość g-bufora w OpenGL37
298
Cienie + PCF + HDR + Gauss + mapowanie normalnych + Opóźnione cieniowanie + SkyBox +… ?
Nie tylko, że się da - tak działa większość silników 3D!
299
Przykład OpenGL38
300
Przetwarzanie w OpenGL38
301
Wady opóźnionego cieniowania
Dwie podstawowe wady opóźnionego cieniowania: Wszystkie obiekty muszą być przetwarzane w taki sam sposób
Nie można bezpośrednio korzystać z kanału alfa
Istnieją obejścia w/w problemów Warunkowe/wieloprzebiegowe cieniowanie
Łączenie opóźnionego cieniowania z normalnym (trzeba tylko zachować i wykorzystać bufor głębokości z etapu tworzenia g-bufora)
Pojawiają się za to nowe możliwości…
302
SSAO
303
Screen-Space Ambient Occlusion
SSAO wymyślone przez Crytek dla gry Crysis
304
"Screen space ambient occlusion" by Vlad3D at en.wikipedia - Transferred from en.wikipedia. Licensed under Public Domain via Commons - https://commons.wikimedia.org/wiki/File:Screen_space_ambient_occlusion.jpg#/media/File:Screen_space_ambient_occlusion.jpg
Vladimir Kajalin, 2007
AO (Ambient Occlusion)
Generowanie zacienienia miejsc z mniejszym dostępem światła w dobrze oświetlonym otoczeniu
Sprawdzenie: Ile przestrzeni w otoczeniu każdego
punktu jest wolne od obiektów?
305
AO, c.d.
Wykonanie takiego testu w przestrzeni sceny byłoby dość skomplikowane:
Obiekty są definiowane jako powierzchnie, a nie objętości
Trudno określić, dla których miejsc i jak gęsto dokonywać sprawdzania
306
SSAO
Stąd powstał prawdopodobnie pomysł, aby detekcji zacienienia dokonywać w przestrzeni ekranu, czyli już po etapie wygenerowania g-bufora
Po dołączeniu do g-bufora tekstury z głębokością zawiera on przybliżone dane 3D sceny
Detekcja odbywa się dla każdego fragmentu: współrzędne x i y to współrzędne fragmentu we framebuferze, z jest pobierane z g-bufora
307
Bufor głębokości w g-buforze
Po przekształceniu perspektywicznym głębokość (współrzędna Z) nie jest liniowa
To bardzo utrudniałoby próbkowanie w shaderze SSAO
Dlatego zamiast wykorzystać automatycznie tworzony bufor głębokości, należy utworzyć dodatkowy, w którym zapisywane wartości będą liniowe
308
Mapowanie głębokości
Do przejścia z (near, far) na (0, 1): Zamiast:
OpenGL stosuje:
309
SSAO, c.d.
310
Próbkowanie otoczenia
Nie da się przejrzeć całej kuli otaczającej punkt, ale można wygenerować kilkadziesiąt punktów próbkujących
311
Kształt otoczenia
Próbkowanie otoczenia w kuli jest najprostszym rozwiązaniem, jednak ma tę wadę, że nawet całkiem płaska ściana jest wykrywana jako w 50% zacieniona…
Aby tego uniknąć stosuje się półkule, zorientowane zgodnie z normalną do badanej powierzchni
312
Wynik SSAO
313
Usprawnienia SSAO
Punkty próbkowania powinny być wylosowane niejednorodnie – więcej punktów powinno znaleźć się bliżej środka kuli/półkuli
Punkty powinny być dodatkowo randomizowane dla poszczególnych fragmentów
Tekstura uzyskana po SSAO powinna zostać rozmyta
314
Schemat działania
315
Zamiast:
Kontrola zasięgu
Dodatkowe sprawdzenie, które pozwala uniknąć zacieniania pomiędzy fragmentami znacząco się różniącymi głębokością
316
Cechy SSAO
Szybkość działania SSAO nie zależy od: Złożoności sceny
Liczby świateł
Szybkość działania zależy od: Rozdzielczości ekranu (czyli liczby fragmentów)
Liczby próbek na fragment
317
Konfiguracja SSAO
SSAO posiada kilka parametrów konfiguracyjnych, których dobór jest kluczowy dla uzyskania dobrego efektu: Promień półkuli
Liczba próbek w półkuli
Rozkład próbek w półkuli
Stopień rozmycia
318
Przykład OpenGL39
319
Rendering instancyjny
320
Instancing
Rendering wielu jednakowych/podobnych obiektów Jeżeli każdy obiekt będzie
zlecany do renderingu oddzielnie przez CPU, to czas będzie tracony na: Przesył danych do GPU i wywołanie
funkcji OpenGL
Przygotowanie danych dla shaderów w GPU
321
Rendering instancyjny
Problem dostrzeżono już dawno i od OpenGL 3.1 jest możliwość zlecenia renderingu wielu kopii tego samego obiektu (a właściwie zawartości VBO)
322
Rendering instancyjny, c.d.
Wywołanie renderingu instacyjnego: glDrawArraysInstanced(mode, first, count, instances_count);
glDrawElementsInstanced(mode, count, type, void *indices, instances_count);
Zamiast pojedynczego: glDrawArrays(mode, first, count);
glDrawElements(mode, count, type, void *indices);
Dodatkowy argument instances_count mówi, ile razy rendering ma być wykonany
323
Rozróżnianie instancji
Jeżeli w shaderach nie rozróżnimy instancji, to wizualnie nic nie uzyskamy…
Najprostszym sposobem jest wykorzystanie zmiennej gl_InstanceID dostępnej w vertex shaderze
gl_InstanceID Przyjmuje wartości 0..instances_count-1
Jeżeli nie wykorzystujemy renderingu instancyjnego, to zmienna ta ma zawsze wartość 0
324
Przykład OpenGL40
325
Rozróżnianie instancji, c.d.
Użycie gl_InstanceID czasami jest jednak niewygodne
Bardziej uniwersalna metoda polega na: Przygotowaniu VBO z danymi dla poszczególnych instancji
Podłączeniu tych danych jako wejściowych dla vertex shadera
326
VBO z parametrami instancji
Przygotowanie i przesłanie do GPU identyczne, jak dla VBO z parametrami wierzchołków, np.:
struct InstanceData { GLfloat x, y, z, r, g, b; };
InstanceData positions_and_colors[1000];
…
GLuint VBO_positions;
glGenBuffers(1, &VBO_positions);
glBindBuffer(GL_ARRAY_BUFFER, VBO_positions);
glBufferData(GL_ARRAY_BUFFER, sizeof(InstanceData)*1000, positions_and_colors, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); 327
VBO z parametrami instancji, c.d.
Podczepienie VBO pod VAO: glBindVertexArray(VAO);
…
attr = glGetAttribLocation(shaderProgram, "instancePosition");
glBindBuffer(GL_ARRAY_BUFFER, VBO_positions);
glVertexAttribPointer(attr, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), 0);
glVertexAttribDivisor(attr, 1);
glEnableVertexAttribArray(attr);
328
VBO z danymi wierzchołków i VBO z danymi instancji
W pseudokodzie, poszczególne inwokacje vertex shadera będą otrzymywać następujące dane:
for (int i = 0; i < inst_count; i++)
for (int k = 0; k < vert_count; k++)
vertex_shader(uniformy, VBO_vertex[k], VBO_instance[i]);
329
Przykład OpenGL41
330
Tesselation shaders
331
Tesselacja
Wygenerowanie prymitywów (potencjalnie ich dużej ilości) na podstawie punktów kontrolnych (potencjalnie małej ilości)
LOD (Level of Detail), czyli uzależnienie szczegółowości modelu od jego wielkości na ekranie
332
Trzy etapy tesselacji
333
Dostępne od OpenGL 4.0
Opcjonalna tesselacja następuje po vertex shaderze
Tesselation control shader (TCS)
Vertex shader generuje zestawy wierzchołków
Zestaw nosi nazwę patch, a wierzchołki, które się na niego składają to tzw. punkty kontrolne (control points, CP)
Każdy patch ma zawsze tę samą liczbę punktów kontrolnych, maksymalnie 32, choć dana implementacja OpenGL może podnieść ten limit, sprawdzić to można poprzez: glGetIntegerv(GL_MAX_PATCH_VERTICES, …);
Liczbę punktów kontrolnych ustawia się poleceniem glPatchParameteri(GL_PATCH_VERTICES, …);
334
Tesselation control shader, c.d.
TCS jest wywoływany dla każdego wyjściowego punktu kontrolnego
TCS musi określić stopień (czyli gęstość) tesselacji wypełniając tablice gl_TessLevelOuter[] i gl_TessLevelInner[] Maksymalna wartość określa GL_MAX_TESS_GEN_LEVEL, obecnie na ogół jest to 64
TCS może zmienić punkty kontrolne, dodać im nowe atrybuty itp.
TCS może też wygenerować parametr dla całego patcha, poprzedzając deklarację słowem kluczowym patch, np.: patch out vec3 jakisparametr;
335
Tesselation control shader, c.d.
Dane wejściowe punktów kontrolnych dostępne są w tablicy gl_in[]
Dane wyjściowe zapisuje się w tablicy gl_out[]
Numer aktualnie przetwarzanego wyjściowego punktu kontrolnego określa zmienna wbudowana gl_InvocationID
Każdy dodatkowy atrybut punktu kontrolnego na wejściu i na wyjściu jest tablicą
Liczba punktów na wyjściu z TCS określa deklaracja, np.: layout (vertices = 3) out;
336
Tesselation engine
Kiedy patch zostanie obrobiony przez TCS (co oznacza, że tablice kontrolne tesselacji też zostały wypełnione) do pracy przystępuje tesselation engine, który generuje współrzędne punktów tesselacji
Dla każdego wygenerowanego punktu tesselacji uruchamiany jest tesselation evaluation shader (TES), który ma dostęp do wszystkiego, co ustawił TCS oraz dodatkowo do współrzędnych wygenerowanego wierzchołka poprzez zmienną wbudowaną gl_TessCoord 337
Tesselation evaluation shader (TES)
Zadaniem TES jest wygenerowanie wierzchołków dla docelowych prymitywów (trójkątów, linii lub punktów)
TES określa sposób (tryb) przeprowadzenia tesselacji: layout (triangles) in;
layout (quads) in;
layout (isolines) in;
Dostęp do punktów kontrolnych uzyskiwany jest przez tablicę gl_in[], dostęp do ich atrybutów poprzez zdefiniowane tablice, dostęp do wygenerowanego punku tesselacji poprzez gl_TessCoord
338
Tryby tesselacji
Dostępne są trzy tryby tesselacji: Trójkąty
Kwadraty
Izolinie
339
Tryb tesselacji: trójkąt
Występuje, gdy kompilator znajdzie w TES deklarację: layout (triangles) in;
W trójkącie generowane są punkty wg schematu
340
gl_TessLevelOuter
Podział każdej krawędzi jest definiowany osobno, aby możliwe było dokładne dopasowanie sąsiadujących patchy
341
Tryb tesselacji: trójkąt, c.d.
Współrzędne są w tzw. systemie barycentrycznym https://pl.wikipedia.org/wiki/Współrzędne_barycentryczne_(matematyka)
Układ punktów zależny jest tylko od gl_TessLevel*[], nie zależy w szczególności od punktów kontrolnych
342
Przykład OpenGL42
343
Tryb tesselacji: kwadrat
Występuje, gdy kompilator znajdzie w TES deklarację: layout (quads) in;
W kwadracie generowane są punkty wg schematu
344
Tryb tesselacji: kwadrat, c.d.
Współrzędne są w układzie kartezjańskim
Układ punktów zależny jest tylko od gl_TessLevel*[], nie zależy w szczególności od punktów kontrolnych
345
Przykład OpenGL43
346
Tryb tesselacji: izolinie
Występuje, gdy kompilator znajdzie w TES deklarację: layout (isolines) in;
W kwadracie generowane są punkty wg schematu
347
Dodatkowe modyfikatory
W deklaracji TES: layout (mode, …) in możliwe jest podanie dodatkowych modyfikatorów:
Dotyczących rozkładu: equal_spacing, fractional_even_spacing, fractional_odd_spacing
Generującego punkty: point_mode
Dotyczących „skrętności” trójkątów wynikowych: cw, ccw 348
Krzywe Bézier
Pierre Étienne Bézier 1960(?) (Sergei Natanovich Bernstein 1912, algorytm Paula de Casteljau 1959)
https://en.wikipedia.org/wiki/Bézier_curve
349
Płaty Bézier
350
"Bicubic Patches" by Philip Rideout - Own work. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:Bicubic_Patches.png#/media/File:Bicubic_Patches.png
Ed Catmull – model słonia Gumbo
Składa się z płatów Bézier
Każdy płat kontrolowany jest przez 16 punktów (w tym przykładzie)
Przykład OpenGL44
351
Mapowanie terenu
Generowanie terenu na podstawie mapy wysokości
Użycie instancingu do wygenerowania bazowej siatki
Użycie tesselacji do wygenerowania gęstej siatki Zmienny LOD – Level of Detail
352
Bjørn Sandvik, http://blog.thematicmapping.org/2013/10/terrain-building-with-threejs-part-1.html
Przykład OpenGL45
353
Mgła
354
Mgła
Efekt mgły pozwala na bardziej realistyczne odwzorowanie otwartych przestrzeni dodając im dodatkowej głębi
355 http://www.iquilezles.org/www/articles/fog/fog.htm
Mgła, c.d.
Mgła pozwala także na uniknięcie problemu skończoności modelu terenu – przy odpowiednio dodanych parametrach mgły „końca świata” nie będzie widać
Efekt mgły można oczywiście także stosować we wnętrzach
356
Mgła w funkcji odległości
W OpenGL 1.0 można było uzyskać efekt mgły wykorzystując jeden z trzech predefiniowanych modeli Liniowy:
Wykładniczy:
Wykładniczy, wersja 2:
357
Mgła w funkcji odległości, c.d.
Gdzie: f – współczynnik „mglistości”
z – odległość fragmentu od kamery
d – gęstość mgły
start, end – zasięg mgły w modelu liniowym
Obecnie w shaderach można zaprogramować dowolną funkcję mgły – należy dobrać taką, która najlepiej odpowiada wymaganiom
358
Mgła w shaderach
Należy ustalić kolor mgły, a następnie na ten właśnie kolor ustawić kolor tła
Vertex shader (lub tesselation evaluation shader): float z = length(gl_Position - eyePos);
fogFactor = clamp(exp(-fogDensity*fogDensity * z*z), 0.0, 1.0);
Fragment shader: color = mix(vec4(fogColor, 1.0), color, fogFactor);
359
Mgła w funkcji wysokości
Efekt zalegania mgły „w dolinach”
Natężenie mgły zależne od wysokości terenu h:
Jest to wzór przybliżony, ale daje dobre rezultaty wizualne…
360
Mgła w funkcji wysokości, c.d.
Można też policzyć dokładniej:
361
Przykład OpenGL46
362
Głębia ostrości
363
Depth of Field
Głębia ostrości
Jest to kolejny efekt mający na celu podniesienie realizmu renderowanej sceny
Wynika z braku możliwości takiego ustawienia układu optycznego (kamery, aparatu fotograficznego, źrenicy oka), aby obiekty o różnym oddaleniu od obiektywu były ostre na elemencie światłoczułym (kliszy, detektorze, siatkówce)
364
Krążek rozmycia (Circle of Confusion)
365
CoC zależy od średnicy soczewki (proporcjonalnie)
CoC jest ograniczone dla obiektów „w nieskończoności”
CoC nie jest ograniczone dla obiektów bliskich soczewce Obiekty bliskie są bardziej rozmyte niż odległe
Krążek rozmycia, c.d.
Wzór określający CoC:
f – ogniskowa
d – średnica apertury
https://en.wikipedia.org/wiki/Circle_of_confusion
366
Krążek rozmycia w praktyce
367
Głębia ostrości – czy zawsze warto stosować?
Jeżeli chcemy, aby nasza scena przypominała zdjęcie lub film, to można to rozważyć
Jeżeli scena ma przypominać świat rzeczywisty, jakim widzimy go dookoła (zakładając dobry wzrok lub poprawne szkła korygujące), to już niekoniecznie… Bo nasze oczy automatycznie dostosowują się do różnej odległości poprzez
akomodację, tak aby to na co patrzymy, było maksymalnie ostre
Jeśli jakiś fragment obrazu rozmyjemy, to żadne wytężanie wzroku już nie pomoże…
368
Porównajmy…
369 http://www.trusim.com/
Metody
Istnieje kilka metod na uzyskanie efektu głębi ostrości, różnią się uzyskaną dokładnością oraz złożonością Wykorzystanie akumulacji
Ray tracing dla obszaru soczewki
Wykorzystanie bufora głębokości
Metody oparte o bufor głębokości są najszybsze, tym bardziej, że bufor głębokości i tak często jest generowany jako element g-bufora
370
Metody oparte o bufor głębokości
Sposób 1 – bardziej poprawny Należy przeprowadzić rozmycie zależne od różnicy odległości fragmentu i
odległości o prawidłowej ostrości
Tu ponownie jest kilka metod do wyboru
Sposób 2 – mniej poprawny Wykonujemy rozmytą kopię całej sceny
W trakcie końcowego renderingu mieszamy obraz rozmyty i ostry w stosunku zależnym od różnicy między odległością fragmentu i odległości o prawidłowej ostrości
371
Przykład OpenGL47
372
Obiekty przejrzyste
373
Kanał alfa
Model kolorów w OpenGL to RGBA
Kanał alfa dodany w 1971-72 (Edwin „Ed” Catmull & Alvy Ray Smith)
Nazwa od greckiej litery α w równaniu interpolacji liniowej:
374
Kanał alfa, c.d.
Zgodnie z konwencją A określa stopień nieprzejrzystości (1.0 - całkowite zasłanianie, 0.0 całkowita transparentność)
Domyślnie A jest ignorowane – jeśli chcemy korzystać z przejrzystości należy włączyć jej obsługę i określić tryb mieszania kolorów (blending)
Albo wykorzystać wartość A we fragment shaderze do odrzucania fragmentu
375
Test alfa
Ta druga możliwość nosi nazwę testu alfa (alpha test) i polega na tym, że we fragment shaderze, po ustaleniu koloru fragmentu wykonujemy operację: if (color.a < jakiś_próg)
discard;
Ponieważ odrzucanie jest zerojedynkowe, aby wynikowy obraz nie był postrzępiony, konieczne jest użycie antyaliasingu
376
Testowe drzewo
377
model
tekstura
Przykład OpenGL48
378
Włączenie mieszania (blending)
Mieszanie pozwala określić jaka wartość RGBA ma zostać umieszczona w framebuforze, po obliczeniu nowych wartości przez fragment shader
Aby włączyć funkcję mieszania należy wywołać funkcję: glEnable(GL_BLEND);
W przeciwnym wypadku do framebufora zostanie zawsze skopiowana nowa wartość RGBA policzona we fragment shaderze
379
Określenie mieszania
Funkcje określające sposób mieszania wartości już istniejącej w framebuforze oraz wartości obliczonej przez fragment shader: glBlendFunc(GLenum src, GLenum dst)
glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
Druga wersja umożliwia ustawienie innego mieszania dla komponentu A, a innego dla RGB
Możliwe stałe src i dst przedstawia tabelka na następnym slajdzie src oznacza wartość z fragment shadera
dst oznacza wartość z framebufora
380
381
Funkcja mieszania
Po określeniu, jak ma zostać przetworzone src i dst, ostateczny wynik może zostać: Dodany: p(src) + p(dst) operacja domyślna
Odjęty: p(src) – p(dst)
Odjęty, ale odwrotnie: p(dst) – p(src)
Ustalony jako minimum: min(p(src), p(dst))
Ustalony jako maximum: max(p(src), p(dst))
Określa się to funkcją glBlendEquation(), lub glBlendEquationSeparate() z parametrem: GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN albo GL_MAX
Przykład mieszania
Najbardziej popularne ustawienie to: glEnable(GL_BLEND);
• Włącza mieszanie
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD); • Ostateczny kolor umieszczony we framebufferze to:
• Czyli stopień wymieszania z „tłem” określa Asrc ustalone przez fragment shader
383
Mieszanie we fragment shaderze
Mieszanie we fragment shaderze jest dostępne na razie jedynie jako rozszerzenie na OpenGL ES: EXT_shader_framebuffer_fetch https://www.khronos.org/registry/gles/extensions/EXT/EXT_shader_framebuffer_fetch.txt
Rozszerzenie udostępnia zmienną gl_LastFragData z aktualną zawartość framebufera
384
Rendering obiektów przejrzystych
Umieszczenie w scenie obiektów przejrzystych wymaga następujących kroków:
Włączenie odpowiedniego mieszania kolorów
Wyrenderowanie całej sceny z pominięciem obiektów prześwitujących
Wyłączenie uaktualniania bufora głębokości glDepthMask(GL_FALSE);
Wyrenderowanie obiektów przejrzystych w kolejności od najdalszych do najbliższych
385
Sortowanie względem głębokości
Jest zadaniem nie zawsze jednoznacznym…
386
Przykład OpenGL49
387
A może można prościej?…
Prościej, czyli bez oddzielania nieprzejrzystych obiektów od przejrzystych i bez sortowania tych drugich…
Można:
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
388
Alpha to coverage
Realizowany przez kartę algorytm alfa test, ale na poziomie antyaliasingu
Liczba odrzuconych fragmentów zależy od wartości alfa
Wadą jest to, że zaczyna ładnie wyglądać dopiero od 32 próbek na fragment
389
Alpha to coverage dla różnej liczby próbek
390
Przykład OpenGL50
391
Billboardy
Płaszczyzny (na ogół prostokąty), które, niezależnie od położenia kamery, są zawsze zwrócone do niej na wprost
392
Zastosowanie billboardów
Wizualizacja systemów cząsteczkowych Chmury
Ogień
Dym
Gwiazdy
itp.
W pewnych okolicznościach także np. drzewa, kempki trawy itp.
Paski życia (health bar) 393
„Eternal lands1” autorstwa Eternal Lands developers; screenshot taken by Sir Lothar - Eternal Lands. Licencja CC BY 3.0 na podstawie Wikimedia Commons - https://commons.wikimedia.org/wiki/File:Eternal_lands1.jpg#/media/File:Eternal_lands1.jpg
Generowanie billboardów
Jedną z metod jest generowanie billboardu w geometry shaderze Geometry shader na wejściu dostaje położenie punktu centralnego
i rozmiar billboardu
Geometry shader generuje 2 trójkąty stanowiące prostokąt
Geometry shader generuje także współrzędne tekstury
Właściwy rendering wykonuje fragment shader nakładając odpowiednią teksturę
394
Przykład OpenGL51
395
Chmury
396 https://tel.archives-ouvertes.fr/tel-00319974/file/defense.pdf
Chmury, dym, ogień, gwiazdy itp.
Na drodze symulacji (która również może być realizowana na GPU!) generujemy punkty (cząstki), których ruch i położenie imituje te zjawiska
Wizualizacja chmury cząstek najczęściej jest wówczas realizowana przy pomocy billboardów z teksturą o dużej przejrzystości (małym alfa)
397
Przykład OpenGL52
398
Przykład OpenGL53
399
Obrazowanie objętościowe
400
Volume rendering
Medycyna
Obrazowanie wolumetryczne wykorzystywane jest głównie w medycynie (ale też np. w archeologii)
Tomografia komputerowa (CT) Pierwszy tomograf powstał w 1968 roku
(nobel w 1979)
Pierwszy pacjent zbadany w 1972
Rezonans magnetyczny (MRI) Wymyślony w 1971 roku (nobel w 2003)
Pierwszy przydatny skan w 1980
Pozytonowa tomografia emisyjna (PET) – dodatkowo z CT lub MRI 401
Seria zdjęć
Wynikiem działania CT czy rezonansu jest seria obrazów o tej samej rozdzielczości przedstawiającej przekroje fragmentu ciała pacjenta (głowy, tułowia, stawu kolanowego itp.) w jednakowych odstępach
402
Seria zdjęć, c.d.
Sama seria zdjęć przynosi już lekarzowi bardzo dużo informacji
Chcemy jednak uzyskać także możliwość wizualizacji 3D oraz uzyskania dowolnego przekroju badanej części ciała
403
Główne problemy
Badanie MRI trwa kilka-kilkanaście minut – nie zawsze jest możliwe całkowite unieruchomienie pacjenta
Zbyt niska rozdzielczość obrazów lub zbyt duży odstęp między przekrojami
Nieostre obrazy
Szumy i inne zakłócenia
Różnice pomiędzy obrazami w poszczególnych warstwach
Mnogość formatów danych
404
Wyzwanie
Jednoczesna wizualizacja danych z wielu źródeł…
405
DICOM
Digital Imaging and Communications in Medicine
Format przechowywania obrazów medycznych Wiele dodatkowych danych dotyczących pacjenta, metody pomiaru itp.
Opracowana dla ujednolicenia wymiany danych
Główna wada: format jest zbyt obszerny, wielość możliwości powoduje, że w różnych programach lub placówkach medycznych są one wypełniane w różny sposób
Osobnym problemem jest brak mechanizmów szyfrowania/uwierzytelniania
406
Trywialna rekonstrukcja 3D
Polega na wyrenderowaniu odpowiedniej liczby prostokątów z naniesionymi teksturami
Pojawi się problem z obserwacją dla niewielkich kątów patrzenia
Pojawi się problem z kolejnością rysowania prostokątów
Dobór kanału alpha, progu odcięcia
Stosuje się na ogół rzutowanie równoległe zamiast perspektywicznego
Uwaga na problem lustrzanego odbicia! Ciało ludzkie jest prawie symetryczne, więc łatwo o pomyłkę
407
Przykład OpenGL54
408
Kolejność renderingu warstw
Kolejność renderingu warstw ma znaczenie
Należy wyświetlać je w kolejności od najdalszej do najbliższej (w stosunku do obserwatora)
409
Przykład OpenGL55
410
Tekstury 3D
W OpenGL mamy do dyspozycji także tekstury trójwymiarowe
Ich obsługa (alokowanie, wczytywanie, dostęp w shaderach) jest identyczny jak w przypadku tekstur 2D, dodana jest jedynie trzecia współrzędna określająca odpowiednio rozmiar tekstury lub położenie punktu w teksturze
411
Tekstury 3D, c.d.
Jedno z podejść polega na tym, że w czasie renderingu przekształcane są współrzędne tekstury 3D a nie obiektu
Często voxele (trójwymiarowe odpowiedniki pixeli) nie są sześcianami – OpenGL nie uwzględnia takiej możliwości (podobnie jak niekwadratowych pixeli) bezpośrednio, trzeba to uwzględnić w odpowiednim skalowaniu macierzy, np.: v_matrix.scale(1.0, width*spacing_x/(height*spacing_y),
width*spacing_x/(depth*spacing_z)); 412
Przykład OpenGL56
413
Inne metody
Splatting Rzutowanie vexeli jako billboardy
Shear warp Odpowiednie przeskalowanie i przesunięcie źródłowych tekstur
https://graphics.stanford.edu/papers/shear/shearwarp.pdf
414
Odtwarzanie geometrii
Innym sposobem wizualizacji jest próba rekonstrukcji geometrii na podstawie danych wolumetrycznych
Wymaga określenia progu, który umożliwi detekcję izopowierzchni – jest najłatwiejsze w przypadku kości, czy zastosowania kontrastu
415
Algorytm marching cubes
Wymyślony w 1987 (Williama E. Lorensen, Harvey E. Cline)
Algorytm opatentowany w USA w 1987, jednak już wygasł…
Podział przestrzeni na sześciany
416 "Marchingcubes-head". Licensed under CC BY-SA 2.5 via Commons - https://commons.wikimedia.org/wiki/File:Marchingcubes-head.png#/media/File:Marchingcubes-head.png
Marching cubes
W każdym z wierzchołków sześcianu sprawdzane jest, czy wartość pola skalarnego jest większa, czy mniejsza od progu
Jest zatem 256 możliwości (28), jednak ze względu na różne symetrie autorzy wyodrębnili jedynie 15 przypadków kanonicznych
417
„MarchingCubes” autorstwa Jmtrivial (talk). Licencja GPL na podstawie Wikimedia Commons - https://commons.wikimedia.org/wiki/File:MarchingCubes.svg#/media/File:MarchingCubes.svg
Teksturowanie proceduralne
418
Tekstury statyczne
Tradycyjne tekstury
Zaleta: dzięki sprzętowej realizacji i optymalizacji są bardzo szybkie
Wady: • Zajmują pamięć GPU
• Są statyczne
• Sprawiają kłopot przy projektowaniu obiektów o skomplikowanym kształcie
419
420
Tekstury proceduralne
Innym pomysłem na określenie koloru fragmentu jest jego algorytmiczne obliczenie, tak aby obiekt przypominał wykonanie z jakiegoś naturalnego materiału (klasyczne przykłady to drewno i marmur)
Tekstury proceduralne mogą być 2D lub 3D (a nawet 4D)
Wadą jest brak pełnej kontroli grafików nad powstającą teksturą (w odróżnieniu od tekstur klasycznych, którym zawsze można coś dorysować)
421
Tekstury proceduralne
Tekstury proceduralne mogą być obliczane: Off-line, czyli np. przed renderingiem sceny czy animacji i następnie
załadowane jako tradycyjne tekstury statyczne do GPU – to rozwiązanie dotyczy głównie tekstur 2D
On-line, czyli na bieżąco w shaderze w czasie redneringu – obliczenia powinny być zatem maksymalnie uproszczone i zoptymalizowane
Podobnie, jak tekstury tradycyjne, mogą być wykorzystywane także do zaburzania normalnych, przesunięcia paralaksy, generowania wysokości terenu itp. 422
Tekstury proceduralne 3D
Wartość tekstury obliczana jest w funkcji trójwymiarowej pozycji
mapowanie do współrzędnych tekstury UV nie jest potrzebne
Uzyskujemy efekt wycięcia obiektu z materiału
423
Tekstury proceduralne 3D, c.d
Często stosowany schemat:
gdzie: c – kolor wynikowy
p – funkcja periodyczna
n – wykładnik funkcji periodycznej (zmienia szerokości warstw)
f – funkcja pozycji (może być także zależna od czasu t)
s – funkcja szumu
424
Przykład 1 - drewno
Funkcje:
p – funkcja piłokształtna
425
Przykład 2 - marmur
426
Funkcje:
p – funkcja sin
Przykład OpenGL57
427
Świat nie jest idealny
Drewniane klocki z ostatniego przykładu wyglądają jakby: Wyciął je stolarz perfekcjonista (idealnie centralnie i symetrycznie)
Zostały wycięte z idealnego drzewa (słoje regularne i koncentryczne, o stałej grubości)
Marmurowe klocki wyglądają jeszcze gorzej…
428
Wprowadzenie zniekształceń
Można wprowadzić macierz przekształcenia tekstury, która poprzez translację i rotację, wyeliminuje problem idealnego rzemieślnika (stolarza, kamieniarza)
Do wprowadzenia zakłóceń w strukturze należy użyć funkcji szumu s
429
Funkcje szumu
Funkcje szumu służą do wprowadzenia kontrolowanej losowości do modelu tekstury
Szum musi być powtarzalny
Najczęściej jest realizowany poprzez pseudolosową funkcję s, której parametr (1,2,3…n wymiarowy) jest przekształcany na liczbę z zakresu [-1, 1] lub [0, 1]
Dla tego samego parametru funkcja musi zwrócić tę samą wartość
430
Funkcje szumu, c.d.
Funkcja szumu powinna mieć następujące własności: Średnia wartość szumu powinna być w połowie przedziału
Charakter statystyczny nie powinien zależeć od translacji i rotacji (powinien być izotropowy)
W GLSL dostępne są funkcje noiseN(…), jednak na większości kart nie działają Kompilacja shadera uda się, program się uruchomi, ale noise() zwraca stałą
wartość…
431
Kenneth H. "Ken" Perlin
1981 – praca przy filmie TRON
1983 – pierwszy generator szumu
1984 – pierwszy shader (język+interpreter)
1986-88 – Pixar, Alias, Softimage, Renderman, Dynamation, 3D Studio Max, …
• Filmy Jamesa Camerona (Abyss,Titanic,...), filmy animowane (Lion King, Moses,...), filmy ze Schwarzeneggerem (T2, True Lies, ...), filmy Star Wars, filmy Star Trek, filmy z Batmanem, …
1989 – hypertekstury
432
http://mrl.nyu.edu/~perlin/
To Ken Perlin for the development of Perlin Noise, a technique used to produce natural appearing textures on computer generated surfaces for motion picture visual effects.
The development of Perlin Noise has allowed computer graphics artists to better represent the complexity of natural phenomena in visual effects for the motion picture industry.
433
Nagroda Akademii „Technical Achievement Award” – 1997
Szum Perlina
Algorytm w skrócie: Funkcja Rn [-1, 1] (najczęściej n = 1…4)
W przestrzeni Rn mamy regularną siatkę z wygenerowanymi pseudolosowo wartościami gradientu (osobny algorytm)
Dla każdego punktu odnajdujemy jego 2n najbliższych węzłów
Dla każdego węzła obliczamy wektor do niego i mnożymy iloczynem skalarnym z wartością gradientu w węźle
Otrzymane wartości interpolujemy jakąś gładką funkcją np. 3x2 – 2x3
(potrzebne jest 2n - 1 interpolacji) 434
Szum Perlina, c.d. Użycie czasu jako dodatkowego wymiaru pozwala na uzyskanie
animacji (szum 3D i 4D):
435
http://www.noisemachine.com/talk1/
Szum Simplex
Inny sposób generacji szumu (Ken Perlin 2001) Mniejsza ilość obliczeń
Mniejsza złożoność obliczeniowa O(n2) zamiast O(2n) (n – wymiarowość)
Lepsza niezależność szumu od kierunku
Łatwiejsza implementacja sprzętowa
Szczegóły: http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
436
Szum widmowy (fraktalny)
Prosta funkcja szumu nie daje czasem oczekiwanych rezultatów ze względu na mały zakres częstotliwości
W celu uzyskania szumu widmowego należy zastosować sumę szumów, za każdym razem zwiększając częstotliwość (2x) i zmniejszając amplitudę (2x)
437
Przykład OpenGL58
438
Użycie funkcji szumu w teksturach
Zaburzając współrzędne tekstury szumem możemy uzyskać bardziej naturalne efekty
Np. dla drewna:
439
Przykład OpenGL59
440
Szum Worley’a
Wprowadzony przez Stevena Worley’a w 1996
Zwany też szumem komórkowym (cell noise)
Bazuje na diagramie Woronoja (Георгий Феодосьевич Вороной)
Algorytm: Pseudolosowe wylosowanie punktów w przestrzeni
Funkcja szumu Fn(x,y,z) zwraca n najbliższych odległości od punktów
Szczegóły: http://www.rhythmiccanvas.com/research/papers/worley.pdf
441
Przykład OpenGL60
442
Zaznaczanie obiektów
443
Wskazywanie obiektów (object picking)
Często zdarza się, że powstaje potrzeba, aby użytkownik mógł wskazać (klikając myszką, dotykając palcem) obiekt lub jego fragment
Istnieje wiele metod na wykrycie, który obiekt został wskazany, wybór metody zależy od takich czynników, jak np.: Szybkość działania
Oczekiwana precyzja
Dostępne dane 444
Wskazywanie obiektów, c.d.
Mając dane współrzędne ekranu (x, y), należy znaleźć obiekt, który w tym miejscu został wyrenderowany
Metody geometryczne Ray-tracing pojedynczego promienia (od kamery przez (x, y)) i znalezienie
najbliższego przecięcia
Odczytanie z z-bufora wartości głębokości z i dokonując przekształceń odwrotnych można z punktu (x, y, z) odtworzyć punkt w przestrzeni modelu a następnie odnaleźć obiekt znajdujący się najbliżej
445
Wskazywanie obiektów, c.d.
Metody oparte o OpenGL Renderowanie obiektów z
zapamiętaniem ich identyfikatorów we framebuforze
• Można napisać oddzielny program na GPU, który wykona tę czynność wtedy kiedy taki odczyt będzie konieczny
• Można także wykonać tę czynność przy okazji generowania g-buffora w metodzie opóźnionego cieniowania
446
Przekazanie id obiektu do shadera
Jeżeli obiekty są renderowane oddzielnie jako uniform
Jeżeli wiele obiektów jest renderowanych z VBO id jako dodatkowe pole (obok położenia, normalnych itp.)
Jeżeli wykorzystujemy instancing można wykorzystać gl_InstanceID
Inne
447
Pobranie id z tekstury
W wersjach OpenGL < 4.5 Musimy pobrać całą teksturę do CPU
glBindTexture(GL_TEXTURE_2D, tex_id);
glGetTexImage(GL_TEXTURE_2D, 0, …, …, ids);
W OpenGL >= 4.5 Można pobrać dowolny prostokąt z tekstury, także taki o wymiarach 1x1:
glGetTextureSubImage(tex_id, 0, x, y, 0, 1, 1, 1, …, …, sizeof(id), &id);
448
Przykład OpenGL61
449
Obwódka obiektu (outline)
Wizualne sygnalizowanie wskazanego obiektu Zmiana koloru
Obwódka
Do zrobienia obwódki można wykorzystać: Bufor z id obiektów
• Obwódkę można dodatkowo rozmyć (np. gaussem), co może dać dodatkowy ładny efekt
Stencil buffer
450
Przykład OpenGL62
451
Stencil buffer
Dodatkowy bufor pozwalający na ciekawe operacje, np. maskowanie
Bufor najczęściej jest 8bitowy (8 bitów na każdy pixel), ale można to sprawdzić: int stencil_bits;
glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
452
Stencil buffer, c.d.
Stencil buffer wykonuje dwie czynności: testowanie i właściwą operację
Testowanie polega obliczeniu wyrażenia: (ref & mask) FUNC (stencil & mask) Gdzie:
• ref – referencyjna stała całkowita
• mask – stała całkowita umożliwiająca korzystanie z wybranych bitów stencil bufora
• FUNC – operacja logiczno-relacyjna (GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL, GL_EQUAL, GL_NOTEQUAL
• stencil – zawartość stencil bufora
glStencilFunc(func, ref, mask) 453
Stencil buffer, c.d.
Właściwa akcja definiowana jest dla 3 sytuacji: sfail – stytuacja, kiedy test stencil bufora zwrócił 0
dpfail – sytuacja, kiedy test stencil bufora nie zwrócił 0, ale test głębokości wykazał, że fragment będzie niewidoczny
dppass – sytuacja gdy oba testy przeszły pomyślnie
Uwaga: fragment nie jest obliczany, jeśli stencil test zwrócił 0!
W każdym z w/w przypadków możliwa jest jedna z czynności: GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_INCR_WRAP, GL_DECR, GL_DECR_WRAP, GL_INVERT
glStencilOp(sfail, dpfail, dppass) 454
Czynności wykonywane na stencil buforze GL_KEEP - nie zmienia zawartości
GL_ZERO - ustawia na 0
GL_REPLACE - zamienia na wartość ref
GL_INCR - zwiększa zawartość stencil bufora o 1, aż do maksimum
GL_INCR_WRAP - zwiększa zawartość bufora, ale przechodzi na 0 po osiągnięciu maksimum
GL_DECR - zmiejsza wartość stencil bufora aż do 0
GL_DECR_WRAP - zmiejsza wartość stencil bufora, ale przechodzi na maksimum po osiągnięciu 0
GL_INVERT - neguje bity stencil bufora
455
Przykład - obwódka obiektu z wykorzystaniem stencil bufora glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
// tu renderujemy obiekt normalnie efekt: „1” tam gdzie obiekt
glStencilFunc(GL_NOTEQUAL, 1, 0xFFFF);
// tu renderujemy obiekt jako wireframe „grubymi” liniami
glDisable(GL_STENCIL_TEST); 456
457
1
2
Bufor koloru i stencil
Wady i zalety metody
Wady Obrysowywany obiekt wymaga dwukrotnego renderingu
Linie, szczególnie grubsze nie są ładnie rysowane przez OpenGL
Zalety Prostota implementacji
Brak konieczności alokowania i używania własnych buforów
458
Przykład OpenGL63
459
Wykrywanie krawędzi
W celu uzyskania „komiksowego” wyglądu można dokonać detekcji krawędzi w oparciu o: Wektory normalne
Bufor głębokości
Wykrywanie krawędzi – wiele algorytmów: Krzyż Robertsa
Operator Sobela
Operator Sharra
Algorytm Canny’ego
… 460
Krzyż Robertsa
461
Operator Sobela
462
Przykład OpenGL64
463
464