Nowoczesna OpenGL - rendering wielokątów Rendering wielokątów w kontekście biblioteki SFML

Podobne dokumenty
Shadery. Artur Staszczyk Bartłomiej Filipek

OpenGL - tekstury Mapowanie tekstur

Programowanie Procesorów Graficznych

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

1 Temat: Vertex Shader

Janusz Ganczarski. OpenGL Pierwszy program

Potok graficzny i shadery. Hubert Rutkowski

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

1. Prymitywy graficzne

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

1 Podstawy c++ w pigułce.

Plan wykładu. Akcelerator 3D Potok graficzny

RENDERING W CZASIE RZECZYWISTYM. Michał Radziszewski

Języki i techniki programowania Ćwiczenia 2

Bartłomiej Filipek

8 Przygotował: mgr inż. Maciej Lasota

OpenGL : Oświetlenie. mgr inż. Michał Chwesiuk mgr inż. Tomasz Sergej inż. Patryk Piotrowski. Szczecin, r 1/23

1 Podstawy c++ w pigułce.

Niezwykłe tablice Poznane typy danych pozwalają przechowywać pojedyncze liczby. Dzięki tablicom zgromadzimy wiele wartości w jednym miejscu.

Gry Komputerowe Laboratorium 4. Teksturowanie Kolizje obiektów z otoczeniem. mgr inż. Michał Chwesiuk 1/29. Szczecin, r

Biblioteka OpenGL: Wprowadzenie

Programowanie w języku C++ Grażyna Koba

Bartosz Bazyluk OpenGL Współczesne podejście do programowania grafiki Część II: Programy cieniujące (shadery)

Podstawy programowania, Poniedziałek , 8-10 Projekt, część 1

3 Przygotował: mgr inż. Maciej Lasota

Podstawy programowania skrót z wykładów:

Język ludzki kod maszynowy

W dowolnym momencie można zmienić typ wskaźnika.

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Programowanie Obiektowo Zorientowane w języku c++ Przestrzenie nazw

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Programowanie warstwy wizualnej gry

Bartosz Bazyluk OpenGL Programowalny potok renderowania, buforowanie geometrii (VBO, IBO, VAO).

Podstawy Programowania C++

Wprowadzenie do QT OpenGL

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Ćwiczenie 3 stos Laboratorium Metod i Języków Programowania

GRAFIKA KOMPUTEROWA 7: Kolory i cieniowanie

Jak napisać program obliczający pola powierzchni różnych figur płaskich?

Programowanie w języku Python. Grażyna Koba

Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp. Krzysztof Banaś Podstawy programowania 1

GLKit. Wykład 10. Programowanie aplikacji mobilnych na urządzenia Apple (IOS i ObjectiveC) #import "Fraction.h" #import <stdio.h>

Podstawy programowania w C++

Zatem standardowe rysowanie prymitywów wygląda następująco:

Informatyka I. Typy danych. Operacje arytmetyczne. Konwersje typów. Zmienne. Wczytywanie danych z klawiatury. dr hab. inż. Andrzej Czerepicki

1. Pierwszy program. Kompilator ignoruje komentarze; zadaniem komentarza jest bowiem wyjaśnienie programu człowiekowi.

Materiał Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Wskaźniki Tablice dynamiczne Referencje Funkcje

Grafika Komputerowa Wykład 5. Potok Renderowania Oświetlenie. mgr inż. Michał Chwesiuk 1/38

// Potrzebne do memset oraz memcpy, czyli kopiowania bloków

2 Przygotował: mgr inż. Maciej Lasota

Podstawy programowania. Wykład: 5. Instrukcje sterujące c.d. Stałe, Typy zmiennych c.d. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Smarty PHP. Leksykon kieszonkowy

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Programowanie Obiektowew języku C++ Zadania L4

Instrukcja do ćwiczeń

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

Wprowadzenie do programowania z wykorzystaniem biblioteki OpenGL. Dorota Smorawa

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

Wstęp do informatyki- wykład 7

Programowanie. programowania. Klasa 3 Lekcja 9 PASCAL & C++

ANALIZA I INDEKSOWANIE MULTIMEDIÓW (AIM)

IX. Wskaźniki.(3 godz.)

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Algorytm. a programowanie -

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

Programowanie proceduralne INP001210WL rok akademicki 2018/19 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

Typy złożone. Struktury, pola bitowe i unie. Programowanie Proceduralne 1

Programowanie proceduralne w języku C++ Pojęcia podstawowe - kod źródłowy

Wykład VII. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

Dynamiczny przydział pamięci w języku C. Dynamiczne struktury danych. dr inż. Jarosław Forenc. Metoda 1 (wektor N M-elementowy)

Zapis algorytmów: schematy blokowe i pseudokod 1

Funkcje. Spotkanie 5. Tworzenie i używanie funkcji. Przekazywanie argumentów do funkcji. Domyślne wartości argumentów

Cwiczenie nr 1 Pierwszy program w języku C na mikrokontroler AVR

Język C++ zajęcia nr 2

Przekształcenia geometryczne. Dorota Smorawa

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Podstawy programowania. Wykład: 9. Łańcuchy znaków. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Wykład 5. Rendering (2) Geometria

Języki C i C++ Wykład: 2. Wstęp Instrukcje sterujące. dr Artur Bartoszewski - Języki C i C++, sem. 1I- WYKŁAD

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

TEMAT : KLASY DZIEDZICZENIE

Idź do. Spis treści Przykładowy rozdział Skorowidz. Katalog książek. Katalog online Zamów drukowany katalog. Twój koszyk.

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

Systemy wirtualnej rzeczywistości. Komponenty i serwisy

Scenariusz lekcji opartej na programie Program nauczania informatyki w gimnazjum DKW /99

Laboratorium nr 12. Temat: Struktury, klasy. Zakres laboratorium:

Laboratorium 1 Temat: Przygotowanie środowiska programistycznego. Poznanie edytora. Kompilacja i uruchomienie prostych programów przykładowych.

Laboratorium grafiki komputerowej i animacji. Ćwiczenie V - Biblioteka OpenGL - oświetlenie sceny

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Język programowania zbiór reguł określających, które ciągi symboli tworzą program komputerowy oraz jakie obliczenia opisuje ten program.

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk. Wydział Inżynierii Metali i Informatyki Przemysłowej

Wyrażenie include(sciezka_do_pliku) pozwala na załadowanie (wnętrza) pliku do skryptu php. Plik ten może zawierać wszystko, co może się znaleźć w

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 15 kwietnia K.Grzelak (Wykład 8) Programowanie w C++ 1 / 33

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Transkrypt:

Nowoczesna OpenGL - rendering wielokątów Rendering wielokątów w kontekście biblioteki SFML Mirosław Głowacki 1 1 Akademia Górniczo-Hutnicza im. Stanisława Staszica w Krakowie Wydział Inżynierii Metali i Informatyki Stosowanej Katedra Informatyki Stosowanej i Modelowania Marzec 2017 Mirosław Głowacki (AGH, UJK) OpenGL 2017 1 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 2 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 3 / 79

Potok OpenGL Przetwarzanie strumienia grafiki komputerowej przy użyciu shaderów obejmuje następujące po sobie etapy przetwarzania danych wejściowych w celu uzyskania obrazu końcowego - poszczególne kroki ilustruje rysunek: Mirosław Głowacki (AGH, UJK) OpenGL 2017 4 / 79

Wierzchołki Tworzenie obrazu przy użyciu OpenGL zaczyna się od utworzenia zbioru wierzchołków (ang. vertex). Wierzchołkami nazywamy punkty, z których kształtuje się trójkąty i inne wielokąty. Każdy z tych punktów jest przechowywany z pewnymi atrybutami i to do programisty należy decydzja, jakie cechy należy przechowywać. Powszechnie stosowane są atrybuty pozycji 3D we współrzędnych świata oraz współrzędne tekstury. Wierzchołki są danymi wejściowymi shadera wierzchołków - części oprogramowania sprzętowego karty graficznej. Shader wierzchołków to niewielki program uruchamiony na karcie graficznej, który przetwarza każdy z wierzchołków indywidualnie Mirosław Głowacki (AGH, UJK) OpenGL 2017 5 / 79

Shader wierzchołków Shader wierzchołków to miejsce, gdzie odbywa się przekształcanie pozycji wierzchołków: z trójwymiarowego układu współrzędnych świata do znormalizowanego układu urządzenia NDC. Ważne atrybuty wierzchołków, takie jak kolor czy współrzędne zaczepienia tekstury, przechodzą dalej wzdłuż strumienia grafiki. Z tak przekształconych wierzchołków karta graficzna będzie tworzyć trójkąty, linie lub punkty w procesie zwanym shape assembly. Utworzone prymitywy stanowią podstawę złożonych kształtów. Do wyboru jest kilka dodatkowych trybów rysowania, takich jak: pasma trójkątów (ang. triangle strips ), i linii (ang. line strips ). Umożliwia to ograniczenie liczby przekazywanych wierzchołków dla obiektów, dla których każdy następny prymityw jest połączony z poprzednim - np. ciągła linia składająca się z kilku segmentów. Mirosław Głowacki (AGH, UJK) OpenGL 2017 6 / 79

Shader geomertii Kolejnym krokiem przetwarzania strumienia grafiki jest shader geometrii (ang. geometry shader ), który jest całkowicie opcjonalny i został wprowadzony do użytku dopiero niedawno. W przeciwieństwie do shadera wierzchołków shader geometrii może generować i przekazać dalej dodatkowe informacje. Wejściowe prymitywy z etapu shape assembly mogą być: przekazywane dalej w dół strumienia grafiki bez zmian, modyfikowane przed przekazaniem, w całości odrzucone, zastąpione innymi prymitywnami. Ponieważ komunikacja pomiędzy GPU a resztą komputera jest stosunkowo wolna, etap ten może zmniejszyć rozmiar danych, które muszą zostać przekazane dalej. Przykładowo kostki voxeli mogą być tworzone w shaderze geometrii na bazie wierzchołków punktowych i ich współrzędnych i atrybutów oraz przekazywane dalej zamiast wierzchołków. Mirosław Głowacki (AGH, UJK) OpenGL 2017 7 / 79

Shader fragmentów Po tym, jak ostateczna lista kształtów jest kompletna i dostosowana do współrzędnych ekranu, rasteryzator konwertuje widoczne elementy kształtów na zbiór fragmentów wielkości piksela. Atrybuty wierzchołków pochodzące z shadera wierzchołków lub shadera geometrii są interpolowane dla każdego fragmentu i przekazywane jako dane wejściowe do shadera fragmentów. Kolory są równomiernie interpolowane dla każdego z fragmentów, które tworzą trójkąt, choć zostały określone jedynie dla trzech punktów stanowiących wierzchołki trójkąta. Shader fragmentów przetwarza pojedynczo każdy fragment wraz z jego interpolowanymi atrybutami i określa jego ostateczny kolor poprzez: pobranie próbki z tekstury zaczepionej w wierzchołkach lub proste przekazanie koloru fragmentu. Mirosław Głowacki (AGH, UJK) OpenGL 2017 8 / 79

Mieszanie fragmentów i testowanie buforów W bardziej zaawansowanych scenariuszach, są również kalkulowane: oświetlenie i cieniowanie, efekty specjalne, możliwość opuszczenia fragmentu, co oznacza, że kształt będzie w tym miejscu przezroczysty. Ostatecznie, efekt końcowy jest budowany ze wszystkich fragmentów kształtu przez: mieszanie ich ze sobą, testowanie bufora głębokości dla każdego fragmentu, testowanie bufora szablonowego dla poszczególnych fragmentów. Negatywny wynik jednego z powyższych testów powoduje odrzucenie fragmentu. Na przykład, jeśli jeden trójkąt zasłonia inne trójkąty, to wynik testowania bufora głębokści jest dla fragmentów tych innych trójkątów negatywny. Mirosław Głowacki (AGH, UJK) OpenGL 2017 9 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 10 / 79

Obiekty sceny graficznej Na początek należy określić dane niezbędne do tego, aby karta graficzna zwróciła prawidłową scenę. Jak już wspomniano, te dane to atrybuty wierzchołka. Istnieje dowolność w przekazywaniu atrybutów, ale podstawowe i zawsze wymagane to pozycja we współrzędnych świata - niezależnie od tego czy tworzymy grafikę 2D czy 3D. Gdy wierzchołki zostaną przetworzone wg przedstawionego algorytmu, ich współrzędne zostaną przekształcone do współrzędnych urządzenia. Mirosław Głowacki (AGH, UJK) OpenGL 2017 11 / 79

Obiekty sceny graficznej Współrzędne urządzenia to x i y, odwzorowywane na ekranie w zakresie ( 1.0, 1.0). Umieśćmy na scenie trójkąt o wierzchołkach ulokowanych w punktach (0, 0.5), (0.5, 0.5) i ( 0.5, 0, 5) zgodnie z ruchem wskazówek zegara. Mirosław Głowacki (AGH, UJK) OpenGL 2017 12 / 79

Obiekty sceny graficznej Jedyną różnica między wierzchołkami przedstawionymi na rysunku jest pozycja, więc będzie to jedyny atrybut. Ponieważ definiujemy trójkąt bezpośrednio we współrzędnych urządzenia, to x i y są wystarczające. OpenGL oczekuje na wysyłanie wszystkich wierzchołków w postaci jednego wektora, co może na początku wydawać się mylące. Aby zrozumieć format tego wektora, zobaczymy jak będzie on wyglądał dla naszego przykładowego trójkąta. float vertices[] = { 0.0f, 0.5f, // Vertex 1 (X, Y) 0.5f, -0.5f, // Vertex 2 (X, Y) -0.5f, -0.5f // Vertex 3 (X, Y) }; Mirosław Głowacki (AGH, UJK) OpenGL 2017 13 / 79

Wektor wierzchołków Wektor powinien zawierać listę wszystkich wierzchołków z ich atrybutami spakowane razem. Kolejność w jakiej pojawiają się atrybuty, nie ma znaczenia tak długo, jak jest ona taka sama dla każdego wierzchołka. Kolejność wierzchołków nie musi być sekwencyjna - czyli nie muszą one występować koniecznie w kolejności, w której powstają kształty. Wymaga to jednak dostarczenia dodatkowych danych w postaci bufora elementów. Kwestia ta zostanie omówiona na końcu wykładu, ponieważ teraz byłoby to skomplikowane. Mirosław Głowacki (AGH, UJK) OpenGL 2017 14 / 79

Vertex Buffer Object - VBO Następnym krokiem jest przesłanie danych dotyczących wierzchołków do karty graficznej. Jest to ważne, ponieważ pamięć karty graficznej jest o wiele szybsza i nie ma wtedy potrzeby przesyłania danych za każdym razem, gdy scena musi być renderowana (około 60 razy na sekundę). Można to zrobić tworząc Vertex Buffer Object ( VBO ) GLuint vbo; glgenbuffers(1, &vbo); // Generate 1 buffer Pamięć jest zarządzana przez OpenGL, więc zamiast wskaźnika dodatnia liczba jest odniesieniem do bufora. GLuint vbo (substytut unsigned int vbo ) jest liczbą potrzebną do uaktywnienia VBO lub zniszczenia go, gdy przestaje być potrzebny. Mirosław Głowacki (AGH, UJK) OpenGL 2017 15 / 79

Vertex Buffer Object - VBO Aby przesłać aktualne dane do VBO trzeba najpierw uczynić go aktywnym obiektem wywołując glbindbuffer : glbindbuffer(gl_array_buffer, vbo); Oprócz GL_ARRAY_BUFFER typu wyliczeniowego istnieją inne rodzaje buforów, ale nie są one ważne w tej chwili. Dyskutowana instrukcja czyni właśnie utworzony VBO aktywnym, co pozwala na skopiowanie do niego danych wierzchołków instrukcją: glbufferdata(gl_array_buffer, sizeof(vertices), vertices, GL_STATIC_DRAW); Należy zauważyć, że funkcja nie odnosi się do ID naszego VBO, lecz do GL_ARRAY_BUFFER. Mirosław Głowacki (AGH, UJK) OpenGL 2017 16 / 79

Vertex Buffer Object - VBO Drugi parametr określa rozmiar wektora wierzchołków w bajtach. Wartość ostatniego parametru narzuca sposób wykorzystania danych wierzchołków - dane te są przesyłane raz, a następnie: używane wiele razy (GL_STATIC_DRAW), zmieniane od czasu do czasu i używane wiele razy (GL_DYNAMIC_DRAW), użyte raz (GL_STREAM_DRAW). Wartość ta określa w jakiej pamięci dane są zapisywane na karcie graficznej, aby najwyższą wydajność. Przykładowo, VBO z GL_STREAM_DRAW powoduje użycie pamięci, która umożliwia szybkie zapisywanie kosztem nieco wolniejszego rysowania. Mirosław Głowacki (AGH, UJK) OpenGL 2017 17 / 79

Przetwarzanie danych wierzchołkowych Wierzchołki i ich atrybuty zostały skopiowane do karty graficznej, ale nie są jeszcze gotowe do użytku - nadchodzi więc pora na wyjaśnienie jak karta graficzna radzi sobie z atrybutami. Jak wiemy, istnieją trzy etapy przetwarzania danych wymagające użycia shaderów - każdy z nich ma ściśle określony cel. W starszych wersjach OpenGL można było tylko w niewielkim zakresie modyfikować operacje wykonywane przez kartę graficzną. Nowoczesna OpenGL pozwala na instruowanie karty graficznej o sposobie postępowania z danymi. Aby uzyskać efekt na ekranie, należy zaimplementować zarówno shader wierzchołków, jak i shader fragmentów. Shader geometrii jest opcjonalny i zostanie omówiony później. Mirosław Głowacki (AGH, UJK) OpenGL 2017 18 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 19 / 79

Shadery Shadery są programowane w języku podobnym do C o nazwie GLSL (OpenGL Shading Language). OpenGL kompiluje kod źródłowy w trakcie wykonywania programu i kopiuje go do karty graficznej. Każda wersja OpenGL posiada własną wersję języka cieniującego z pewnym zestawem funkcji - wersje te stanowią parę. Dzieje się tak dlatego, że shadery zostały wprowadzone dopiero dla OpenGL 2.0 jako w wersja GLSL 1.10. Następne pary (OpenGL, GLSL) to: (2.1, 1.20), (3.0, 1.30), (3.1, 1.40), a do wersji 3.2 OpenGL przypisana jest wersja 1.50 GLSL. Począwszy od wersji 3.3 OpenGL wersja GLSL jest taka sama jak wersja OpenGL. Mirosław Głowacki (AGH, UJK) OpenGL 2017 20 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 21 / 79

Shader wierzchołków Shader wierzchołków ( vertex shader ) jest programem karty graficznej, który przetwarza każdy wierzchołek i jego atrybuty w kolejności ich występowanie w wektorze wierzchołków. Jego obowiązkiem jest obliczenie końcowego położenia wierzchołków we współrzędnych urządzenia i przekazanie wymaganych danych do shadera fragmentów. Dlatego właśnie tutaj zachodzą wszelkie przekształcenia obiektów w przestrzeni 3D i ich rzutowanie do 2D. Shader fragmentów wymaga atrybutów, takich jak kolor czy współrzędne tekstury, które zwykle są przekazywane przez shader wierzchołków z wejścia na wyjście bez żadnych zmian. Mirosław Głowacki (AGH, UJK) OpenGL 2017 22 / 79

Shader wierzchołków W naszym przykładzie shader wierzchołków nie będzie miał wiele do roboty, gdyż: wierzchołki zostały określone we współrzędnych urządzenia, nie wprowadziliśmy żadnych atrybutów, const char* vertexsource = R"glsl( #version 150 in vec2 position; void main(){ gl_position = vec4(position, 0.0, 1.0); } )glsl"; dyrektywa preprocesora #version została użyta w celu określenia wersji GLSL ( 1.50 ). Następnie stwierdzono, że istnieje tylko jeden atrybut - pozycja. Mirosław Głowacki (AGH, UJK) OpenGL 2017 23 / 79

Shader wierzchołków Oprócz typów oferowanych przez język C, GLSL posiada wbudowane typy wektorowe i macierzowe identyfikowane przez vec* i mat*. Typ wartości wewnętrznych w tych konstrukcjach jest dla OpenGL 3.2 i 3.3 typem float. Liczba występująca po vec zamiast gwiazdki określa liczbę elementów ( x, y, z, w ), a liczba po mat określa liczbę wierszy/kolumn macierzy kwadratowych. Ponieważ atrybut position zawiera tylko dwie współrzędne - x i y - typ vec2 jest odpowiedni dla tego zestawu danych. Przedrostek in oznacza, że mamy do czynienia z danymi wejściowymi. Mirosław Głowacki (AGH, UJK) OpenGL 2017 24 / 79

Shader wierzchołków Końcowa pozycja wierzchołka jest przypisywana do specjalnej zmiennej gl_position, która będzie używana podczas łączenia prymitywów oraz wielu innych procesów. Aby te działały poprawnie, ostatnia wartość (tzn. w ) musi mieć wartość 1.0f. Poza tym istnieje duża dowolność w pracy z atrybutami i w dalszej części wykładu przedstawimy sposoby nadawania trójkątowi kolorów. Również praca z typami języka GLSL jest elastyczna. Mirosław Głowacki (AGH, UJK) OpenGL 2017 25 / 79

Shader wierzchołków W zprezentowanym przykładzie użyto skrótu, aby ustawić dwa pierwsze pola wektora vec4 przy użyciu pól wektora vec2. Można jednak inaczej - poniżesz dwie linie kodu powodują taki sam efekt: gl_position = vec4(position, 0.0, 1.0); gl_position = vec4(position.x, position.y, 0.0, 1.0); Podczas pracy z kolorami można uzyskać dostęp do poszczególnych składników modelu RGBA - tzn. r, g, b i a w sposób identyczny jak w przypadku x, y, z i w. Może to pomóc w przejrzystości. Mirosław Głowacki (AGH, UJK) OpenGL 2017 26 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 27 / 79

Shader fragmentów Dane wyjściowe z modułu shadera wierzchołków podlegają interpolacji do wszystkich pikseli ekranu pokrytych przez prymityw w procesie zwanym rasteryzacją. Piksele te są nazywane fragmentami i są obiektami, na których działa shader fragmentów. Podobnie jak shader wierzchołków generuje on na wyjściu jeden obowiązkowy atrybut - końcowy kolor fragmentu. Do programisty należy utworzenie kodu generującego ten kolor na podstawie: koloru wierzchołków, współrzędnych tekstury innych danych pochodzących z modułu shadera wierzchołków. Mirosław Głowacki (AGH, UJK) OpenGL 2017 28 / 79

Shader fragmentów Nasz trójkąt składa się tylko z białych pikseli, więc shader fragmentów po prostu generuje ten kolor dla każdego fragmentu: const char* fragmentsource = R"glsl( #version 150 out vec4 outcolor; void main(){ outcolor = vec4(1.0, 1.0, 1.0, 1.0); } )glsl"; Od razu zauważymy, że nie użyliśmy żadnej wbudowanej zmiennej do wyprowadzania koloru, takiej jak np. gl_fragcolor. To dlatego, że shader fragmentów może wygenerować wiele kolorów, a jak się takimi zmiennymi posługiwać zobaczymy podczas omawiania ładowania shaderów. Mirosław Głowacki (AGH, UJK) OpenGL 2017 29 / 79

Shader fragmentów Zmienna outcolor używa typu vec4, ponieważ każdy kolor składa się z kolorów podstawowych: czerwonego, zielonego, niebieskiego, oraz kanału alfa. Kolory w OpenGL są reprezentowane jako zmiennoprzecinkowe liczby z zakresu ( 0.0, 1.0 ) zamiast powszechnie używanego zakresu całkowitoliczbowego ( 0, 255 ). Mirosław Głowacki (AGH, UJK) OpenGL 2017 30 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 31 / 79

Kompilowanie kodu shaderów Kompilowanie kodu shaderów jest łatwe i następuje po załadowaniu kodu źródłowego z pliku lub z tablicy znakowej typu GLchar*. Proces zaczyna się od: utworzenia obiektu shadera oraz załadowania do niego kodu shadera, co dla shadera wierzchołków wygląda to tak: GLuint vertexshader = glcreateshader(gl_vertex_shader); glshadersource(vertexshader, 1, &vertexsource, NULL); W przeciwieństwie do VBO, zamiast aktywować kod shadera lub stosować podobne rozwiązania, należy jedynie przekazać funkcji glshadersource uchwyt do obiektu shadera ( vertexshader ) oraz wskaźnik do tablicy znakowej ( vertexsource ) zawierającej kod shadera. Mirosław Głowacki (AGH, UJK) OpenGL 2017 32 / 79

Kompilowanie kodu shaderów Prototyp funkcji glshadersource to: void glshadersource(gluint shader, GLsizei count, const GLchar **string, const GLint *length); Funkcja może zatem przyjmować nie jedną, ale wiele ( count ) tablic znakowych - zazwyczaj stosuje się jednak kod zawarty w jednej tablicy znakowej, tak jak w naszym przykładzie. Ostatni parametr może stanowić: count -elementowa tablica długości poszczególnych tablic znakowych ( *string ) - terminatorów null nie wlicza się wtedy do długości, NULL co oznacza, że każda tablica znakowa kończy się terminatorem null. Mirosław Głowacki (AGH, UJK) OpenGL 2017 33 / 79

Kompilowanie kodu shaderów Teraz pozostaje skompilowanie programu shadera na kod maszynowy, który może być wykonany przez kartę graficzną: glcompileshader(vertexshader); Należy pamiętać, że jeśli kompilacja się nie powiedzie, np. ze względu na błąd składni, funkcja glgeterror zwracająca błędy OpenGL nie odnotuje tego faktu! Aby sprawdzić poprawność kompilacji należy wykonać test: GLint status; glgetshaderiv(vertexshader, GL_COMPILE_STATUS, &status); Jeśli status jest równy wartości GL_TRUE, kod programu shadera został z powodzeniem skompilowany. Mirosław Głowacki (AGH, UJK) OpenGL 2017 34 / 79

Kompilowanie kodu shaderów W wypadku wystąpienia błędów można pobrać log kompilacji za pomocą instrukcji: char buffer[512]; glgetshaderinfolog(vertexshader, 512, NULL, buffer); Instrukcje te zapiszą pierwsze 511 bajtów logu kompilacji + terminator null do zadefiniowanego bufora znakowego buffer. Log kompilacji może również zawierać przydatne ostrzeżenia i to nawet wtedy, gdy kompilacja zakończyła się pomyślnie, więc warto sprawdzić go od czasu do czasu podczas kodowania shaderów. Trzeci parametr funkcji glgetshaderinfolog zachowuje się analogicznie jak ostatni parametr funkcji glshadersource. Mirosław Głowacki (AGH, UJK) OpenGL 2017 35 / 79

Kompilowanie kodu shaderów Postępowanie z shaderem fragmentów jest dokładnie takie samo jak postępowanie z shaderem wierzchołków. GLuint fragmentshader = glcreateshader(gl_fragment_shader); glshadersource(fragmentshader, 1, &fragmentsource, NULL); glcompileshader(fragmentshader); Poprawność kompilacji i jej log należy sprawdzić ponownie, tym razem zastępując vertexshader przez fragmentshader - może to oszczędzić w przyszłości sporo wysiłku. Mirosław Głowacki (AGH, UJK) OpenGL 2017 36 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 37 / 79

Łączenie shaderóww program Na razie wierzchołki i fragmenty są osobnymi obiektami. Mimo, że zostały zaprogramowane do współpracy, nie są jeszcze w rzeczywistości połączone. Odbywa się to poprzez utworzenie programu z obu shaderów. GLuint shaderprogram = glcreateprogram(); glattachshader(shaderprogram, vertexshader); glattachshader(shaderprogram, fragmentshader); Shader fragmentów może przekazywać dane do wielu buforów, należy więc jednoznacznie określić, które wyjście jest zapisywane do którego bufora i musi to nastąpić przed łączeniem programu. glbindfragdatalocation(shaderprogram, 0, "outcolor"); Mirosław Głowacki (AGH, UJK) OpenGL 2017 38 / 79

Łączenie shaderóww program Buforem domyślnym shadera fragmentów jest ten o numerze 0 i nasz shader zawiera tylko jedno wyjście, więc wywołanie funkcji glbindfragdatalocation nie jest konieczne Podczas renderowania wielu buforów można w razie potrzeby użyć funkcji gldrawbuffers, która wyspecyfikuje bufor dla shadera fragmentów - wyjście pierwsze jest wyjściem domyślnym. Po dołączeniu shaderów, należy dokonać linkowania programu. gllinkprogram(shaderprogram); Po tej operacji można zmieniać shadery, ale nie będzie to miało wpływu na program dopóki linkowanie nie zostanie powtórzone. Obiekt shadera może również zostać usunięty za pomocą funkcji gldeleteshader, ale funkcja nie da efektu, dopóki nie zostanie on odłaczony od wszystkich programów przy użyciu funkcji gldetachshader. Mirosław Głowacki (AGH, UJK) OpenGL 2017 39 / 79

Łączenie shaderóww program Aby zacząć używać shaderów w programie OpenGL, wystarczy odwołać się do funkcji: gluseprogram(shaderprogram); Podobnie jak to miało miejsce w przypadku bufora wierzchołków, tylko jeden program shadera może być aktywny równocześnie. Mirosław Głowacki (AGH, UJK) OpenGL 2017 40 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 41 / 79

Wiązanie wierzchołków z atrybutami Chociaż mamy teraz sprecyzowane dane wierzchołków i program shaderów, OpenGL nadal nie wie, jak sformatowane i uporządkowane są atrybuty. Aby rozwiązać problem najpierw należy pobrać uchwyt do wektora position w shaderze wierzchołków: GLint posattrib = glgetattriblocation(shaderprogram, "position"); Jego wartość jest liczbą zależną od kolejności definicji danych wejściowych - pierwsza i jedyna pozycja wejściowa w maszym przykładzie zawsze będzie miała wartość 0. Mirosław Głowacki (AGH, UJK) OpenGL 2017 42 / 79

Wiązanie wierzchołków z atrybutami Po ustaleniu uchwytu do danych wejściowych należy określić, w jaki sposób dane te będą pobierane z tablicy: glvertexattribpointer(posattrib, 2, GL_FLOAT, GL_FALSE, 0, 0); Pierwszy parametr odwołuje się do wejścia. Drugi parametr określa liczbę wartości tego wejścia, która jest taka sama jak liczba elementów vec2. Trzeci parametr określa typ każdego składnika, Czwarty parametr określa, czy wartości wejściowe powinny być normalizowane między -1.0 a 1.0 (lub 0.0 i 1.0 w zależności od formatu), jeśli nie są liczbami zmiennoprzecinkowymi. Mirosław Głowacki (AGH, UJK) OpenGL 2017 43 / 79

Wiązanie wierzchołków z atrybutami Ostatnie dwa parametry są dla nas najważniejsze, ponieważ określają, jak atrybut jest ulokowany w tablicy wierzchołków. Pierwsza liczba określa krok ( stride ), tzn. liczbę bajtów między poszczególnymi pozycjami wierzchołków w tablicy - wartość 0 oznacza, że nie ma między nimi innych danych. Dzieje się tak wtedy, gdy pozycja każdego wierzchołka następuje bezpośrednio po pozycji poprzedniego wierzchołka. Ostatni parametr określa przesunięcie ( offset ) atrybutów względem początku tablicy - tj. numer kolejny bajtu w tablicy, w którym atrybut się rozpoczyna - ponieważ nie ma innych atrybutów, wartość ta wynosi również 0. Mirosław Głowacki (AGH, UJK) OpenGL 2017 44 / 79

Wiązanie wierzchołków z atrybutami Należy pamiętać, że funkcja glvertexattribpointer wiąże nie tylko stride i offset, ale także VBO, który jest aktualnie przypisany do GL_ARRAY_BUFFER. Oznacza to, że: nie ma konieczności arbitralnego odwoływania się do odpowiedniego VBO podczas wywoływannia funkcji rysowania. można użyć różnych VBO dla różnych atrybutów. Na koniec wiązania wierzchołków z atrybutami tablica atrybutów wierzchołków musi zostać aktywowana. glenablevertexattribarray(posattrib); Mirosław Głowacki (AGH, UJK) OpenGL 2017 45 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 46 / 79

Vertex Array Objects Można się spodziewać, że prawdziwe programy graficzne używają wielu różnych shaderów i układów wierzchołków. Zmiana aktywnego programu shadera jest łatwa dzięki funkcji gluseprogram, ale ponowne ustawianie wszystkich atrybutów byłoby to dość niewygodne. Na szczęście OpenGL rozwiązuje ten problem przy pomocy obiektów Vertex Array Objects ( VAO ). VAO przechowują wszystkie powiązania między atrybutami a VBO zawierającym surowe dane wierzchołkowe. Obiekty VAO są tworzone w taki sam sposób jak obiekty VBO : GLuint vao; glgenvertexarrays(1, &vao); Mirosław Głowacki (AGH, UJK) OpenGL 2017 47 / 79

Vertex Array Objects Pracę z VAO należy rozpocząć przyłączając go instrukcję: glbindvertexarray(vao); Gdy tylko jakiś VAO zostanie przyłączony, każde wywołanie funkcji glvertexattribpointer spowoduje, że informacje będą przechowywane w tym VAO. Stąd przełączanie pomiędzy różnymi danymi i formatami wierzchołków jest proste i polega na wiązaniu różnych VAO! VAO nie przechowuje żadnych danych wierzchołkowych, a jedynie odwołuje się do utworzonych VBO i zawiera informacje o sposobach pobierania z nich atrybutów. Należy się upewnić, że VAO zostało utworzone i przywiązane na początku programu, gdyż wszystkie bufory wierzchołków przyłączone wcześniej niż VAO będą ignorowane. Mirosław Głowacki (AGH, UJK) OpenGL 2017 48 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 49 / 79

Obrazowanie Po załadowaniu danych wierzchołków, utworzeniu programów shaderowych i połączeniu danych z atrybutami, można już narysować trójkąt. Obiektr VAO przechowujący informacje o atrybutach został przyłączony, więc nie ma potrzeby się tym martwić. Pozostaje tylko proste wywołanie funkcji gldrawarrays w głównej pętli: gldrawarrays(gl_triangles, 0, 3); Pierwszy parametr określa typ prymitywu (zwykle punkt, odcinek lub trójkąt). Drugi określa, liczbę wierzchołków pomijanych na początku. Ostatni parametr określa liczbę wierzchołków do przetworzenia ( UWAGA: nie prymitywów! ). Mirosław Głowacki (AGH, UJK) OpenGL 2017 50 / 79

Obrazowanie Po uruchomieniu programu, na ekranie powinno się pokazać okno: Mirosław Głowacki (AGH, UJK) OpenGL 2017 51 / 79

Obrazowanie Jeśli po uruchomieniu programu nic nie widać, należy się upewnić, że zostały spełnione wszystkie warunki: shadery zostały poprawnie skompilowane, program został poprawnie zlinkowany, tablica atrybutów została przyłączona, VAO został przyłączony przed określeniem atrybutów, dane wierzchołkowe są prawidłowe, funkcja glgeterror z biblioteki OpenGL zwraca wartość 0. Jeśli błędy wystąpiły, to funkcja zwóci kod błędu ( GLenum errorcode ) - informacje o błędzie można uzyskać odwołując się do gluerrorstring(errorcode). Mirosław Głowacki (AGH, UJK) OpenGL 2017 52 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 53 / 79

Uniforms Dotychczasowy program ustalił biały kolor trójkąta w kodzie shadera fragmentów. Co jednak gdyby zaszła potrzeba zamiany tego koloru już po skompilowaniu tego modułu? Okazuje się, że atrybuty wierzchołków nie są jedynym sposobem przekazywania danych do programów cieniujących. Inny sposób przekazywania danych do shaderów wykorzystuje system tzw. uniforms. Uniforms to zasadniczo zmienne globalne, mające tę samą wartość dla wszystkich wierzchołków i/lub fragmentów. Aby zademonstrować, jak z nich korzystać, zmienimy kolor trójkąta w samym programie OpenGL. Mirosław Głowacki (AGH, UJK) OpenGL 2017 54 / 79

Uniforms Nadanie jednolitego koloru trójkątowi wymaga nstępującego (lub podobnego) kodu shadera fragmentów: #version 150 uniform vec3 trianglecolor; out vec4 outcolor; void main(){ outcolor = vec4(trianglecolor, 1.0); } Ostatnim parametrem koloru wyjściowego jest przeźroczystość, którą obecnie nie będziemy się zajmować i która ustalona została na 1 - brak przeźroczystości. Teraz program wygeneruje czarny (niewidoczny na czarnym tle) trójkąt, ponieważ wartość trianglecolor nie została jeszcze ustawiona. Mirosław Głowacki (AGH, UJK) OpenGL 2017 55 / 79

Uniforms Zmiana wartości zmiennych typu uniform przebiega tak samo jak zmiana atrybutów wierzchołków - na początek należy uchwycić lokalizację wektora trianglecolor : GLint unicolor = glgetuniformlocation(shaderprogram,"trianglecolor"); Wartości uniforms zmienia się przy użyciu jednej z funkcji gluniformxy, gdzie X oznacza liczbę elementów, a Y oznacza typ. Typowymi typami są f dla float, d dla double i i dla integer. gluniform3f(unicolor, 1.0f, 0.0f, 0.0f); Uruchomienie programu teraz spowoduje wyświetlenie czerwonego trojkata. Mirosław Głowacki (AGH, UJK) OpenGL 2017 56 / 79

Uniforms Aby utworzyć bardziej ekscytujący obraz, można zmieniać kolor w czasie za pomocą konstrukcji umieszczonej w pętli głównej: auto t_start = std::chrono::high_resolution_clock::now(); //... auto t_now = std::chrono::high_resolution_clock::now(); float time = std::chrono::duration_cast<std::chrono:: duration<float>>(t_now - t_start).count(); gluniform3f(unicolor, (sin(time * 4.0f) + 1.0f) / 2.0f, 0.0f, 0.0f); Chociaż powyższy przykład nie jest być może bardzo ekscytujący, to jednak pokazuje, że uniforms są niezbędne do kontrolowania zachowań shaderów w czasie wykonywania programu. Z drugiej strony do opisu pojedynczego wierzchołka bardziej nadają się atrybuty wierzchołków. Mirosław Głowacki (AGH, UJK) OpenGL 2017 57 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 58 / 79

Więcej kolorów Choć uniforms mają swoje zalety, to kolor jest czymś, co należy określać dla każdego wierzchołka trójkąta. Aby to osiągnąć należy dodać atrybut koloru do wierzchołków. W tym celu należy uzupełnić dane wierzchołkowe. Przejrzystość jest w zasadzie domeną całego trójkąta, więc dodamy tylko barwy - czerwoną, zieloną i niebieską: float vertices[] = { 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, // Vertex 1: Red 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // Vertex 2: Green -0.5f, -0.5f, 0.0f, 0.0f, 1.0f // Vertex 3: Blue }; Mirosław Głowacki (AGH, UJK) OpenGL 2017 59 / 79

Więcej kolorów Teraz należy zmienić shader wierzchołków tak, aby pobierał nowe dane wejściowe i kierował część z nich do shadera fragmentów: #version 150 in vec2 position; in vec3 color; out vec3 Color; void main(){ Color = color; // input ---> output gl_position = vec4(position, 0.0, 1.0); } Wejściem ( in ) shadera są wektory: dwuelementowy position zawierający 2 współrzędne pozycji i trójelementowy color zawierający 3 wartości atrybutów koloru. Wyjściem ( out ) shadera jest trójelementowy wektor Color zawierający 3 wartości atrybutów koloru. Mirosław Głowacki (AGH, UJK) OpenGL 2017 60 / 79

Więcej kolorów Color stanowi dane wejściowe do shadera fragmentów: #version 150 in vec3 Color; out vec4 outcolor; void main(){ outcolor = vec4(color, 1.0); } Należy się upewnić, że wyjście shadera wierzchołków i wejście shadera fragmentów mają tę samą nazwę - inaczej nie można poprawnie powiązać shaderów. Mirosław Głowacki (AGH, UJK) OpenGL 2017 61 / 79

Więcej kolorów Należy również nieco zmienić kod wskaźnika atrybutów, aby uwzględnić nową kolejność atrybutów x, y, R, G, B. GLint posattrib = glgetattriblocation(shaderprogram, "position"); glenablevertexattribarray(posattrib); glvertexattribpointer(posattrib, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), 0); GLint colattrib = glgetattriblocation(shaderprogram, "color"); glenablevertexattribarray(colattrib); glvertexattribpointer(colattrib, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(2*sizeof(float))); stride w funkcji glvertexattribpointer jest ustawiony na 5*sizeof(float) - każdy wierzchołek to 5 wartości float. offset wynosi 2*sizeof(float) - przesunięcie atrybutów koloru to 2 wartości zmiennoprzecinkowe. Mirosław Głowacki (AGH, UJK) OpenGL 2017 62 / 79

Obrazowanie Tym razem na ekranie powinno się pokazać okno: Mirosław Głowacki (AGH, UJK) OpenGL 2017 63 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 64 / 79

Bufor elementów W obecnej wersji programu wierzchołki są określone w kolejności ich rysowania. Jeśli chcesz dodać kolejny trójkąt, musisz dodać 3 dodatkowe wierzchołki do tablicy wierzchołków. Istnieje sposób kontrolowania kolejności, co pozwala również na ponowne wykorzystanie istniejących wierzchołków - tablica elementów. Może to zaoszczędzić wiele pamięci podczas pracy z prawdziwymi modelami 3D w przyszłości, ponieważ każdy wierzchołek przynależy zazwyczaj do trzech trójkątów! Tablica elementów jest wypełniona liczbami całkowitymi bez znaku odnoszącymi się do wierzchołków związanych z GL_ARRAY_BUFFER. Mirosław Głowacki (AGH, UJK) OpenGL 2017 65 / 79

Bufor elementów Jeśli należy je narysować w dotychczasowej kolejności, będzie to wyglądać tak: GLuint elements[] = { 0, 1, 2 }; Elementy są ładowane do pamięci karty graficznej tak jak dane wierzchołkowe tworząc EBO (Element Buffer Object): GLuint ebo; glgenbuffers(1, &ebo); //... glbindbuffer(gl_element_array_buffer, ebo); glbufferdata(gl_element_array_buffer, sizeof(elements), elements, GL_STATIC_DRAW); Jedyną rzeczą, która różni bufor EBO od VBO jest argument GL_ELEMENT_ARRAY_BUFFER. Mirosław Głowacki (AGH, UJK) OpenGL 2017 66 / 79

Bufor elementów Aby skorzystać z tego bufora, należy zmienić polecenie rysuj na: gldrawelements(gl_triangles, 3, GL_UNSIGNED_INT, 0); Pierwszy parametr jest tu taki sam jak w przypadku gldrawarrays, ale wszystkie pozostałe odnoszą się do bufora elementów. Drugi parametr określa liczbę elementów do narysowania. Trzeci parametr określa typ danych elementu. Ostatni parametr określa przesunięcie. Jedyna różnica polega na tym, że teraz mówimy o elementach zamiast o wierzchołkach. Mirosław Głowacki (AGH, UJK) OpenGL 2017 67 / 79

Bufor elementów Zalety bufora elementów można zauważyć rysując prostokąt złożony z dwóch trójkątów. float vertices[] = { -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // Top-left 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // Top-right 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // Bottom-right }; 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // Bottom-right -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, // Bottom-left -0.5f, 0.5f, 1.0f, 0.0f, 0.0f // Top-left Wywołując gldrawarrays zamiast gldrawelements tak jak przedtem, bufor elementów zostanie po prostu zignorowany: gldrawarrays(gl_triangles, 0, 6); Mirosław Głowacki (AGH, UJK) OpenGL 2017 68 / 79

Bufor elementów Prostokąt jest renderowany tak, jak powinien, ale powtórzenie danych wierzchołków to marnotrawstwo pamięci. Korzystanie z bufora elementów umożliwia ponowne użycie danych: float vertices[] = { -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // Top-left 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // Top-right 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // Bottom-right -0.5f, -0.5f, 1.0f, 1.0f, 1.0f // Bottom-left }; //... GLuint elements[] = { 0, 1, 2, 2, 3, 0 }; //... gldrawelements(gl_triangles, 6, GL_UNSIGNED_INT, 0); Mirosław Głowacki (AGH, UJK) OpenGL 2017 69 / 79

Obrazowanie Teraz program powinien wygenerować okno: Mirosław Głowacki (AGH, UJK) OpenGL 2017 70 / 79

Spis treści 1 Potok grafiki OpenGL 2 Realizacja przedstawionego scenariusza 3 Shadery 4 Shader wierzchołków 5 Shader fragmentów 6 Kompilowanie kodu shaderów 7 Łączenie shaderów w program 8 Wiązanie wierzchołków z atrybutami 9 VAO - Vertex Array Objects 10 Obrazowanie 11 Uniforms 12 Więcej kolorów 13 Bufor elementów 14 Przykładowy program Mirosław Głowacki (AGH, UJK) OpenGL 2017 71 / 79

Kolorowy trojkąt 1/8 // Link statically with GLEW #define GLEW_STATIC // Nagłówki #include <GL/glew.h> #include <SFML/Window.hpp> // Kody shaderów const GLchar* vertexsource = R"glsl( #version 150 core in vec2 position; in vec3 color; out vec3 Color; void main(){ Color = color; gl_position = vec4(position, 0.0, 1.0); } )glsl"; Mirosław Głowacki (AGH, UJK) OpenGL 2017 72 / 79

Kolorowy trojkąt 2/8 const GLchar* fragmentsource = R"glsl( #version 150 core in vec3 Color; out vec4 outcolor; void main() { outcolor = vec4(color, 1.0); } )glsl"; int main() { sf::contextsettings settings; settings.depthbits = 24; settings.stencilbits = 8; Mirosław Głowacki (AGH, UJK) OpenGL 2017 73 / 79

Kolorowy trojkąt 3/8 // Okno renderingu sf::window window(sf::videomode(800, 600, 32), "OpenGL", sf::style::titlebar sf::style::close, settings); // Inicjalizacja GLEW glewexperimental = GL_TRUE; glewinit(); // Utworzenie VAO (Vertex Array Object) GLuint vao; glgenvertexarrays(1, &vao); glbindvertexarray(vao); // Utworzenie VBO (Vertex Buffer Object) // i skopiowanie do niego danych wierzchołkowych GLuint vbo; glgenbuffers(1, &vbo); Mirosław Głowacki (AGH, UJK) OpenGL 2017 74 / 79

Kolorowy trojkąt 4/8 GLfloat vertices[] = { 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f }; glbindbuffer(gl_array_buffer, vbo); glbufferdata(gl_array_buffer, sizeof(vertices), vertices, GL_STATIC_DRAW); // Utworzenie i skompilowanie shadera wierzchołków GLuint vertexshader = glcreateshader(gl_vertex_shader); glshadersource(vertexshader, 1, &vertexsource, NULL); glcompileshader(vertexshader); Mirosław Głowacki (AGH, UJK) OpenGL 2017 75 / 79

Kolorowy trojkąt 5/8 // Utworzenie i skompilowanie shadera fragmentów GLuint fragmentshader = glcreateshader(gl_fragment_shader); glshadersource(fragmentshader, 1, &fragmentsource, NULL); glcompileshader(fragmentshader); // Zlinkowanie obu shaderów w jeden wspólny program GLuint shaderprogram = glcreateprogram(); glattachshader(shaderprogram, vertexshader); glattachshader(shaderprogram, fragmentshader); glbindfragdatalocation(shaderprogram, 0, "outcolor"); gllinkprogram(shaderprogram); gluseprogram(shaderprogram); Mirosław Głowacki (AGH, UJK) OpenGL 2017 76 / 79

Kolorowy trojkąt 6/8 // Specifikacja formatu danych wierzchołkowych GLint posattrib = glgetattriblocation(shaderprogram, "position"); glenablevertexattribarray(posattrib); glvertexattribpointer(posattrib, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(glfloat), 0); GLint colattrib = glgetattriblocation(shaderprogram, "color"); glenablevertexattribarray(colattrib); glvertexattribpointer(colattrib, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(glfloat), (void*)(2 * sizeof(glfloat))); Mirosław Głowacki (AGH, UJK) OpenGL 2017 77 / 79

Kolorowy trojkąt 7/8 // Rozpoczęcie pętli zdarzeń bool running = true; while (running) { sf::event windowevent; while (window.pollevent(windowevent)) { switch (windowevent.type) { case sf::event::closed: running = false; break; } } // Nadanie scenie koloru czarnego glclearcolor(0.0f, 0.0f, 0.0f, 1.0f); glclear(gl_color_buffer_bit); Mirosław Głowacki (AGH, UJK) OpenGL 2017 78 / 79

Kolorowy trojkąt 8/8 // Narysowanie trójkąta na podstawie 3 wierzchołków gldrawarrays(gl_triangles, 0, 3); // Wymiana buforów tylni/przedni window.display(); } // Kasowanie programu i czyszczenie buforów gldeleteprogram(shaderprogram); gldeleteshader(fragmentshader); gldeleteshader(vertexshader); gldeletebuffers(1, &vbo); gldeletevertexarrays(1, &vao); // Zamknięcie okna renderingu window.close(); return 0; } Mirosław Głowacki (AGH, UJK) OpenGL 2017 79 / 79