Lepszy Imageshack

28.01.2009 21:14 in internet

Strasznie wkurzający jest sposób w jaki Imageshack prezentuje nam obrazki (reklamy, mnóstwo Javascriptu, itd.) Można na to zaradzić. Po pierwsze, instalujemy do Firefoksa plugin Greasemonkey. Po drugie, wklepujemy mu taki skrypt:

// ==UserScript==
// @name           delimgshk
// @namespace      abw
// @description    brak
// @include        http://img*.imageshack.us/my.php?image=*
// ==/UserScript==

var xx = document.getElementById('ibl_fs_im').innerHTML;
var xx1 = xx.indexOf('http:');
var xx_a = xx.substring(xx1);
var yy = document.getElementById('page').innerHTML;
var off1 = yy.indexOf('<div class="cTab2">');
var yy_a = yy.substring(off1);
var yy_b = yy_a.substring(0, yy_a.indexOf('[/IMG][/URL]'));
var yy_c = yy_b.substring(yy_b.indexOf('[IMG]')+5);
var adres = yy_c.replace('th.', '');
document.location = adres;

Dzięki temu każdy imageshackowy link będzie prowadził bezpośrednio do obrazka. Prawda, że fajne? :)

Kod jest straszny, ale nie o jego piękno tu chodzi. ;)

Wygrałem w Software Competition

23.01.2009 21:48 in życie

laptop.jpg

W końcu pojawiły się wyniki Software Competition organizowanego przez Samsung R&D i BEST. Nieskromnie mogę pochwalić się, że zająłem pierwsze miejsce. :)

Z tego powodu wzbogaciłem się o (kolejnego do kolekcji) laptopa.

Konkurs był dwuetapowy. Pierwszy polegał na napisaniu testu z kruczków C++, a drugi na zaimplementowaniu wirtualnej maszyny o podanej specyfikacji. W sumie nic trudnego, a opłaciło się wziąć udział. :)

Piknik na skraju drogi

22.01.2009 21:08 in książki, recenzje

_bc242.jpg

Postanowiłem przeczytać książkę, której fabuła była inspiracją gry S.T.A.L.K.E.R. Piknik na skraju drogi Arkadija i Borysa Strugackich okazał się znakomitą lekturą science-fiction.

Książka opowiada o mieszkańcach miasteczka leżącego nieopodal Strefy. Strefa to zamknięty obszar pełny niewytłumaczalnych zjawisk i przedmiotów, które pojawiły się w nim po krótkotrwałym pobycie obcej cywilizacji na Ziemi. Tyle tytułem wstępu. Co jest w Pikniku fajne? Przede wszystkim koncepcja, że przybysze z kosmosu w żaden sposób nie nawiązali kontaktu z nami. Wpadli, zostawili po sobie trochę śmieci - i tyle.

Drugim ogromnym walorem książki jest styl, w jakim została napisana. Autorzy opowiadają historię bez zbędnego patosu, przedstawiając świat z perspektywy głównego bohatera w bardzo subiektywny sposób, nie szczędząc typowo słowiańskiego humoru. W zasadzie Piknik to prawie że S-F obyczajowe i podobnie jak np. Limes inferior pokazuje nam świat z perspektywy codziennego dnia mniej lub (w przypadku Pikniku) bardziej przeciętnego człowieka. Takie S-F lubię.

Na koniec muszę dodać, że po lekturze tej książki podchodzę do gry S.T.A.L.K.E.R. w trochę inny sposób, darząc okolice Zony i jej anomalie z dużo większym respektem. Spróbujcie sami - przechadzka po Czarnobylu smakuje zupełnie inaczej, kiedy powtarzamy sobie w duchu ,,dobra, omińmy tę <<łysicę>> po lewej'' i uważamy, żeby nie wpaść w ,,<<czarci pudding>>''. Polecam.

dot: Kropka i tyle?

17.01.2009 16:24 in grafika 3D, programowanie, porady

Z instrukcji dot korzystamy przy liczeniu oświetlenia w shaderze. Przykładowo:

color = texture * dot(N, L);

Nie wszyscy jednak są świadomi, że dot można użyć w zupełnie innych celach. Najpierw zobaczmy czym ta instrukcja jest (za Cg Reference Manual):

float dot(float4 a, float4 b)
{
  return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
}

Do czego jeszcze użyć iloczynu skalarnego (bo tak się właśnie nazywa ta operacja)? Np. do konwersji kolorów:

float4 pixel(float4 c : COLOR) : COLOR
{
float bw = c.r * 0.59 + c.g * 0.3 + c.b * 0.11;
return float4(bw, bw, bw, 1);
}

Wersja z prostym filtrem RGB -> BW potrzebuje siedmu instrukcji Pixel Shader 2.0:

def c0, 0.30000000, 0.58999997, 0.11000000, 1.00000000
dcl v0.xyz
mul r0.x, v0.y, c0
mad r0.x, v0, c0.y, r0
mov r0.w, c0
mad r0.xyz, v0.z, c0.z, r0.x
mov oC0, r0

Wersja z dot:

float4 pixel2(float4 c : COLOR) : COLOR
{
float bw = dot(c.rgb, float3(0.59, 0.3, 0.11));
return float4(bw, bw, bw, 1);
}
def c0, 0.58999997, 0.30000000, 0.11000000, 1.00000000
dcl v0.xyz
mov r0.w, c0
dp3 r0.xyz, v0, c0
mov oC0, r0

I co? Dwie instrukcje mniej! Niby niewiele, ale dla filtru nałożonego na obraz HD to ponad 4 mln taktów shadera mniej (bez AA). Dlatego warto pamiętać o takiej drobnej optymalizacji, zwłaszcza, że kompilator Cg (notabene bardzo inteligentny) nie był w stanie uprościć pierwszego shadera do drugiego nawet dla stałego wektora (0.59, 0.3, 0.11).

Budujemy silnik: shadery kontratakują

16.01.2009 00:24 in silnik, grafika 3D, programowanie, shadery

Ha! Jestem z siebie dumny. Po 10 dniach klepania kodu mój prototypowy kompilator shaderów działa i ma się dobrze.

prog1.jpg

Jak pisałem w poprzednich odcinkach, jest kilka koncepcji co do shaderów. Okazało się, że #ifdefowe shadery substraktywne nie są na tyle elastyczne, by podołać rosnącemu skomplikowaniu silnika. Napisałem więc kompilator który buduje PS i VS na podstawie schematu i parametrów. Wygląda to mniej więcej tak: kod shadera.

Język zawiera kilka elementów.

  • zmienne - jest ich kilka typów. Generalnie można podzielić je na następujące typy:
    • zmienne stream czyli pochodzące bezpośrednio z danych o modelu (jak pozycja wierzchołka czy normalna w model space)
    • zmienne varying obliczane w VS i przekazywane do PS (jak np. pozycja w world space; dzielą się na zmienne globalne dla obiektu i zmienne per-light jak np. pozycja w shadowmap space
    • zmienne uniform przekazywane jako parametry do PS/VS, również dzielą się na parametry per-object (lub też per-material) oraz per-light
    ze zmiennymi jest trochę zabawy:
    • silnik musi być kompatybliny z kompilatorem i wiedzieć jakie dokładnie parametry będą potrzebne dla danego shadera, jaki będzie ich typ, itd.
    • pojawia się problem ograniczonej ilości interpolatorów między VS a PS (konkretnie mamy do dyspozycji 8 texcoordów). Pozycja i texcoord to dwa. Normalna i tangent? Cztery. Jeżeli dla światła chcielibyśmy przekazać jego pozycję w tangent space to konieczne będą następne 2. Współrzędne dla shadowmapy? Razem mamy już 7 argumentów. Jeżeli chcemy mieć 1 pass, to obliczenia trzeba przenieść niestety do world space. Wtedy możemy mieć do 4 cieni na obiekt.
  • parametry - przekazujemy je do kompilatora również na kilku poziomach (scene, material, light)
  • funckje - serce języka

Zobaczmy przykładowy kod:

[Require(Material.ColorSampler, TextureCoord, Material.AlphaTestValue)]
Null AlphaTest[Material.AlphaTestMode = TRUE]
{
	!let %texture Texture
	if (%texture.a < Material.AlphaTestValue)
	{
		discard;
	}
}

Funkcja ta zgłasza zapotrzebowanie na sampler tekstury, koordynaty dla niej i wartość do testu alfa. Sampler i wartość dla testu są uniform, dlatego zostaną wystawione jako zewnętrzne parametry (i przekazane przez silnik), natomiast TextureCoord jest varying per-object, dlatego zostanie uruchomiony odpowiedni podprogram VS:

[Requires(Stream.Texcoord)]
[Provides(TextureCoord)]
{
	float2 %coord = Stream.Texcoord;
	$ %coord
}

OK, dalej mamy instrukcję !let. Jest to statyczne przypisane wartości funkcji. Potęga tego mechanizmu polega na wybraniu odpowiedniej wartości funkcji na podstawie parametrów. Texture na przykład, może być samplowane w sposób prosty lub używając splattingu. Instruckja !let wybierze odpowiednią, i co więcej - jeżeli gdzieś będziemy mieli np. taką instrukcję:

!let %ambient Ambient
!color %color
!add %color %ambient

to w przypadku gdy Ambient jest statycznym nullem, żadne dodawanie nie zostanie wykonane. Szkoda w końcu tracić czas na dodawanie niczego ;).

Zobaczmy jak wygląda przykładowy wynik działania kompilatora (przykładowy, większość "prawdziwych" funkcji graficznych nie jest zaimplementowana albo jest uproszczona) dla następujących parametrów:

Compile{Fragment=fp40,Vertex=vp40}Technique{DRAW}Lights{2}Object{}Material{AlphaTestMode=TRUE}Light0{}Light1{LightType=POINT,ShadowType=SHADOWMAPPING}

Co jeszcze można dodać? Język jest supersetem Cg, więc w razie potrzeby można używać dowolnych features z Cg. Jedynie deklarowanie zmiennych jest nieco opakowane (float3 %zmienna) w celach bezpieczeństwa (unikanie 2 takich samych nazw zmiennych), ale jeżeli mamy pewność że nasza zmienna nie pogryzie się z żadną inną do funkcje można pisać w "czystym" Cg. I warto dodać że całość mieści się w wielu plikach, więc można to dosyć logicznie zorganizować (osobno funkcje/zmienne związanie z shadowmapami, oddzielnie liczenie attenuation, itd.)

Aha, jeszcze słowo o założeniach. Mam zamiar użyć techniki którą roboczo nazywam At-once-rendering, czyli renderowanie geometrii w 1 przebiegu (+ pomocniczy przebieg Z-fill). Wydajnośc powinna być wyższa niż w przypadku deferred (fillrate!), choć koszta przełączania shaderów mogą dać o sobie znać. Okaże się w praktyce.

Nie wiem jaki to jest rodzaj shaderów (zapewne nadal substraktywny), ale mam nadzieję, że da radę. Zabieram się za integrację z silnikiem. :)

Fallout 3

03.01.2009 20:56 in gry, recenzje

Wojna nigdy się nie zmienia. Ale Fallout i owszem. Moje odczucia po przejściu gry (oznaczenia przy komentarzach W - Wiedźmin, S - Stalker):

thumbs-up.jpg
  • Silnik użyty w grze spełnia swoje zadanie znakomicie. Świat doczytuje się w tle, króciutkie wczytywanie ma miejsce tylko przy wchodzeniu do budynków (nie to, co w W). Na GF 7300 da się płynnie grać przy całkiem niezłych efektach.
  • Fizyka dobrze spełnia swoją rolę (nie jest w tej grze najważniejsza, ale nie została też doczepiona ,,na siłę'' jak w W).
  • Graficznie gra jest ładna, ale dosyć nudna. Co prawda w postatomowym świecie ciężko o urozmaicone krajobrazy, ale chodząc kilka godzin po metrze człowiek jest w stanie z pamięci odtworzyć wszystkie tekstury. Także ilość modeli postaci nie jest szczególnie ogromna, ale ponieważ świat nie jest zbyt gęsto zaludniony, nie przeszkadza to zbytnio. Ciekawym dodatkiem jest możliwość ustawienia wyglądu swojej postaci w edytorze rodem z The Sims. Bonus: ojciec będzie podobny do postaci gracza. Nice.
  • Fabularnie gra jest dosyć przeciętna. Z jednej strony mamy ,,sandbox'', czyli swobodne chodzenie po mapie, a z drugiej totalnie liniowy główny quest. Efekt jest taki, że przez pierwszą połowę gry zrobiłem wszystkie zadania poboczne, a w drugiej zostało mi już tylko to główne...
  • Niczego dobrego nie da się powiedzieć o walce i sztucznej inteligencji wrogów i przyjaciół. Walka w trybie V.A.T.S. jest kompletnym nieporozumieniem. Procentowa szansa trafienia nie ma żadnego (kompletnie żadnego!) wpływu na skuteczność ataku. Przykład: 95%, strzelba, 3 metry od wroga - 4 strzały, 4 pudła. 2%, karabin, tak-daleko-że-aż-go-nie-widać od wroga - 3 strzały, 3 trafienia, w tym jeden krytyk. Jednocześnie walka w trybie V.A.T.S. nie ma żadnego związku z walką real-time: można strzelić 5/6 razy w V.A.T.S., potem strzelać tak długo aż odnowią nam się punkty akcji, a potem znowu celować. Sztuczna inteligencja natomiast zupełnie nie istnieje. Wrogowie potrafią poruszać się w losowych kierunkach i nic ponadto. Podobnie nasi przyjaciele - chociaż ci potrafią jeszcze ,,zablokować'' się na jakiejś ścianie lub przeszkodzie.
  • Gra ma mnóstwo bugów i niedoróbek. Przykład? W domu w Megatonie mamy stojak na figurki, które można(by) zbierać w czasie gry. Poza jedną, którą dostajemy od ojca, w czasie gry nie uświadczymy żadnej innej (i nie wynika to z przechodzenia w trybie speed gaming -- mam zwyczaj sprawdzać każde pomieszczenie niemalże piksel po pikselu). Kilka razy zdarzyło się też, że kluczowy NPC zablokował się/ginął w czasie drogi i trzeba było wczytywać grę, by pchnąć questa naprzód.
  • Możemy podróżować w drużynie, ale kompana znajdujemy dopiero pod sam koniec gry. Może to i dobrze, bo z jego sztuczną inteligencją (dzikie szarże młotem na 5 supermutantów z ciężką bronią) ciężko utrzymać go przy życiu.
  • Dialogi są niestety totalnym dnem. Większość można podsumować jako ,,-Siema! -Co tam? - Spoko... - No to nara!''. Rozmowa z jedną postacią prawie nigdy nie wpływa na inne postaci (poza kluczowymi questami).
  • Tło świata byłoby całkiem fajne, ale niestety nie zostało dostatecznie rozwinięte. Przez tło rozumiem na przykład zapisy ludzi, którzy obserwowali wybuchy atomowe i próbowali się ukryć, itd. Niestety wartość tego typu ciekawostek zostaje spłycona przez szkielety walające się dosłownie wszędzie (graficy chyba zrobili konkurs na najśmiejszniejszą pozę szkieletu). Apokalipsa apokalipsą, ale przez 200 lat można było to posprzątać, no nie? Dodatkowo elementy takie jak promieniowanie zostały zrobione na ,,odwal się''. Promieniowanie w F, a to w S to niestety zupełnie inna liga.
  • Gra jest niestety, podobnie do W bardzo prosta pod względem RPG. Rozgrywka sprowadza się właściwie do wykonywania kolejnych punktów z naszego notatnika. Jedynym utrudnieniem w ułatwieniu są totalnie nieczytelne mapy, nie uwzględniające kilku kondygnacji w budynkach.
  • Dostaliśmy 3 wersje językowe: polską, polską kinową i angielską. Niestety w każdej z nich filmy są z polskim dubbingiem, co można by przeżyć. Dużo gorzej wygląda sprawa patchów: te ,,oficjalne'' nie pasują niestety do ,,naszej'' angielskiej wersji, więc szanse na posiadanie aktualnego patcha są bardzo niskie (zwłaszcza, że za lokalizację odpowiada znana z Far Cry'owego ,,Mam twój numer!'' Cenega). A co z dodatkami, co z edytorem?

Tak mniej więcej prezentują się moje wrażenia po pierwszym przejściu gry. Jak będę miał trochę wolnego czasu to pewnie zrobię drugie podejście, ale do spatchowanej prawdziwej angielskiej wersji. Ogólnie grę, mimo że technologicznie lepszą od W i mimo wszystko fajną*, oceniam trochę niżej - dam jakieś 5+/8.

*. Tak, gra jest dużo fajniejsza niż wynika z lektury powyższych punktów. Po prostu lubię się czepiać. :)

Budujemy edytor: wymiana informacji

02.01.2009 01:04 in silnik, grafika 3D, programowanie, edytor

Jak można wygodnie wymieniać informacje między silnikiem a edytorem? Ja uznałem, że najwygodniej będzie wymieniać się strukturami danych odpowiedni interpretowanymi przez silnik. W komunikacji socketowej wygląda to tak:

GFX MATERIALLIST
OK road car sky ...
GFX MATERIAL road
OK 0 0 1 0 1 0 0 0 0 2 ...

Doszedłem do wniosku, że zamiast implementować jakiś wyszukany (zwłaszcza po stronie C++, czyli silnika) system refleksji, najprościej będzie stworzyć generator kodu na podstawie struktury. Tak więc, na podstawie struktury materiału (po lewej) generowany jest:

  • formularz do edycji struktury (czemu nie użyłem PropertyGrid? sam nie wiem, ale mam wrażenie, że moje rozwiązanie jest bardziej ,,przyjazne'' dla docelowego użytkownika [grafik, map designer])
  • kod importujący/eksportujący strukturę zarówno po stronie silnika jak i edytora

mat1.png mat2.png

Słowem - prawie jak automatyzacja. I prawie jak LINQ. ;)