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 compatibility
na 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 glVertexAttribPointer
a glEnableVertexAttribArray
i tak dalej? Ustawiasz, które wejście pobiera z którego atrybutu. W GLSL 3.30 używasz następującej składni:
layout(location = 2) in color;
Powoduje to, że color
dane 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 glBindAttrbLocation
przed 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:
layout(location = 1) out secColor;
Istnieją również funkcje API glBindFragDataLocation
i glGetFragDataLocation
, które są analogiczne do glBindAttribLocation
i 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.