Macierze obrotów w 2D a zmiana znaku sinusa


#1

Tworzę sobie macierz obrotu względem osi z, ze współrzędnych kursora i położenia “gracza”:

float px = 400;
float py = 300;
pos = glm::vec3(px, py, 0); //środek ekranu

lookAt = glm::vec3( InputMouse::Instance()->x(), InputMouse::Instance()->y(), 0 );
lookAt.y = abs( lookAt.y - 600 ); // różnica w położeniu punkty OY między ogl a glfw
	
glm::vec3 A = lookAt - pos;
glm::vec3 B = front; // przód
A = glm::normalize( A );
B = glm::normalize( B );

float cosine = glm::dot( A, B ) ;
float sine = glm::length( glm::cross( A, B ) );
float* rotationZ;

// to mnie zastanawia
if( lookAt.x < px ) {
	sine = -sine;
}

rotationZ  = new float[16]{
	cosine,	-sine,	0, 	0,
	sine,	cosine, 0,	0,
	0,	0,	1,	0,
	0,	0,	0,	1
};

info->model->rotationZ = glm::make_mat4( rotationZ );

Jak do tej pory nie rzuciło mi się w oczy, żeby ktoś zmieniał znak sinusa dla II i III ćwiarki. To tak ma być? Czy coś zrypałem po drodze? Kod działa poprawnie.


#2

tak naprawdę masz obrót w 2d nie w 3 więc łatwiej się to analizuje:

jak widać na moim świetnym rysunku
wektor jednostkowy x = [1,0]
przechodzi na [sinA, cosA]
a wektor jednostkowy y = [0,1]
przechodzi na [-cosA, sinA]
więc jeden minus się tam naturalnie przy obrocie pojawia

u ciebie są wcześniej liczone dot i cross jakichś wektorów po normalizacji
które wynikowo mają dać sin i cos kąta obrotu

cross A,B daje wektor prostopadły do A i B,
o długości abs(cos(A,B)) (* A.len * B.len ale normalizacja)
btw trochę info o znaku jest w obliczeniu bo cross A,B, vs cross B,A da odwrotny zwrot wektora
ale jak masz length to zawsze będzie > 0 więc wyliczyłeś sobie abs(cos) w ten sposób
więc połowy obrotów się tak nie da wyrazić…
wydaje się że ten if to “fixuje” dodając dodatkową logikę sprawiającą że pozostałe 180 stopni też jest dostępne przywracając znak cosinusowi,
tylko pytanie jakie są założenia do tego ifa i czy są słuszne

btw zamiast length można policzyć dot z (0,0,1) nie tracąc znaku cosinusa
ale nie wiem czy to pomoże bo trzeba przeanalizować/przetestować wszystkie ćwiartki

trzeba pamiętać że macierz może wyrażać różne przekształcenia w tym np
[-1 0]
[0, -1]
czyli lustrzane wywrócenie modelu względem punktu w środku układu :stuck_out_tongue:
więc warto testować na modelu który takie rzeczy też pokaże


#3

btw jak dla mnie jakaś zamotana ta logika w tym kodzie
masz ekran e = (800,600),
myszkę na m =(mX, mY)
gracza na środku eranu czyli g =(400, 300)

jak wyznaczysz wektor kierunku
toTarget = (m-g).Normalize();
to już wiesz na co powinien się przekształcić forward gracza
załóżmy że forward to oś X
wtedy wystarczy wstawić to w macierz
[toTarget.x, toTarget.y]
trzeba policzyć jeszcze wektor prostopadły, ponieważ jesteśmy w 3d i wektory są znormalizowane wystarczy
otherAxis = toTarget.cross(0,0,1)
i wpisać na drugiem wierszu macierzy
[otherAxis .x, otherAxis .y]
(być może otherAxis trzeba będzie odbić mnożąc go przez minus 1 albo licząc cross z 0,0,-1 na jedno wyjdzie)


#4

btw zwykle w logice typu look at podajesz też Up vector
bo jak sobie wyobrazisz że zalokowałeś forward na dany kierunek
to możesz dalej dowolnie obracać bryłą wokół tej osi
właśnie po to jest up żeby określić gdzie jest góra
i “zablokować” obrót ustalając jedną wersje
(chyba że kierunek jest dokłądnie up, wtedy się znowu psuje :slight_smile:
zdarzają się przy niektórych równaniach dziwne bugi jak patrzysz idealnie w górę)

u ciebie jest “up” domyślny Z (0,0,1)