Как върховите и фрагментните шейдъри комуникират в OpenGL?

Наистина не разбирам как работи фрагментният шейдър.

знам това

  • вертексният шейдър се изпълнява веднъж на върхове
  • шейдърът на фрагменти се изпълнява веднъж на фрагмент

Тъй като фрагментният шейдър не работи на връх, а на фрагмент, как може да изпраща данни към фрагментния шейдър? Броят на върховете и броят на фрагментите не са равни.

Как може да реши кой фрагмент на кой връх принадлежи?


person Simon Müller    schedule 04.02.2015    source източник


Отговори (2)


За да разберете това, ще трябва да вземете предвид целия конвейер за изобразяване. Резултатите от върховия шейдър (освен специалния изход gl_Position) се предават като „асоциирани данни“ на върха към следващите етапи в конвейера.

Докато върховият шейдър работи върху един връх наведнъж, без изобщо да се интересува от примитивите, следващите етапи на тръбопровода вземат предвид примитивен тип (и информация за свързване на върхове). Това обикновено се нарича "примитивно сглобяване". Сега все още имаме единичните върхове със свързаните данни, произведени от VS, но също така знаем кои върхове са групирани заедно, за да дефинират основен примитив като точка (1 връх), линия (2 върха) или триъгълник (3 върхове).

По време на растеризирането се генерират фрагменти за всяко местоположение на пиксел в изходния пикселен растер, който принадлежи на примитив. По този начин свързаните данни на върховете, дефиниращи примитив, могат да бъдат интерполирани в целия примитив. В линия това е доста просто: прави се линейна интерполация. Нека наречем крайните точки A и B с всяка асоцииран изходен вектор v, така че да имаме v_A и v_B. Отвъд линията получаваме интерполираната стойност за v като v(x)=(1-x) * v_A + x * v_B във всяка крайна точка, където x е в диапазона от 0 (в точка A) до 1 (в точка Б). За триъгълник се използва барицентрична интерполация между данните на всичките 3 върха. Така че, докато няма съпоставяне 1:1 между върховете и фрагментите, изходните данни на VS все още дефинират стойностите на съответния вход на FS, просто не по директен начин, а индиректно чрез интерполация през използвания примитивен тип.

концептуален поток на шейдър на данни в GL

Формулата, която дадох досега, е малко опростена. Всъщност по подразбиране се прилага корекция на перспектива, ефективно чрез модифициране на формулата по такъв начин, че да се вземат предвид ефектите на изкривяване на перспективата. Това просто означава, че интерполацията трябва да действа, тъй като се прилага линейно в пространството на обекта (преди да бъде приложено изкривяването от проекцията). Например, ако имате проекция в перспектива и някакъв примитив, който не е успореден на равнината на изображението, преминаването с 1 пиксел надясно в пространството на екрана означава преместване на променливо разстояние върху реалния обект, в зависимост от разстоянието на действителната точка до равнината на камерата.

Можете да деактивирате корекцията на перспективата, като използвате квалификатора noperspective за променливите in/out в GLSL. След това се използва линейната/барицентрична интерполация, както я описах.

Можете също така да използвате квалификатора flat, който ще деактивира изцяло интерполацията. В този случай стойността само на един връх (така наречения "провокиращ връх") се използва за всички фрагменти от целия примитив. Целочислените данни никога не могат автоматично да се интерполират от GL и трябва да бъдат квалифицирани като flat, когато се изпращат към фрагментния шейдър.

person derhass    schedule 04.02.2015
comment
Така че предполагам, че можем да приложим тази формула и върху uv координати за текстуриране. - person Simon Müller; 05.02.2015
comment
Добре. Основният принцип се прилага към всички изходни променливи, а uv координатите са само един пример. Разширих отговора си, за да разгледам корекцията на перспективата за интерполацията. - person derhass; 05.02.2015

Отговорът е, че не го правят - поне не директно. Има допълнително нещо, наречено "растеризатор", което се намира между върховия процесор и фрагментния процесор в конвейера. Растеризаторът е отговорен за събирането на върховете, които излизат от върховия шейдър, повторното им сглобяване в примитиви (обикновено триъгълници), разбиването на тези триъгълници на "растери" от (частично) покриващи пиксели и изпращането на тези фрагменти към фрагментния шейдър.

Това е (предимно) хардуер с фиксирана функция, който не програмирате директно. Има някои ощипвания на конфигурацията, които можете да направите, които засягат това, което третира като примитив и това, което произвежда като фрагменти, но в по-голямата си част това е просто там между върховия шейдър и фрагментния шейдър, които вършат своето.

person Chris Dodd    schedule 04.02.2015