Doznałem wczoraj oświecenia jak usunąć artefakt z symulatora czarnej dziury.
Problem
W obecnym symulatorze czarnej dziury istnieje jeden brzydki aspekt generowanego obrazu. Dokładnie 90 stopni od kierunku do czarnej dziury powstaje artefakt graficzny - rozmazany pasek błędnie obliczanych pikseli (patrz obrazek po prawej). Powód tkwi zaszyty głęboko w mechanizmie generowania obrazu.
W skrócie wygląda to tak - nie da się w rozsądnym czasie obliczyć koloru każdego piksela przez raytracing. Korzystając zatem z symetrii czarnej dziury Schwarzschilda, stworzyłem sobie gotową tabelę zakrzywień promieni. Dzięki symetrii, każdy promień mogę opisać jednym parametrem - w uproszczeniu, tym, w jakiej odległości mija on czarną dziurę w najbliższym punkcie toru (dokładniej: parametrem zderzenia). Do odchylenia potrzebuję jeszcze drugiego parametru, a mianowicie odległości od czarnej dziury, z której go wysyłam/odbieram - im większą część pełnej trasy promień ma do przebycia, tym większe odchylenie.
Tak stabelaryzowane dane trafiały do karty graficznej. Później, w trakcie rysowania, każdy piksel był przeliczany na kierunek wysyłania promienia, a ten na parametr zderzenia. Odległość była znana niezależnie od tego. Odpowiednie odchylenie było odczytywane z tabeli i właściwy kolor nadawany pikselowi.
W teorii wszystko jest fajnie, ale pojawił się jeden problem - parametry zderzenia promieni wysłanych w różne kierunki bliskie 90 stopni od czarnej dziury są prawie identyczne. To daje niemal identyczne wartości odchylenia, co przekłada się na ten sam kolor piksela. Powstaje brzydki pasek.
Rozwiązanie
Cały problem bierze się z zastosowania parametru zderzenia do opisu promienia. To bardzo ułatwia generowanie tabeli odchyleń, ale jak widać, powoduje duże problemy. Co więc, gdyby z tego zrezygnować? Spróbujmy opisać każdy promień odległością od czarnej dziury i kątem między jego kierunkiem a kierunkiem do czarnej dziury.
Po pierwsze, potrzebujemy sposobu, by mając dany kąt, wygenerować wektor czteroprędkości promienia. Najwygodniej zrobić to, mając dwa jednostkowe, ortogonalne wektory przestrzenne (którymi wygenerujemy punkty na okręgu) i jeden wektor czasowy, który dodamy w odpowiedniej proporcji, żeby otrzymać wektor zerowy (wektory zerowe opisują promienie światła).
Aby móc opisywać również wnętrze czarnej dziury, symulator pracuje we współrzędnych Eddingtona-Finkelsteina (w literaturze chyba to, co ja oznaczam przez , oznacza się - zostanę przy , gdyż już wszędzie stosuję to oznaczenie i próba zmiany będzie powodować pomyłki). W tych współrzędnych jest tym samym wektorem, co we współrzędnych Schwarzschilda, natomiast jest wektorem zerowym skierowanym w przeszłość.
Skierowanie w przeszłość to dokładnie to, czego szukam - ponieważ w rzeczywistości obserwator nie wysyła promieni, a je odbiera. Obliczenia zaczynam jednak od oczu obserwatora, więc będę chciał śledzić tory promieni wstecz w czasie. Chcę mieć zatem początkowy kierunek skierowany w przeszłość.
jest więc świetnym kandydatem na jeden z naszych parametryzowanych promieni - będę chciał go otrzymać dla kąta ( będzie kątem od kierunku w stronę czarnej dziury). Zastanówmy się, jaki promień byłby dobry dla kierunku .
Na razie będziemy rozważać tylko kierunek do czarnej dziury i czas, co oznacza, że nie mieszamy ani . To zostawia nas z .
Byłoby dobrze, żeby wektor ten był "znormalizowany": .
No i, oczywiście, ma to być wektor zerowy: .
We współrzędnych E-F, mamy , , . To daje:
Rozwiązanie:
Stąd mamy .
Mając dwa wektory zerowe, do i od czarnej dziury, łatwo znaleźć wektor czasowy - wystarczy je zsumować i znormalizować wynik. To daje:
Teraz, jeden z wektorów przestrzennych będzie kombinacją i , skąd otrzymujemy:
Drugi będzie po prostu w kierunku :
Mamy teraz kompletną, ortonormalną bazę dla promieni. Promień wysłany w kierunku pod kątem do czarnej dziury będzie miał czteroprędkość:
(przy jest minus, aby oznaczało kierunek do czarnej dziury, a nie od).
Ponieważ nasze wektory przestrzenne są ortogonalne do , jest czteroprędkością obserwatora, względem którego wytyczamy okrąg. Okazuje się, że ten obserwator nie spoczywa względem czarnej dziury, co oznacza, że kąta nie będzie można przełożyć bezpośrednio na piksel tła, ale to na szczęście nie problem. Kąt będzie potrzebny jedynie jako parametryzacja tabeli, ostateczne miejsce w które trafi promień będzie odczytywanie ze współrzędnej promienia, a ta już jest odpowiednio rozłożona w układzie spoczywającym. Drobne problemy mogą się pojawić przy dobieraniu odpowiednich kątów dla pozycji w tabeli, ale ten problem prawdopodobnie rozwiążę inaczej.
Algorytm obliczania piksela będzie wobec tego wyglądał tak: dla każdego piksela, oblicz kierunek promienia; kierunek promienia przelicz na kąt (dzięki ); odczytaj wartość odchylenia dla danych i ; znajdź właściwy piksel.
Pozostała kwestia, czym właściwie mają być wartości w tabeli. Jeśli chcę móc rysować czarną dziurę również z dużych odległości, będę potrzebował czegoś, co daje się ładnie ekstrapolować lub obliczyć w przybliżeniu, gdy obserwator jest daleko - nie wygeneruję tabeli dla nieskończonego zasięgu. Dlatego dobrze byłoby przechowywać odchylenia od kąta, który uzyskalibyśmy w płaskiej czasoprzestrzeni.
Końcowy kąt , który otrzymalibyśmy w płaskiej czasoprzestrzeni, łatwo znaleźć: trzeba znaleźć wektor przy , znaleźć składowe w kierunkach i (pamiętając, że i ) w zwykłych współrzędnych sferycznych i stąd obliczyć . Stąd otrzymamy:
Zakładając, że obserwator znajduje się w i jest w kierunku czarnej dziury, będzie dokładnie końcową współrzędną promienia przy braku odchyleń. Możemy to odjąć od współrzędnej znalezionej raytracingiem i w ten sposób otrzymać odchylenie od płaskiego przypadku, które w dużych odległościach powinno zmierzać do ( będące odległością promienia od czarnej dziury w najbliższym punkcie toru, związaną z parametrem zderzenia przez ).
Dla dalszego usprawnienia obliczania, można spróbować dla każdej odległości od czarnej dziury zależność odchylenia od kąta przybliżyć analitycznie. To jest kwestia, którą zamierzam poddać dalszym badaniom. Wówczas pozostanie mi stabelaryzowanie współczynników występujących w analitycznej funkcji i interpolowanie ich między predefiniowanymi odległościami. Jeśli to się uda, będzie to chyba najlepszy sposób. Jedno, o czym trzeba będzie pamiętać, to że dla pewnego zakresu kątów nie będzie sensownych wartości odchyleń - a to przez fakt, że promienie wysłane w niektóre kąty będą trafiały w czarną dziurę. Graniczny parametr zderzenia będzie dany przez (promień sfery fotonowej), co da . Przeliczenie tego na graniczne będzie już stosunkowo proste.
Mój plan na najbliższe dni jest zatem taki, aby spróbować wygenerować wartości odchyleń dla różnych , wyrysować wykresy i sprawdzić, czy da się dobrać jakąś sensowną formę funkcji, która to przybliży. Opiszę oczywiście postępy tutaj :)
Do następnego razu!