Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Някак си ми се искаше да работи с графика, и реших да направя SSAO от земята, на базата на моя опит с проследяване на лъчи и по-рано придобитите знания за това как като цяло би трябвало да работи. По принцип, аз поставя задачата да пише на своя двигател демо, като се използват най-различни технологии. Също така беше решено poizuchat разсрочено засенчване и екранно пространство местните отражения, но повече за това някой друг път. В тази статия ще се съсредоточи върху SSAO.

За нетърпеливите, ето резултата:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Това, което казваме Wikipedia за околната оклузия:

засенчване модел, използван в триизмерна графика и позволява да добавите реалистичен образ чрез изчисляване на интензитета на светлината, която достига повърхността на точката. За разлика от местните методи, като Фонг засенчване, метод стайна оклузия е глобално, т.е. стойността на яркостта на всяка точка на обекта зависи от други обекти в сцената. По принцип, това е доста смътно напомня на глобалното осветление.

Оказва се, че ние трябва да се изчисли колко светлина достига до конкретна точка на полукълбо, ориентирани по протежение на нормалното в този момент. Мога дори да направи снимка в Photoshop:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Това, което виждаме тук:

На всичко отгоре е камера, която изглежда в нашия сцена.

Различните цветове показват точката на обекта, неговото нормално и полукълбо, на която ще се съберат засенчване.

Точка посочено fioleovym не затъмнява.

Въпросът маркирани в жълто - засенчени от доста малко.

Точката посочено от синьо - сенчести почти наполовина.

Но на мястото на обекта във фонов режим, са маркирани в оранжево, на теория, от гледна точка на камерата на частично блокиран от предния обекта, но тъй като тя е сравнително далеч от обекта, след което обектът е в действителност пред че да не закрива точка. С това ние ще трябва да се бори сам, за да се избегнат неприятни артефакти под формата на тъмни силуети на предмети.

По този начин, ние трябва да се изчисли оцветяване за всяка точка, като се вземе предвид разстоянието до обекта, който се отнася. Това ще бъде нашата околна оклузия.

Реших, за разлика от "традиционната» SSAO (като това, което, ако си спомням правилно, първият използван в Crysis) брой не е в място на екрана, и в пространството на оглед. Недостатък на този подход е по-сложните изчисления (макар и също трябва да изглежда, провери и сравни), плюс - по-точно AO.

Така че, за да се изчисли околна оклузия, ние се нуждаем две текстури: дълбочината и обичайното.

По-скоро, първата функция тук дори не е необходимо, тъй като той се използва в нормален запис.

Виж текстура с нормали ще бъде като този:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Дебелина на структурата, ние ще проведе "стандартна дълбочина» OpenGL.

дълбочината на стойностите, посочени в интервала [-1..1] и построени в 64-та степен, че приличам нещо като това:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Но, тъй като ние ще използваме пространството за оглед, ние трябва да възстанови позицията на точката на дълбочина оглед пространство. За информация как да направите това, вижте последната част на "nishtyak". За сега, нека бъдем четири функции в Shader:

Също така, за да се получи разнообразие в нашия изчисляване на АО, ние се нуждаем от текстура с шума. Най-често срещаният текстура с шум, дори и аз няма да го покажа тук. В допълнение към това текстура специално за нас ще бъде необходимо текстура координати. Такава структура за изготвяне на екрана на Texel пиксел. Като цяло, това не е задължително, но е желателно, че пробата е била "по-небрежно".

Общо, на входа на Shader на фрагмент имаме три текстури, и две групи от текстурата координати.

Трябва да се отбележи, че в моя двигател за подкрепа на различни версии на шейдърите, следните неща са направени:
Променлива член в Shader на фрагмент - etFragmentIn. При по-старите шейдъри заменя с различна. нов в инча
Резултати фрагмент шейдър записани в променлива etFragmentOut (gl_FragColor в старите шейдъри и "вън vec4." + GlBindFragDataLocation нови версии).

Общо шейдър парче вече имаме:

Сега вече можете да преминете направо към изчисляването на нашия засенчване.

Основната идея е, както следва: в този момент, за да получите позицията и нормално, след това генерира няколко произволни посоки на полукълбо, работа нормално, и да ги покажат на сянка. Резултатът се събира и се разделя на броя на пробите. По този начин ние искаме да се контролира най-малко три parametra:
1) на броя на пробите;
2) Минималното разстояние, на което ние проверяваме сянка (това е необходимо за нас да се отървете от някои неприятни артефакти);
3) максималното разстояние, на което ние проверяваме сянка;

Ето няколко снимки за сравнение на параметъра: Броят на пробите - колкото повече, толкова по-гладка и красива оцветяване, получаваме:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Максималното разстояние - колкото по-голяма е тя, толкова "по-голям", а по-мек ние оцветяване:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

За да тествате сцена (Crytek Sponza) аз използвах следните параметри:

За съжаление, не са дошли на ум, как да се отървем от тези параметри и да ги изчисли на базата на това, което имаме на екрана. Аз ще се радвам, ако някой ще ви каже къде да отида в тази посока.

Така че ние всички трябва да се изчисли засенчване на всяка точка на екрана. Първо трябва да намерите най-нормалното в този момент и си положение (да не забравяме, че ние работим с оглед пространство). Това се прави само с четене от нормалната текстура и възстановяване на ситуацията в дълбочина:

etTexture2D функция - също е магията на моя двигател. За по-стари версии на GLSL го превръща в texture2D, нов в текстура.

Сега, не в града, всичко това в тялото на главната функция (), ръководител на специална функция, която изчислява засенчването в даден момент. Обадих жалък си performRaytracingInViewSpace:

Ами, всъщност, да не се мъчи, останалата част от Shader:

В резултат на това ние ще се сенки тази точка. Ако имате нужда от покритие, ние просто се изважда засенчване на уреда:

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

По този начин, тайната в нашата изчисляване на функцията на засенчване. Нека да разгледаме това по-близо.

Тук трябва да се генерира случайна посока на полукълбо, работа в нормалното време. Аз го правя много просто: нормализиране стойността на текстурата на шума, и ако тя е в другата полуравнина от нас желания към нормалното, тя се умножава по -1. Тя изглежда така:

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

Сега имаме случайна посока, в която ние ще направите избор. Ние се измести точката в тази посока при случайна стойност между MIN_SAMPLE_SIZE и SAMPLE_SIZE и да го проектираме в място на екрана. Тогава ние се получи някои нови координати текстура и дълбочина в диапазона [0..1].

На следващо място, ние гледаме на каква дълбочина сме на новите координати на текстурата, направи нов избор от дълбочината на текстурата. След това проверете дали новият дълбочината е повече от това, което имаме, след като проекцията, а след това новата точка се намира на нашия собствен и не може да се припокриват, няма припокриване - нула връщане от функцията:

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

Това, което трябва да се изчисли:
- колко обекта (до точката, че попаднахме) припокрива нашата отправна точка.

Това, което трябва да се вземе предвид:
- колкото по-близо до прогнозирания новата точка, толкова по-голямо припокриване;
- ако на нова точка за силно "далече" от прогнозирания, толкова по-малко се припокриват.

Общо: ние трябва да сравните два нелинейна дълбочина, което е вероятно да бъде в близост до единство. Можете отново да възстановите дълбочината на линейната, но можете да направите малко хак и да получите стойност, която характеризира дълбочината. След известно експериментиране, дойдох до заключението, че функцията на формата
най-подходящ за такава оценка.

Общо, ние имаме две стойности за оценка на дълбочина, да се прави разлика между тях, която ще се характеризира с разстоянието на една точка от друг:

Тъй като искаме да се контролира степента на засенчване, ние можем да се въведе koeffetsient за тази разлика. В последния вариант се получава, както следва:

За да тествате модела Използвах стойност DEPTH_DIFFERENCE_SCALE равно на 3.33333. Всичко зависи от степента, в която ние се възползва от това, което искаме да се изчисли засенчване.

Сега имаме разстояние между две точки, нека да се изчисли степента на засенчване. Отново, след много експерименти, дойдох до заключението, че най-добре описва засенчване функция на формата:

За да го направи по-меко и приятно, все още можете да се вземе предвид разстоянието, което сме направили проба (намален до интервала [0..1]). Получената формула изглежда така:

Ето как да се отрази на размера на разстояния (този, който DEPTH_DIFFERENCE_SCALE)

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

С намаляване на разстоянието (в DEPTH_DIFFERENCE_SCALE <1.0) за объектами появляются темные ореолы. При увеличении масштаба затенение становится практически незаметным.

Ами, всъщност, и всичко, за всяка точка ние почувствахме, засенчване.

Сега, когато имаме тази страхотна функция за изчисляване на засенчване мисля за това дали може по някакъв начин да го удължи? Да, имаме почти без да натоварва може да изчисли първия цвят размисъл (както аз го разбирам, алгоритъм, като например запушване място на екрана посока - проверете в Google). Всичко, от което се нуждаете - е да направите избор от текстурата с цвета на новите координати и се вземе предвид влиянието на цвета върху текущата точка. Предполагаме, че ефекта на цвят е пряко пропорционално на това колко нова точка предимство пред нашите оригинални. Ето защо, сега нашата мода функция performRaytracingInViewSpace не плава възвръщаемост, както и цвета (vec4) и алфа компонент ще запази светлосенките. Също така, ние се нуждаем от текстурата на цвят. Тъй като съм използвал в демо отлага засенчване, то е вече имам, може да се наложи да затегнете множествена направи цели или да направите многоходова атака. Като цяло, ние на входа има и друга текстура:

Функция сега изглежда така:

Е, в чиято основна функция, сега ние не плават, и vec4:

В резултат на това, ние получаваме нещо подобно на това изображение (алфа канал не е показано, и яркостта се увеличава с коефициент 10 за яснота):

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

В тази структура, както може да се види, цветът се запазва, което е отразено от околните обекти. Ние използваме:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

На пръв поглед разликите малко, но трябва да се търсят в правилната картина: осветлението е много по-мек и по стените има малки отражения върху "завеси". Като цяло, се оказа, че използвате интересна техника при минимални разходи.

Общо, това, което ние имаме:
1) за да се изчисли един вид глобално покритие, не perediraya първоначален код шейдър;
2) изчисляване на първо отражение светлината с минимални усилия и промени в алгоритъма;
3) На последно място, да се разбере как работи.

Както обещах, в последната част, аз публикувате код откъси:

Запис и нормалното възстановяване (взет от тук):

разпоредби за регенериране с оглед пространство от дъното и проектиране обратно:

clipPlanes - е разстоянието до близки и далечни стреляйки самолет (това, което ние се хранят на създаването перспектива проекцията на функцията).
texCoordScales - обратни матрични компоненти прожекционни взети със знак минус.

Те са необходими за правилното положение за възстановяване въз основа на ъгъла на наблюдение, за обзорната точка пропорции.


Като цяло, в демото, има много неща, в допълнение към сенки и отразяване на светлината:

Screen пространство околната оклузия, като се вземе предвид нормалното и изчисляване на отражението на светлината

Както казах в началото, това изпълнение е много проста и ясна. Ето линкове към някои други техники, които се използват в игри: