Skocz do zawartości

Optymalizacja Światła


 Udostępnij

Rekomendowane odpowiedzi

Witajcie!
Eksperymentuję sobie z prostym systemem światła w gmie, oto mój kod:
 

//CREATE
surf=surface_create(room_width,room_height);
surface_set_target(surf);
draw_clear(c_black);
surface_reset_target();
//CLEAN
if surface_exists(surf){
	surface_free(surf);
	}
//DRAW
if !surface_exists(surf){
	
	surf=surface_create(room_width,room_height);
	surface_set_target(surf);
	draw_clear(c_black);
	surface_reset_target();
	}


	surface_set_target(surf);
	draw_sprite_ext(sprBlackRectangle,0,camera_get_view_x(view_camera[0]),camera_get_view_y(view_camera[0]),1920,1080,0,c_black,.5);
	surface_reset_target();
	
	surface_set_target(surf);
	with(objUnit){
		gpu_set_blendmode(bm_subtract)
		draw_sprite_ext(sprLight2,0,x,y,1,1,0,c_white,1)
		gpu_set_blendmode(bm_normal)
		}
		
		
	surface_reset_target();
	draw_set_alpha(.8);
	draw_surface(surf,0,0);
	
	draw_set_alpha(1);

Problem jest taki że mam bardzo mało klatek przy kilkunastu obiektach, myślę że przez to że 'ciemność' rysuje się na całym roomie

Próbowałem ustawić to w wielkości viewa i podążać za kamerą.

surf=surface_create(1920,1080);
draw_surface(surf,camera_get_view_x(view_camera[0]),camera_get_view_y(view_camera[0]));

Tu fpsy są spoko i podąża za kamerą ale niestety światło i wypełnianie go się nie rysuje tak jak trzeba. 
Macie pomysł jak to naprawić tak żeby całość rysowała się elegancko w widoku kamery?

Odnośnik do komentarza
Udostępnij na innych stronach

2 godziny temu, Adriann napisał:

Tu fpsy są spoko i podąża za kamerą ale niestety światło i wypełnianie go się nie rysuje tak jak trzeba. 

Jeżeli rysujesz duszki na surface to X i Y będą zawsze 0,0 i nie "podąża" za kamerą tak jak by można było się spodziewać, ale możesz spróbować odjąć pozycję kamery żeby przesunąć rysowanie duszków na właściwą pozycję, oraz duszka "black rectangle" możesz rysować na pozycji 0,0

  • Lubię (+1) 1
Odnośnik do komentarza
Udostępnij na innych stronach

Godzinę temu, Konrad-GM napisał:

Jeżeli rysujesz duszki na surface to X i Y będą zawsze 0,0 i nie "podąża" za kamerą tak jak by można było się spodziewać, ale możesz spróbować odjąć pozycję kamery żeby przesunąć rysowanie duszków na właściwą pozycję, oraz duszka "black rectangle" możesz rysować na pozycji 0,0

Dziękuję, podążanie za kamerą działa!
Niestety problem małej ilości FPSów wciąż występuje, teraz gwałtownie spadają jak najedzie się na większą ilość ruchomych źródeł światła.

Da się to jakoś zoptymalizować poza zmniejszeniem ilości obiektów?

Odnośnik do komentarza
Udostępnij na innych stronach

Robiłem test czy gm da radę pociągnąć rtsa z setkami jednostek, już przy ledwo ponad 100 klatki lecą o połowę :P 

W lewym górnym rogu są fpsy oczywiście zasłonięte przez ikonki:|
W widoku bezpośrednio z YT widać normalnie
PS: podobne ilości powiedzmy pustych obiektów jak drzewa z dodanym światłem też sprawiają że fpsy spadają

 

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

Dziwne, że przy tylu obiektach laguje, robiłem już testy, gdzie 10000 obiektów daje radę, zresztą dobrym przykładem jest Forager - https://www.yoyogames.com/en/blog/forager-optimization-in-gamemaker

  • Lubię (+1) 1
  • Przykro (+0) 1
Odnośnik do komentarza
Udostępnij na innych stronach

Zamieniłem 

with(objUnit){
		gpu_set_blendmode(bm_subtract)
		draw_sprite_ext(sprLight2,0,x,y,1,1,0,c_white,1)
		gpu_set_blendmode(bm_normal)
		}

na

with(objUnit){
		gpu_set_blendmode(bm_subtract)
		draw_set_color(c_black)
		draw_circle(x-camera_get_view_x(view_camera[0]),y-camera_get_view_y(view_camera[0]),200,0)
		gpu_set_blendmode(bm_normal)
		}

I jest znacznie lepiej ALE przy 200-300+ jest ten sam problem
Testowałem ok 1000 jednostek i wtedy niezależnie od wszystkiego spadają fpsy o połowę albo gorzej, spróbowałem więc z domkami które miały tylko światło i niestety w dużych ilościach też spadają, nie aż tak ale spadają :(

Odnośnik do komentarza
Udostępnij na innych stronach

Jeżeli chodzi o mikro-optymalizacje, to przełączanie stanu karty graficznej np. `gpu_set_blendmode` wyrzuciłbym poza loopa, ale to pewnie niewiele zmieni i tak. Tego typu fog of war można też zrobić na tablicach, większe komórki i aktualizowałbyś je jedynie gdy jednostka przekroczy jej granicę. A rysować mógłbyś też tylko wycinek takiej tablicy na surface bez znaczenia ile jednostek jest na ekranie. Minus taki, że byłoby to bardziej "rozpikselowane", ale możesz spróbować wygłądzić krawędziie jakimś shaderem.

  • Lubię (+1) 1
Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy
8 godzin temu, Adriann napisał:
with(objUnit){
		gpu_set_blendmode(bm_subtract)
		draw_set_color(c_black)
		draw_circle(x-camera_get_view_x(view_camera[0]),y-camera_get_view_y(view_camera[0]),200,0)
		gpu_set_blendmode(bm_normal)
		}

Ja bym zrobił:

 

gpu_set_blendmode(bm_subtract);
draw_set_color(c_black);

with(objUnit){
	draw_circle(x-camera_get_view_x(view_camera[0]),y-camera_get_view_y(view_camera[0]),200,0);
}

gpu_set_blendmode(bm_normal);

 

Wtedy blend mode nie będzie tyle razy zmieniany.

  • Lubię (+1) 1
Odnośnik do komentarza
Udostępnij na innych stronach

W dniu 12.03.2022 o 14:48, Konrad-GM napisał:

Tego typu fog of war można też zrobić na tablicach, większe komórki i aktualizowałbyś je jedynie gdy jednostka przekroczy jej granicę. A rysować mógłbyś też tylko wycinek takiej tablicy na surface bez znaczenia ile jednostek jest na ekranie. Minus taki, że byłoby to bardziej "rozpikselowane", ale możesz spróbować wygłądzić krawędziie jakimś shaderem.

Przetestowałem to jeszcze z ciekawości i póki co mam ładny efekt i nawet mniejsze fpsy :D
Tak wygląda rysowanie mgły:

if FOG_ON{
	display_set_gui_size(camera_get_view_width(view_camera[0]), camera_get_view_height(view_camera[0]));
	var _cam_x=camera_get_view_x(view_camera[0]);
	var _cam_y=camera_get_view_y(view_camera[0]);
	

	var _pos_x=_cam_x div grid_size;
	var _pos_y=_cam_y div grid_size;
	
	var _off_x=(_pos_x * grid_size) - _cam_x;
	var _off_y=(_pos_y * grid_size) - _cam_y;
	
	draw_set_color(c_black);
	for(var _r=0;_r < cam_cell_width;_r++)
	{
		for(var _c=0;_c < cam_cell_height;_c++)
		{
			var _s=shroud_grid[# _pos_x + _r,_pos_y+_c];
			if (_s>0)
			{
				draw_set_alpha(_s)
				var _rx=(_r * grid_size)+_off_x;
				var _ry=(_c * grid_size)+_off_y;
				draw_rectangle(_rx,_ry,_rx + (grid_size-1),_ry + (grid_size-1),false)
			}
		}
	}
draw_set_alpha(1);
display_set_gui_size(1920,1080);
}

Trochę nie rozumiem z czego wynika ten spadek
EDIT: Chociaż bez rysowania też są spadki do 50 więc może nie tu tkwi problem :D

 

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

Czym jest

cam_cell_width

??


Wygląda trochę, jakbyś może rysował poza ekranem.

Nie wiem też czy ma sens ta ciągła zmiana wielkości GUI.

Odnośnik do komentarza
Udostępnij na innych stronach

Zmiana wielkości jest po to żeby dopasować rysowanie mgły do zbliżeń ekranu.

A co do drugiego:

//CREATE
grid_size=32;
shroud_grid=ds_grid_create(room_width div grid_size, room_height div grid_size);

ds_grid_clear(shroud_grid,.5);
clear_grid_size=13;

var _sow=1;
var _soh=1

if (camera_get_view_width(view_camera[0]) mod grid_size!=0) _sow++
if (camera_get_view_height(view_camera[0]) mod grid_size!=0) _soh++

cam_cell_width=(camera_get_view_width(view_camera[0]) div grid_size)+_sow;
cam_cell_height=(camera_get_view_height(view_camera[0]) div grid_size)+_soh;

monitor_list=ds_list_create();

W alarmie następuje kod który dodaje pole widzenia i czyści mgłę, myślę że ta część całość najbardziej spowalnia. Tam są skrypty do samej mgły, nie wiem na ile istotne w tym problemie.
Nie wiem tylko jak mogę to zoptymalizować:

if FOG_ON
{
	shroud_add_fog();
	var _sz=ds_list_size(monitor_list);
	
	if _sz>0
	{
		for(var _a=0; _a<_sz; _a++)
		{
			if instance_exists(monitor_list[|_a])
			{
				shroud_clear_position(monitor_list[|_a])
			}
		}
	}
}

alarm[0]=fog_trigger_time;

W sumie to gdyby ktoś chciał rzucić okiem to wstawiam link: 

https://drive.google.com/file/d/1FP5GtQOgK2pwZVDkVcdulcPKnpWg1XZ4/view?usp=sharing

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

A załączałeś profilowanie w debuggerze? Ono całkiem sporo mówi:

obraz.png

 

I teraz najśmieszniej, bo jak zmieniłem draw_rectangle na draw_sprite, to udało mi się uzyskać 60 FPS, ale to wciąż słabiutko. To by trzeba jakoś zoptymalizować, na zasadzie sprawdzania całych stref gdzie ta mgła jest, a gdzie jej nie ma (np. obszar 128x128px) i wtedy malować za jednym zamachem większe czarne prostokąty. No i nie aktualizować co klatkę wszystkich pozycji. Albo zwiększyć rozdzielczość mgły, żeby nie było aż 8000 komórek na raz na ekranie.

  • Super (+1) 1
Odnośnik do komentarza
Udostępnij na innych stronach

Właśnie najbardziej nie wiem jak sprawdzać pozycje inaczej niż co klatkę. Będą okazje że na ekranie znajdzie się na tyle jednostek że fpsy spadną. W ogóle myślicie że taki rts z setkami jednostek jak nie tysiącami licząc chłopów i inne obiekty z mgłą wojny(+ rysowaną na minimapie :P ) jest realny? A i często większą mapką niż to co mamy tu

Odnośnik do komentarza
Udostępnij na innych stronach

Godzinę temu, Adriann napisał:

W ogóle myślicie że taki rts z setkami jednostek jak nie tysiącami licząc chłopów i inne obiekty z mgłą wojny(+ rysowaną na minimapie :P ) jest realny? A i często większą mapką niż to co mamy tu

Tak szczerze powiedziawszy, bez sensownego dostępu do wielowątkowości, to byłoby to zapewne trudne do osiągnięcia - ale jakby mocno skupić się na optymalizowaniu, to może nawet w miarę sensowny klatkarz można uzyskać.

 

Kilka propozycji jak można zoptymalizować rysowanie fog of war:

  • Nie aktualizuj wszystkich jednostek, ale tylko komórki, które są zajęte przez jednostki. Do tego możesz wykorzystać "słownik", np. tworzysz loopa po liście monitor_list tak jak do tej pory, ale przy okazji zapisujesz w której to komórce jednostka rysowała swój fog of war i np. przed shroud_clear_position może sprawdzić, czy tej komórki już nie liczyłeś, taki pseudo kod:
    var dict = new Dictionary
    for unit of unit_list:
      if dict.has(key: unit.x + '_' + unit.y)):
        continue
      shroud_clear_position(unit.x, unit.y)
      dict.set(key: unit.x + '_' + unit.y, value: unit.shroud_radius)

     jeżeli każda jednostka ma własny shroud_radius, to możesz przed shroud_clear_position zapełnić nową listę ale tylko z jednostkami o największym shroud_radius na daną komórkę.

  • Dodatkowo możesz zoptymalizować samą funkcję shroud_clear_positionhttps://www.redblobgames.com/grids/circle-drawing/ polecam
  • Nie modyfikuj tablicy shroud_grid jednostkami poza ekranem, monitor_list powinien posiadać tylko jednostki, której pierścień fog of war może być widoczny.
  • Możesz spróbować też brać tylko wycinek z listy monitor_list i co kolejna klatka aktualizować tylko część jednostek - jeżeli aktualizacja tablicy będzie się działa np. 15x na sekundę to raczej nikt nawet nie zauważy zastosowanego triku.

Jeżeli jeszcze na coś wpadnę to chętnie się podzielę pomysłami.

  • Super (+1) 1
Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
 Udostępnij

  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...