Odrzucanie fragmentów i modyfikacja współrzędnych w programowaniu shadera
Przedstawiamy kolejny fragment publikacji Karola Sobiesiaka i Piotra Sydowa Shadery, z którego dowiecie się czym jest programowanie shadera fragmentów. Autorzy krok po kroku omawiają jak ono wygląda, przytaczając praktyczne kody programowania oraz tabelki. Oprócz tego w książce zostały omówione: architektura programowania, dane, mechanizmy uzupełniające oraz najnowocześniejszy shader obliczeniowy.
Odrzucanie fragmentów
Przetwarzanie shadera fragmentów może być w każdej chwili przerwane przez umieszczenie w kodzie instrukcji discard. Nie należy jej jednak utożsamiać z instrukcją return. Różnica między nimi polega na tym, że return może powodować jedynie wcześniejsze wyjście z głównej funkcji main(). Fragment nadal zostaje wysłany do dalszych zamkniętych etapów przetwarzania, które zamienią go ostatecznie w piksel (lub teksel w przypadku renderowania do tekstury). Natomiast użycie instrukcji discard spowoduje przerwanie wykonania danego fragmentu[1] i jego odrzucenie. Wszystkie bezpośrednie operacje na pamięci wykonane przed pojawieniem się tej instrukcji będą zachowane, jednak sam fragment nie zostanie dalej przetworzony. Tym samym bufor ramki nie zostanie zaktualizowany dla danego fragmentu.
Jednym z prostych zastosowań dla tej instrukcji jest dynamiczne tworzenie przerw w prymitywach. Można w ten sposób np. zmienić kształt punktów, które są zawsze renderowane jako kwadraty na ekranie.
Modyfikacja współrzędnych fragmentów
Każdy fragment, na jakim operuje shader fragmentów ma swoje współrzędne, które pozwalają określić jego położenie na ekranie. Znajdują się one w pierwszych dwóch komponentach (x, y) zmiennej gl_FragCoord. Domyślnie współrzędne te rozchodzą się z lewego dolnego rogu ekranu do prawego górnego, poziomo dla komponentu x i pionowo dla y. Wartości współrzędnych dla piksela znajdującego się w początkowym położeniu wynoszą (0.5, 0.5) i rosną z każdym pikselem o jeden w obu kierunkach. Wejściowy kwalifikator layout może zmienić powyższe zachowanie.
// zmiana pozycji początkowej
layout(origin_upper_left) in vec4 gl_FragCoord;
// zmiana wartości dla centrum piksela
layout(pixel_center_integer) in vec4 gl_FragCoord;
// obie zmiany jednocześnie
layout(origin_upper_left, pixel_center_integer) in vec4 gl_FragCoord;
Zastosowanie jednej z powyższych redeklaracji zmiennej gl_FragCoord w shaderze fragmentów podzielonym na moduły wymaga umieszczenia jej w każdym module, w którym zmienna ta zostaje użyta.
Parametr origin_upper_left odnosi się do początkowego położenia dla rozchodzących się współrzędnych. Jego deklaracja spowoduje zmianę tego położenia z lewego dolnego rogu do lewego górnego rogu. W konsekwencji komponenty x i y zmiennej gl_FragCoord zwrócą współrzędne (0.5, 0.5) dla piksela znajdującego się w lewym górnym rogu i tym samym wartości pionowe będą się rozchodzić z góry na dół. Rozchodzenie się wartości poziomych nie ulegnie zmianie.
Deklaracja drugiego parametru pixel_center_integer, będzie skutkować przesunięciem wartości zwracanych przez komponenty x i y zmiennej gl_FragCoord o pół piksela tak, aby współrzędne dla centrum piksela były pełnymi liczbami. Dotychczasowa wartość dla piksela początkowego (0.5, 0.5) zostanie zamieniona na (0, 0).
Użycie powyższych redeklaracji zmiennej gl_FragCoord, nie wpływa w żaden sposób na proces rasteryzacji, jak również na komponent z przechowujący wartość głębi fragmentu.
[1] Wywołania shadera są grupowane i wykonywane jednocześnie przez wiele jednostek wykonawczych, dlatego wątek przetwarzający taki fragment będzie go wykonywał do końca wraz z innymi w grupie, jednak wszelkie operacje będą przez niego ignorowane.