Autor Wątek: SDL2 Obliczenie proporcji tekstury do rozdzielczości okna  (Przeczytany 881 razy)

Offline Mentaris

  • Użytkownik

# Lipiec 10, 2017, 19:35:45
Witam, natywna rozdzielczość to 1920x1080.

Powiedzmy dla przykładu że mam okno 800x600, tak wiec chce zescalować teksturę tak by jej wielkość była proporcjonalna do wielkości okna jak i by znajdowała się w tym samym miejscu co w przypadku wielkości natywnej.
Próbowałem w ten sposób iż obliczałem ile procent z liczby 1920 jest liczby 800 i w tedy zamieniałem ją na "po przecinku" i wywoływałem z tym SDL_RenderSetScale. Jednakże jakoś nie za bardzo mi to wyszło.
Podkreślę że mówimy tutaj o interface-sie, czyli przyciski, okna w samej grze etc.

Wie może ktoś jak to obliczyć/zrobić?

Napomnę jeszcze tylko że znalazłem w internecie coś takiego:
gameobject.x = 10 * game_resolutionx / 1600;
gameobject.y = 10 * game_resolutiony / 1200;
Gdzie 1600x1200 to niby natywna rozdzielczość.
Jednak za chiny nie wiem jak to poprawnie zinterpretować.
« Ostatnia zmiana: Lipiec 11, 2017, 18:12:39 wysłana przez Mentaris »

Offline Mr. Spam

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

Offline Kyroaku

  • Użytkownik

# Lipiec 11, 2017, 18:14:23
Twój kod byłby tu najbardziej wskazany.

Myślę, że próbówałeś zrobić to dobrze, ale gdzieś się walnąłeś.
SDL wymnaża koordynaty przez wartości ustawione w SDL_RenderSetScale(), więc jeśli programujesz pod rozdzielczość 1920x1080, a rozdzielczość okna to 800x600, to musisz koordynaty "zmniejszać" tak, aby wartość na osi x równa 1920 była równa 800 pikseli, a wartość na osi y równa 1080 była równa 600 pikseli.

Robisz więc SDL_RenderSetScale(renderer, 800/1920.0f, 600/1080f) i kodzisz tak, jakbyś miał ustawioną rozdzielczość 1920x1080.

Tyle wywnioskowałem z dokumentacji. Funkcji nigdy nie używałem, ale mam nadzieje, że pomoglem :)

Offline ChristopherTa...

  • Użytkownik

# Lipiec 12, 2017, 00:18:53
I ja też witam,
Czytając post zaowocowało w kilka pytań u mnie w głowie:

1. Czy używasz OpenGL'a czy tylko SDL2?
2. Jak zrozumieć 'zeskalować' i 'proporcjonalna wielkość'?
3. Czy tekstura jest częścią UI?

Więc odpowiadam na wszystkie 3:

1. Nie wiem co robi SDL_RenderSetScale (jak poprzednik nie miałem styczności z SDL_RenderSetScale): czy ona tylko skaluje - tworząc pikselową blokowość, czy także interpoluje - jak program graficzny (dokumentacja mówi o 'zależności od renderera', a jakaż to jest: nie wiem). Jeśli to jest problemem (funkcja SDL produkuje pikselozę) rozwiazania są dwa, lub trzy (zależy czy używasz dodatkowo OpenGL'a). Pierwsze: polega na tym, że swój program wzbogacasz o jakąś biblioteczkę do skalowania, która interpoluje bilinearnie czy bikubicznie i używasz jednej z tych opcji (zależnie od gusty). Druga: używasz do tego zewnętrznego programu: tworząc kilka wersji jednej grafiki dla różnych rozdzielczości. Trzecie: pozwalasz na to OpenGL'owi używając mipmap'owania (i może to o tym mówi dokumentacja SDL_RenderSetScale). Jest jeszcze funkcja SDL_RenderSetLogicalSize która ma być lepsza, ale zapewna robi to samo co SDL_RenderSetScale, rzekomo umożliwia obliczanie poprawnego skoku w platwormowkach (tej też nie używałem przepisuje tu z innego tekstu, więc klamać mogę).
2. Obraz w rozdzielczości 1920x1080 to obraz który ma proporcje 16:9, 800x600 za to ma 4:3, więc tekstura będąca kwadratem w 16:9 będzie przeskalowanym wysokim prostokątem w 4:3 kiedy przeskalowana będzie szerokość i wysokość kwadratu, i będzie zajmować proporcjonalnie taką samą powierzchnię na ekranie jak w przypadku 16:9 (a), za to przeskalowana wielkość kwadratu proporcjonalna tylko względem szerokości da kwadrat niższy (b), niż taki przeskalowany przez wielkość proporcjonalną względem wysokości (c), ale oba nie będą miały takiej samej (proporcjonalnie) powierzchni. Jeśli SDL_RenderSetScale działa w sposób a, a chciałbyś np. c to całą logikę skalowania trzeba będzie przepisać.
a:                                          b:                                          c:
float skala_dla_szerokosci = 800 / 1920;    float skala_dla_szerokosci = 800 / 1920;   
float skala_dla_wysokosci  = 600 / 1080;                                                float skala_dla_wysokosci  = 600 / 1080;
obiekt.x = obiekt.x * skala_dla_szerokosci; obiekt.x = obiekt.x * skala_dla_szerokosci; obiekt.x = obiekt.x * skala_dla_wysokosci;
obiekt.y = obiekt.y * skala_dla_wysokosci;  obiekt.y = obiekt.y * skala_dla_szerokosci; obiekt.y = obiekt.y * skala_dla_wysokosci;
obiekt.w = obiekt.w * skala_dla_szerokosci; obiekt.w = obiekt.w * skala_dla_szerokosci; obiekt.w = obiekt.w * skala_dla_wysokosci;
obiekt.h = obiekt.h * skala_dla_wysokosci;  obiekt.h = obiekt.h * skala_dla_szerokosci; obiekt.h = obiekt.h * skala_dla_wysokosci;
16:9 +--------------------+                 +--------------------+                      +--------------------+
     |                    |                 |                    |                      |                    |
     |                    |                 |                    |                      |                    |
     +---+                |                 +---+                |                      +---+                |
     |   |                |                 |   |                |                      |   |                |
     +---+----------------+                 +---+----------------+                      +---+----------------+
4:3  +--------------+                       +--------------+                            +--------------+
     |              |                       |              |                            |              |
     |              |                       |              |                            |              |
     +-+            |                       |              |                            +---+          |
     | |            |                       +-+            |                            |   |          |
     +-+------------+                       +-+------------+                            +---+----------+
3. Jest jeszcze sprawa taka, że tekstura może być częścią interfejsu. Jeśli ten, jak w strategiach, jest fragmentem ekranu: można wtedy powierzchnię dla pola gry wyznaczyć stałą proporcjonalną, a skalować tylko interfejs (co tworzyć będzie pasek o różnej szerokości lub wysokości zależnie od położenia UI) i zmieniać jego układ jak na internetowych stronach responsywnych.
16:9 +--------------------+                 +--------------------+
     |           |        |                 |    pole            |
     |   pole    |        |                 |    gry             |
     |   gry     |        |                 |                    |
     |           |        |                 |--------------------|
     +--------------------+                 +--------------------+
4:3  +--------------+                       +--------------+
     |           |  |                       |    pole      |
     |   pole    |  |                       |    gry       |
     |   gry     |  |                       |--------------|
     |           |  |                       |              |
     +--------------+                       +--------------+

Pozdrawiam.
Krzysztof.
« Ostatnia zmiana: Lipiec 12, 2017, 07:19:07 wysłana przez ChristopherTaivaus »

Offline Mentaris

  • Użytkownik

# Lipiec 12, 2017, 15:13:57
Dziękuje za tak rozbudowane odpowiedzi, w przypadku Krzysztofa nawet bym się nie spodziewał że ktoś z takim entuzjazmem przyjdzie z pomocą. : )

Odpowiadając, nie korzystam z OpenGL i raczej nie zacznę, tym bardziej że planuje przeportować tą gre na androida.
Tekstury są konkretnie w menu głównym, czyli przycisk start, logo, wyjście, opcje, okna settingu etc.

Wracając jednak do sprawy. Obie metody poprawnie skalują, jednak większy potencjał zauważyłem w skalowaniu tekstur po przez obliczanie ich nowych x,y,w,h, a to dlatego iż wykrywanie eventów jest na podstawie tych właśnie współrzędnych.

Aczkolwiek problem polega na tym że tak jak w przypadku skalowania renderu, tak w przypadku obliczania współrzędnych, pozycja x y jest nie proporcjonalna do wielkości ekranu.
Dodałem załącznik gdzie pokazuje graficznie o co mi chodzi.

To może po prostu podam kod który wyskrobałem:
Wywołanie:
                           (x,   y,    0,  renderer);
button[1].create(-1, clientArea.h / 2 + 140, 0, renderer);
0 = nieważne ponieważ jest to związane z grafiką która jest ładowana
x = Tutaj chyba problem wychodzi iż dodałem suche 140, tylko jak w inny sposób podać przesunięcie o konkretną ilość? Procentowo? Jeśli tak to na jakiej podstawie?

Obliczenie w samym obiekcie button:
SDL_QueryTexture(t_active, NULL, NULL, &p_range.w, &p_range.h);
if (x == -1) // -1 oznacza że chcę by x bądź y znajdował się na środku ekranu
x = intrfc->clientArea.w / 2 - p_range.w / 2;
if (y == -1)
y = intrfc->clientArea.h / 2 - p_range.h / 2;
p_range.x = x; // p_range przechowuje inty
p_range.y = y;

float rescale = (float)intrfc->clientArea.h / 1080; clientArea to po prostu wysokość ekranu
p_range.x = round(p_range.x * rescale);
p_range.y = round(p_range.y * rescale);
p_range.w = round(p_range.w * rescale);
p_range.h = round(p_range.h * rescale);
Czegoś pewnie nie zrozumiałem i teraz takie rzeczy wychodzą. Pierwszy raz za takie rzeczy się zabieram, praktycznie Krzysztof bardzo dużo mi wytłumaczył.

Offline Kyroaku

  • Użytkownik

# Lipiec 12, 2017, 18:12:24
Jeśli chodzi o problem, jak na obrazkach, które wysłałeś, to tak na szybko patrząc, to:
if (x == -1) // -1 oznacza że chcę by x bądź y znajdował się na środku ekranu
        x = intrfc->clientArea.w / 2 - p_range.w / 2;
if (y == -1)
        y = intrfc->clientArea.h / 2 - p_range.h / 2;
intrfc->clientArea.w i intrfc->clientArea.h, to rozmiar ekranu w pikselach, tak? Natomiast p_range.w i p_range.h, to rozmiar obiektu w pikselach, ale dla natywnej rozdzielczości, tak? Jeśli tak, to rozmiar obiektu musisz oczywiście wyskalować i będzie w porządku.

Cytuj
Obie metody poprawnie skalują, jednak większy potencjał zauważyłem w skalowaniu tekstur po przez obliczanie ich nowych x,y,w,h, a to dlatego iż wykrywanie eventów jest na podstawie tych właśnie współrzędnych.
Według mnie łatwiej by było zrobić jednak SDL_RenderSetScale() i programować dla natywnej rozdzielczości, a kiedy potrzebujesz przejść z przestrzeni natywnego ekranu do przestrzeni realnego ekranu, lub odwrotnie, to wystarczy użyć SDL_RenderGetScale() i wymnożyć wartości przez scaleX, scaleY, lub 1/scaleX, 1/scaleY.

Przykładowo współrzędne myszki musiałbyś przenieść z przestrzeni ekranu realnego do natywnego, co by wyglądało jakoś tak:
float scaleX, scaleY;
SDL_RenderGetScale(renderer, &scaleX, &scaleY);
int newMouseX = mouseX / scaleX;
int newMouseY = mouseY / scaleY;

Zawsze, zanim coś na piszesz, to zastanów się, czy to, co chcesz zrobić nie zablokuje Cię w przyszłości i staraj się myśleć jak najbardziej w przód. O ile na początku wszystko idzie gładko, to przy źle zaprojektowanym kodzie im dalej zajdziesz, tym bardziej będziesz musiał kombinować, albo przerabiać podstawy. Bez doświadczenia oczywiście skutek projektowania kodu będzie mierny, ale IMHO warto sobie wyrobić taki nawyt i trenować ;)

Życzę wytrwałości :D

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Lipiec 12, 2017, 20:11:11
nie korzystam z OpenGL i raczej nie zacznę, tym bardziej że planuje przeportować tą gre na androida
Korzystanie z OpenGL nie tylko nie uniemożliwia portowania gry na Androida (albo jakąkolwiek inną platformę) ale wręcz to ułatwia. :) No ale wymaga to trochę zamozaparcia i zrobienia kilku elementów.

Offline Mentaris

  • Użytkownik

# Lipiec 13, 2017, 15:18:50
Miałeś racje Kyroaku, wystarczyło obliczyć wielkość przed wyśrodkowaniem. Co do samej porady, następnym razem postaram się zrobić tak jak wspomniałeś, a twój kod dotyczący przenoszenia pozycji myszy bardzo mi w tym pomoże.
Postaram się też robić jak mówisz, w programowaniu łatwo się potknąć, planowanie jest tu niesamowicie potrzebne.

Całość wykonałem jednak troszkę inaczej. Usunąłem obliczanie x,y w samym obiekcie,  obliczyłem je przed wywołaniem funkcji opierając się na procentach. Czyli clientArea.w bądź clientArea.h mają wartość 100%. Dzięki temu rozwiązaniu pozycje były zachowane, a mi łatwiej wprowadzać gdzie dana tekstura ma się znajdować.

Zachciało mi się robić cały interface samemu, bez użycia jakiejkolwiek biblioteki. Nie żałuje jednak, więc jeśli to czytasz, a jesteś początkującym, pamiętaj, staraj się robić jak najwięcej samemu. Da ci to ogromne doświadczenie.

Dziękuje wam za pomoc i cenne rady. Problem rozwiązany. : )