Autor Wątek: Jak wygenerować heightmap z *.jpg ?  (Przeczytany 1613 razy)

Offline Arek90

  • Użytkownik

# Marzec 07, 2016, 00:56:42
Witam głowię się nad mapami typu heightmap.
Chodzi o to jak wygenerować mapę heightmap.raw z pliku *.jpg

Poniżej zamieszczam grafikę.



Strona na której to jest http://www.mapy.zabytek.gov.pl/nid/?bbox=702802.219157352,499105.413632332,703145.257542825,499436.223935136

Offline Mr. Spam

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

Offline ChristopherTa...

  • Użytkownik

# Marzec 07, 2016, 06:38:19
Witam,
Cytuj
Jak wygenerować mapę heightmap.raw z pliku *.jpg?
Chodzi o program, który będzie wykonywał taką opearację?
Na podstawie zamieszczonego obrazka to, ostrzegam, dużo matmy przed Tobą, albo przeszukiwania zasobów internetu, ponieważ:

Jeśli ten zrzut ekranu zapisany jest do JPEG'u, to muszę zasmucić, pewne detale są usuwane podczas kwantyzacji, więc lepiej by było gdyby zrzut zapisany był w formacie BMP lub PPM czy nawet PGM, bo potrzebujesz tylko jednego kanału do obliczeń ( o ile obraz jest czarnobiały, jeśli kolorowy to trzeba zastowować desaturacje ).

Część matematyczna:
Każda wartość szarości obrazu to informacja mówiąca o tym pod jakim kątem pada światło:
  • dla kąta 45 stopni piksel będzie biały, a przy 90 i 0 (choć zapewne mapa nie przedstawia bieguna) będzie szary (wartość bajtu równa 128; w miejscu jakiejś kotliny, czy zasłonięty jakąś skałą: też będzie szary mogąc tworzyć nieścisłości w generowanej heightmapie), czyli wartość rosnąca od 128 do 255 będzie mieściła się pomiędzy 0-45 stopni i 90-45 (nie na odwrót!), załóżmy, że płaszczyzna/równina to wartość 192 czyli 22,5 stopnia (w rzeczywistości na załączonym obrazku może być inaczej sprawdź jaka jest wartość piksela po prawej stronie na czymś co zdaje się być jeziorem), więc wszystko powyżej 192 (wartość bajtu/szarości) będzie rosło/wznosiło się, a poniżej malało/obniżało się (wzgędem źródła światła);
  • więc z tą informacją dla każdego elementu (piksela) można określić czy w danym miejscu stok wgórza/góry się wznosi, lub opada;
  • przy czym trzeba pamiętać, że:
    • dwa sąsiednie piksele o takiej samej wartość szarości, niebędącej wartością 192, dają ten sam interwał wysokości (powiedzmy 10 metrów);
    • a także o tym, że wzniesienie może być strome ( czyli większej niż 45 stopni wartości kąta padania światła ) co może spowodować obliczanie spatku gdy powinno być obliczane wzniesienie;
Do obliczenia heightmapy należy stworzyć bufor o wielkość [szerokość * wysokość * detale], gdzie detale to liczba bajtów na element heightmapy (jeśli program ma możliwość wykorzystania więcej niż jednego bajtu na element tworzonej wizualizacji to lepsze będą generowane detale), w przedstawionym przykładzie interwał ( wysokości pomiędzy dwoma obliczanymi elementami ) dla kąta względem światła 22,5 stopnia to sinus z 0 stopni, a dla kąta 45 to sinus z 45, resztę należy obliczyć poprzez odpowiednią, interpolację.
Na podstawie każdego piksela obrazu i jego różnicy z sąsiednim(i) należałoby ustalić/obliczyć interwał i obliczyć jego wartość względem najniższego lub najwyższego elementu i przemnożyć go przez ułamek detalu, a następnie wpisać w bufor, najprawdopodobniej będzie trzeba to wykonać w dwóch krokach:
  • najpierw wypełnić bufor interwałami i zbierać/obliczać wysokość najwyższego i najniższego elementu, a także punktu początkowego względem najniższego ( lub najwyższego ):
(pseudo c/c++)
// funkcja fn_calc_interval oblicza insterwał na podstawie kąta padania światła
// powinna brać kilka sąsiadujących pikseli do interpolacji
// ale w tym momęcie jesteśmy na pierwszym,
// a wartość '0' względem powyższych założeń nie jest wykorzystywana
float interval_value = fn_calc_interval_in_ref_to_light( buffer_of_pixels[ 0 ], 0 );
buffer_of_intervals[ 0 ] = interval_value;
float cur_height = interval_value;
first_point_height = cur_height;
min_height = cur_height;
max_height = cur_height;
char last_pix_grey_val = buffer_of_pixels[ 0 ];
for( int currpix = 1; currpix < all_the_rest; currpix++ )
{
char cur_pix_grey_val = buffer_of_pixels[ currpix ]
if( cur_pix_grey_val == 192 )
{
buffer_of_intervals[ currpix ] = 0;
if( buffer_of_intervals[ currpix - 1 ] != buffer_of_intervals[ currpix ] )
{
float interval_value = fn_calc_interval_in_ref_to_light
// funkcja oblicza insterwał na podstawie kąta światła
// powinna brać kilka sąsiadujących pikseli do interpolacji
( buffer_of_pixels[ currpix ], buffer_of_pixels[ currpix  - 1 ] );
cur_height += interval_value ;
}
}
else
{
if( cur_pix_grey_val == last_pix_grey_val )
{
// z nadzieją, że jesteśmy w tej samej lini cachu co poprzedni interwał
// (zaoszczędzimi trochę czasu wykonywania)
buffer_of_intervals[ currpix ] = buffer_of_intervals[ currpix - 1 ];
cur_height += buffer_of_intervals[ currpix - 1 ];
}
else
{
float interval_value = fn_calc_interval_in_ref_to_light
( buffer_of_pixels[ currpix ], buffer_of_pixels[ currpix - 1 ] );
buffer_of_intervals[ currpix ] = interval_value;
cur_height += interval_value;
}
}
if( cur_height < min_height )
{
first_point_height += min_height - cur_height;
min_height = cur_height;
}
if( cur_height > max_height )
max_height = cur_height;
last_pix_grey_val = cur_pix_grey_val;
}
  • następnie znając wysokość pierwszego, najwyższego i najniższego z punktów obliczyć pozostałe:
(pseudo c/c++)
// detal o wielkości dwóch bajtów na element ( 0 - 65535 )
// fraction to wspominany powyżej ułamek detalu
float fraction = 65535.0f / ( max_height - min_height );
float height_value = first_point_height - min_height;
unsigned short height_detail = ( unsigned short )( height_value * fraction );
output_buffer[ 0 ] = height_detail;
for( int currpix = 1; currpix < all_the_rest; currpix++ )
{
height_value += buffer_of_intervals[ currpix ];
height_detail = ( unsigned short )( height_value * fraction );
output_buffer[ currpix ] = height_detail;
}
Przy czym kąt padania światła trzeba obliczyć na podstawie 3 wymiarów nie dwóch ( tak jak obliczane są bumpmapy ), jeśli założy się, że na obrazie pozycja wektora światła to po 45 stopni w płaszczyznach 'x' i 'y', czyli promień na nim nadchodzi z równej odlegości z 'zachodu' jak i z 'północy', ale już 'z' ma jakieś 50 stopni, więc trzeba innaczej to obliczać.
Mam nadzięję, że to coś pomoże i napisałem w miarę po polsku, lub ktoś przełorzy moją wypowiedź na polski, zbyt skomplikowanie myślę ( taka przypadłość... )...
Albo też mogę się mylić.
Choć po tych kilku godzinach nie myślenia o tym problemie, to zdaje mi się, że nie potrzebnie tak się rozpisywałem na temat światła (wspominałem już, że światło wpadające do atmosfery ulega załamaniu?)...
Pozdrawiam Krzysztof.
« Ostatnia zmiana: Marzec 07, 2016, 11:07:14 wysłana przez ChristopherTaivaus »

Offline Arek90

  • Użytkownik

# Marzec 07, 2016, 23:15:57
Liczyłem na coś w stylu użyj takiego programu i takich i takich opcji.
Plik BMP można zdobyć bo to przecież zrzut ekranu.
Pędzle w programie graficznym który używam są zrobione na takiej mniej więcej zasadzie.
Jakiś czas temu znalazłem mapę na której wystarczyło wskazać kursorem i pokazywało wysokość.

W każdym razie dzięki za fatygę.

Offline komorra

  • Użytkownik
    • Blog naszego teamu (o grze Voxelfield)

# Marzec 07, 2016, 23:31:21
Substance Bitmap2Material coś takiego chyba potrafi.

Offline albireo

  • Użytkownik

# Marzec 07, 2016, 23:33:21