Skąd Fragment Shader wie, jakiej zmiennej użyć dla koloru piksela?

83

Widzę wiele różnych Fragment Shaderów,

I wszystkie używają innej zmiennej dla „koloru wyjścia” (w tym przypadku flatColor). Skąd więc OpenGL wie, co próbujesz zrobić?

Domyślam się, że to działa, ponieważ flatColorjest jedyną zmienną zdefiniowaną jako out, ale możesz dodawać więcej outzmiennych, prawda? A może po prostu się załamie?


Właściwie, jako test, właśnie uruchomiłem to:

Działało dobrze, czy użyłem, xczy y.


Ponadto mamy predefiniowany plik gl_FragColor. Jaka jest różnica i dlaczego ludzie zwykle nalegają na używanie własnych zmiennych?

mpen
źródło

Odpowiedzi:

143

Ponadto mamy predefiniowany gl_FragColor.

Zacznijmy od tego. Nie, nie masz predefiniowanego gl_FragColor. To zostało usunięte z rdzenia OpenGL 3.1 i nowszych. Jeśli nie używasz kompatybilności (w takim przypadku twoje shadery 3.30 powinny wskazywać #version 330 compatibilityna górze), nigdy nie powinieneś tego używać.

Wróćmy teraz do danych wyjściowych Fragment Shadera zdefiniowanych przez użytkownika. Ale najpierw krótka analogia.

Pamiętasz, jak w Vertex Shaderach masz dane wejściowe? I te nakłady stanowią wierzchołek indeksów atrybutów, cyfry, które przechodzą do glVertexAttribPointera glEnableVertexAttribArrayi tak dalej? Ustawiasz, które wejście pobiera z którego atrybutu. W GLSL 3.30 używasz następującej składni:

Powoduje to, że colordane wejściowe modułu cieniującego wierzchołki pochodzą z lokalizacji atrybutu 2. Przed 3.30 (lub bez ARB_explicit_attrib_location) należałoby ustawić to jawnie glBindAttrbLocationprzed połączeniem lub wysłać zapytanie do programu o indeks atrybutów glGetAttribLocation. Jeśli nie podasz jawnie lokalizacji atrybutu, GLSL przypisze lokalizację arbitralnie (tj. W sposób zdefiniowany w implementacji).

Ustawienie go w module cieniującym jest prawie zawsze lepszą opcją.

W każdym razie dane wyjściowe Fragment Shader działają prawie dokładnie w ten sam sposób. Moduły cieniujące fragmentów mogą zapisywać w wielu kolorach wyjściowych , które same są mapowane do wielu buforów w buforze ramki . Dlatego musisz wskazać, które wyjście trafia do którego koloru wyjściowego fragmentu.

Ten proces rozpoczyna się od wartości lokalizacji wyjściowej fragmentu. Jest ustawiony bardzo podobnie do lokalizacji wejściowych Vertex Shader:

Istnieją również funkcje API glBindFragDataLocationi glGetFragDataLocation, które są analogiczne do glBindAttribLocationi glGetAttribLocation.

Jeśli nie wykonasz żadnych wyraźnych przypisań, implementacje zwykle przypiszą jedną z twoich zmiennych wyjściowych do lokalizacji 0. Jednak standard OpenGL nie wymaga takiego zachowania, więc nie powinieneś też na nim polegać.

Teraz, żeby być uczciwym, twój program powinien się nie połączyć, gdy użyłeś dwóch wyjść, które nie miały różnych lokalizacji wyjściowych. Prawdopodobnie zdarzyło się, że Twój kompilator zoptymalizował ten, którego nie napisałeś, więc trochę zapomniał o tym, kiedy nadszedł czas, aby sprawdzić błędy konsolidatora.

Nicol Bolas
źródło
4
Chciałbym móc dać więcej niż +1. Spektakularna odpowiedź. Zastanawiałem się, jak działają lokalizacje wyjściowe fragmentów. :)
kevintodisco,
Ahh… więc wycofali się gl_FragColorna korzyść większej elastyczności przy wyborze bufora do zapisu? Ma sens. Dzięki jeszcze raz!
mpen
3
@Mark: „Wycofanie” oznacza „nadal możesz z niego korzystać, ale może zostać usunięte w późniejszych wersjach”. „Usunięty” oznacza usunięty . Jest różnica. To, co zostało oznaczone jako przestarzałe w GL 3.0, zostało usunięte w 3.1 (z wyjątkiem kilku rzeczy).
Nicol Bolas
3
@NicolBolas: „GL 3.3 i nowsze (to zmiana w stosunku do poprzednich wersji) przypiszą je wszystkie do lokalizacji 0”. Zastanawiam się, gdzie to gwarantuje specyfikacja. 3.3 mówi: „Gdy program jest połączony, wszelkie zmienne różniące się bez określonego [...] powiązania lub wyraźnie ustawione w tekście modułu cieniującego zostaną automatycznie powiązane z kolorami fragmentów i indeksami przez GL. Wszystkie takie przypisania będą używać indeksów kolorów zero.". Ostatnie zdanie nie występuje w 3.2. Jednak indeks nie odnosi się do numeru koloru, ale tylko do wskaźnika mieszania z dwóch źródeł (który został dodany w 3.3).
derhass
1
Dziękuję @NicolBolas, świetna odpowiedź! Więc nie do końca to zrozumiałem i po prostu chcę to zrobić „bez pytania”. W naszych shaderach zawsze określamy, layout (location = 0) out vec4 outColorkiedy rysujemy do tego, co jest związane (najczęściej jest to „ekran”, a nie FBO). Więc domyślnie, jeśli nie związaliśmy FBO (lub innego bufora) za pomocą glDrawBuffers i określimy location = 0, czy zawsze będzie rysował poprawnie na ekranie?
AzP
9

Chciałbym to określić dla OpenGLES 3.1, który używa łącza GLSL_ES_3.10 :

§4.4.2

Jeśli jest tylko jedno wyjście [w Fragment Shader], nie trzeba określać lokalizacji, w takim przypadku domyślnie jest to zero.

Lorenzo Belli
źródło