Kiedy zaczynamy uczyć się programowania, dowiadujemy się, że zmiennym przypisane są zawsze konkretne typy, których należy się trzymać. Trochę później, gdy zajmujemy się już shaderami i programowalnym potokiem graficznym, okazuje się, że większość wartości jest tam tego samego typu (wektor trzech lub czterech float
ów). Różnią się one jednak przypisaną im semantyką, czyli znaczeniem w opisie wierzchołka lub piksela (POSITION
, NORMAL
, COLOR
, i tak dalej).
Jednak sama semantyka nie jest jedyną cechą charakterystyczną danego wektora. Nawet mając do czynienia z dwoma wektorami “geometrycznymi” (czyli opisującymi pozycję lub normalną, a nie np. kolor), nie zawsze możemy wykonywać na nich łączne operacje. Należy bowiem zadbać o to, by wektory te znajdowały się w tej samej przestrzeni. W przeciwnym razie konieczne jest odpowiednie przekształcenie, co sprowadza się do pomnożenia przez jakąś macierz.
Aby łatwiej zorientować się, z którą przestrzenią mamy do czynienia, można stosować odpowiednie nazewnictwo, na przykład w postaci sufiksów. Przyrostki te mogą być przyporządkowane choćby następująco:
_o
(np. vPos_o
) niech odpowiada przestrzeni obiektu (object space), związanej z układem lokalnym konkretnej instancji modelu_w
(np. vPos_w
) może oznaczać globalną przestrzeń świata (world space)_v
to z kolei przestrzeń widoku (view space), związana z pozycją obserwatora_t
możemy przyporządkować do tangent space przy obliczeniach związanych z oświetleniem_l
może w końcu być przyrostkiem wektorów w przestrzeni związanej z pozycją konkretnego światłaOczywiście cały ten pomysł na kilometr śmierdzi sławetną notacją węgierską, jednak w tym przypadku “dekorowanie” nazw zmiennych ma znacznie głębszy sens. Błędów spowodowanych operacjami na wektorach z różnych przestrzeni nie wykryje nam bowiem żaden kompilator, a ich samodzielne wyśledzenie bywa bardzo trudne (podobnie jak większości innych błędów matematycznych). Dlatego należy zawsze zwracać baczną uwagę na to, z jaką przestrzenią aktualnie pracujemy, i stosować jeśli nie specjalne nazewnictwo, to chociaż szczegółowe i jednoznaczne komentarze.
“Błędów spowodowanych operacjami na wektorach z różnych przestrzeni nie wykryje nam bowiem żaden kompilator…”
Myślę, że mogę zaproponować pewne rozwiązanie (nasuwa mi się na myśl słowo “overkill”…). Można zaprząc do pracy mechanizm kontroli typów i template’y – włączyć do typu wektora składową mówiącą o przestrzeni, zablokować wszystkie rzutowania oprócz explicit, wszystkie operatory i funkcje deklarować szablonowo. Jest w boost przykład wykorzystania takiego mechanizmu do rozwiązania podobnego problemu: rozróżniania jednostek fizycznych (kg, m/s etc).
Jak już mówiłem… overkill :)
A to wszystko w (bądź co bądź dość ogranicznym) Cg/HLSL/GLSL? :)
Liosan: Tak, myślałem o czymś podobnym, chociaż w twoim rozwiązaniu nawet nie trzeba dodatkowej składowej (wystarczy zrobić enum z możliwymi przestrzeniami i szablon, który ten enum przyjmuje jako parametr. Jak jednak słusznie zauważyłeś, to całe mnóstwo roboty. I jak słusznie zauważył Charibo, nieszczególnie ta się to zrobić w Cg/itd. ;]
Z tymi przyrostkami to świetny pomysł!