Autor Wątek: Program do generowania normal map  (Przeczytany 1466 razy)

Offline Jakub27

  • Użytkownik

# Luty 05, 2017, 21:54:43
Chciałbym stworzyć mały program w C# do generowania normal map. Obecnie szukam ogólnych  informacji na temat generowania. Wiecie czy są jakieś gotowe biblioteki z algorytmem? Znacie może jakieś przydatne linki gdzie jest szerzej omówione zagadnienie normal map? W ogóle to są przyjęte jakieś standardy w tej technologii? Np zakres palety barw itp.  Z góry dzięki za odpowiedź :)

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline voytech

  • Użytkownik

  • +1
# Luty 06, 2017, 07:30:44
1. Jeżeli chodzi o normal mapy tangent space to spotkałem się z dwoma odmianami. Jest to normalmapa dla wypukłego obiektu na płaszczyźnie:


Jak napiszesz szader dla pierwszego typu to przy zastosowaniu tej drugiej tekstury (tej dolnej) efekty cieniowania będą dość dziwne. Konwersja jest prosta, wystarczy odwrócić kolory kanału zielonego na teksturze (np. G=255-G) albo w szaderze zanegować wartość współrzędnej Y.

Pierwszy typ (prawoskrętny układ współrzędnych) jest stosowany w Blenderze. Drugi typ (lewoskrętny) z tego co pamiętam był używany w cryenginie. Rozpoznać je dość łatwo, kanał Green wygląda jakby wypukłe elementy były oświetlone z dołu dla lewoskrętnego układu. Oczywiście zależy od tego czy wiemy które elementy są wypukłe. Normalmap pluggin do gimpa też tworzy drugi typ normalmapy. Przypuszczam, że pluggin NVidii dla photoshopa podobnie. Oba plugginy mają jednak opcję "Invert Y", więc można wygenerować mapę dla obu typów.

... Po krótkim sprawdzeniu Internetu Unreal Engine podobnie jak Cryengine.

Ogólnie to nie ma reguły odnośnie skrętności układu na normal mapie, więc jak oświetlenie dziwnie reaguje na ruch światła to pierwsze co trzeba sprawdzić to kanał Green tekstury.

2. Dekodowanie normal map w szaderze wygląda tak:
vec3 normal = normalize (texture2D(normalTexUnit,gl_TexCoord[0].st).xyz*2.0 - 1.0);
//albo poniżej jezeli chcemy miec idealne 0.0 dla 128.
vec3 normal = normalize (texture2D(normalTexUnit,gl_TexCoord[0].st).xyz*255.0/128 - 1.0);
z tego wynika, że paleta barw wygląda następująco: 0 = -1.0, 128 = 0.0, 255 = 1.0 dla każdego kanału RGB.

3. z pozostałych rzeczy to warto wiedzieć jak generuje się tangent i binormal wektor dla danego mesha, google np. "mesh tangent calculation". Biblioteki ładujące siatki (np. Assimp) zazwyczaj potrafią wygenerować tangent i binormal jak dany format pliku ich nie przechowuje. W skrypcie w Blenderze też to się da jakby co

4. oczywiście istnieją normal mapy dla obiektów lustrzanych co jeszcze bardziej utrudnia sprawę :)

5. Na początek to darowałbym sobie tangent space i zaczął od czegoś prostszego, np. world space (normal mapy bardziej kolorowe).

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Luty 06, 2017, 23:10:24
Tu nie można mówić o palecie barw, bo w kolorach pikseli na komponentach RGB są po prostu zakodowane wektory. Wektor normalny to (x, y, z) w zakresie -1.0...1.0 wskazujący kierunek prostopadły do powierzchni w danym punkcie, więc się ten zakres upycha do 0.0...1.0 (albo, jeśli przyjmiemy kodowanie jako liczba całkowita 1 bajt, to 0...255), tak że -1.0 to jest komponent koloru 0, 0.0 to jest 0.5 (albo 128), a +1 to jest 1.0 (albo 255).

Dlatego na obrazku powyżej, powierzchnia płaska, która ma wektor zwrócony w kierunku widza (0, 0, 1) ma kolor jasnoniebieski - (0.5, 0.5, 1.0) albo (128, 128, 255) / #8080FF.

Offline voytech

  • Użytkownik

# Luty 07, 2017, 06:40:08
@Reg: Gdyby wziąć pod uwagę to, że normalmapy można również tworzyć i modyfikować metodą malarską w photoshopie albo gimpie to mówienie o palecie barw ma jak najbardziej sens.




Offline koirat

  • Użytkownik

# Luty 07, 2017, 12:33:30
Jak byś tam miał jakiś gradient a nie pojedyncze prostokąty to miało by to może większy sens, ale wtedy raczej nie mówili byśmy o palecie barw.

Offline voytech

  • Użytkownik

# Luty 08, 2017, 00:28:16
Jak byś tam miał jakiś gradient a nie pojedyncze prostokąty to miało by to może większy sens, ale wtedy raczej nie mówili byśmy o palecie barw.

W zasadzie to można zawsze mówić o palecie barw dla normalmap bo pewien zakres kolorów raczej nie występuje, np. szary (#808080) bo wektor normalny o długości zero raczej się nie spotyka, podobnie z kolorami, z których odkodowany wektor ma długość różną od 1.

Poza tym wcześniejsza paleta może i ma mały sens ale obrazek i paleta powstała w pośpiechu :), chociaż ograniczenie ich ilości może mieć pozytywny skutek - czasami szybciej się coś tworzy gdy jest ograniczona ilość dostępnych kolorów do wyboru.

Paletę do malowania można dowolnie uszczegółowić a nawet wygenerować programowo bo format palety w gimpie jest tekstowy i prosty jak drut

GIMP Palette
Name: NormalMap
Columns: 7
#
 58 205 189 Untitled
128 128 255 flat
128 128 255 flat
129 238 192 Untitled
128 128 255 flat
...


Offline Jakub27

  • Użytkownik

# Luty 08, 2017, 03:33:17
Dzięki za odpowiedzi. Widzę, że się staracie ale chyba trochę mijamy się z tematem. Może wyjaśnię bardziej szczegółowo o co mi chodzi ponieważ w pierwszym poście nie opisałem tego w sposób jasny.  Otóż chcę zrobić program który będzie generował normal mapy na podstawie zdjęć które użytkownik wgra. Nie zależy mi na ich wyświetlaniu w 3d itp. ewentualnie zrobię podgląd na wygenerowaną normal mapę ale to tylko import fotki z folderu i tyle. Zależy mi na zrobieniu czegoś w stylu CrazyBump ale znacznie bardziej okrojonego z funkcjonalności(przynajmniej na początku). Na pewnej stronie było napisane,  że takie generowanie normal map ze zdjęć jest o wiele gorszej jakości niż np. zrobienie bake np. w Blenderze z modelu 3d.  Ogólnie zasada jest taka,  że im jaśniejszy piksel tym jest wyżej a im ciemniejszy tym niżej. Teraz pozostaje tylko napisać odpowiedni algorytm np. w C#. A może są już jakieś gotowe biblioteki np.  od Nvidii przeznaczone do tego celu? 

Offline voytech

  • Użytkownik

  • +1
# Luty 08, 2017, 13:22:52
Z blendera wyjdzie zawsze lepiej bo tam jest prawdziwa geometria i realne wektory normalne. W 2D to takie zgadywanie i pozostaje tylko manipulacja filtrami i innymi efektami w celu uzyskania w miarę wyglądającej tekstury. Być może pomogłoby tu jakieś AI albo może jakieś sieci neuronowe czy inne cuda które potrafiły by odczytać mapę wysokości ze zdjęcia?

Tutaj nie chodzi też o to czy piksel jest jaśniejszy czy nie ale bardziej o jego otoczenie. Piksel #808080 jak będzie miał wokoło sąsiednie piksele o wartości #808080 to będzie znaczyło, że leży na płaszczyźnie i na normal mapie jego kolor to #8080FF. Podobnie piksel czarny będzie miał kolor #8080FF jak wokoło będą same czarne. Jeżeli szary będzie sąsiadował z jednej strony z czarnym (najniższym) i białym (najwyższym) to znaczy że leży na stoku o nachyleniu 45 stopni i kolor normalmapy powinien być #2500DA. Już na podstawie tych danym powinieneś móc w stanie zrobić prostą konwersję height mapy na normalmapę, wystarczy potraktować piksele jako wysokość wierzchołków w siatce grida a później już z górki. Pewnie są jakieś liby co potrafią wyliczyć normalne w siatce.

Problem też jest taki, że pluginy nvidii czy te w gimpie badają otoczenie kilku pikseli i nie są w stanie "zobaczyć" większego trendu w zmianie wysokości w heightmapie. Jest jedna w miarę sensowna sztuczka, którą stosowałem, żeby zrobić normalmapę w gimpie, która dawała całkiem ładne cieniowanie:

1. Pierwszy etap do desaturacja obrazka, czyli konwersja na pseudo mapę wysokości. Można zgadnąć, że to jest jeden wielki fałsz bo coś jasnego na teksturze nie oznacza że jest wyżej. Ostre oświetlenie też zrobi przekłamania jak mocno oświetli kanty z jednej strony. Ogólnie przegrana sprawa

2. kopiujemy naszą mapę wysokości w kilku kopiach (widziałem tutoriale z kilkanaście a nawet z kilkadziesiąt różnych warstw :).

3. robimy normalmapę z pierwszej tekstury. Z niej uzyskujemy przede wszystkim drobne szczegóły, ale w szaderze będzie wyglądać ogólnie "płasko" i żaden pluggin nvidii tu nie pomoże.

4. na kolejnych pozostałych teksturach robimy blura o różnym stopniu i na nich robimy osobno normalmapy.
4a. można też zeskalować dany layer w dół i jego konwertować, pluggin będzie w stanie objąć większy obszar oryginalnej tekstury i uwydatnią się większe obiekty na teksturze (kamienie, cegły, etc.)



5. Doprowadzić poszczególne normalmapy do tych samych wymiarów (jeżeli były skalowane w dół) a następnie połączyć je w jedno. Można użyć trybu "Overlay" albo "Grain Merge" w Gimpie. One się najlepiej sprawdzają ale warto popróbować innych.

6. Po scaleniu w jedną warstwę warto ją znormalizować, żeby wektorki miały długość 1. W pluginie ustawiamy konwersję na "Normalize only".


7. 3D Preview pozwala ocenić wygląd naszej normalmapy, trzeba tylko zmienić stadardowe ustawienia z takich które nie występują w realnym świecie na bardziej sensowne. Zawsze Ambient zeruje a Diffuse na 50%, 100% w w przyrodzie nie występuje. Specular włączam i też ustawiam jak Diffuse.


Dzięki tej metodzie tekstura wydaje się "głębsza" i przy nisko padającym oświetleniu daje ładne cienie.

Powodzenia.
« Ostatnia zmiana: Luty 08, 2017, 13:27:35 wysłana przez voytech »