Anmelden

Archiv verlassen und diese Seite im Standarddesign anzeigen : OpenGL - virtuelle Kamera -> toGL()


liquid
2003-06-06, 13:45:33
Moinsen,

ich habe eine virtuelle Kameraklasse in OGL gebaut (weil es unter OGL ja in dem Sinne keine Cam gibt).
Jedenfalls basiert die momentane Kamera (ich habe noch Quaternionen im Angebot, aber das ist momentan nicht relevant) auf einem Achsensystem und einem Positionsvektor.

Das Achsensystem sieht folgendermaßen aus:
front = 0|0|1
up = 0|1|0
right = 1|0|0

Der Positionsvektor is standard auf 0|0|0
Der Up-Vektor bleibt konstant auf 0|1|0, da die Kamera nicht die Möglichkeit haben soll um die Z-Achse herum zu rotieren (ähnlich der Bewegung wenn man den Kopf nach links/rechts neigt). Der right und front Vektor werden durch den Input von Maus und Tastatur geändert.
Dazu speichere ich noch einen roll und pitch Wert (X- und Y-Achsen Rotation). Ja, es werden jetzt welche sagen, dass roll und pitch falsch zugeordnet sind, aber ich habe einfach so viel Material im inet gefunden, was sich auch gegenseitig bei der Namensgebung widersprochen hat, sodass ich am Ende bei roll und pitch geblieben bin. Naja, ist ja auch egal.

Jedenfalls werden pro Frame die roll und pitch Werte verändert (durch den Input) und dann mit Sinus und Cosinus die neuen front und right Vektoren errechnet (Kreuzprodukt mit dem up-Vektor, um ein orthonormales System zu erhalten).

So, nun meine eigentliche Frage. Ich hab jetzt ja die roll und pitch Werte. Ich habe es vorher so gemacht, dass ich die bei der toGL Methode (welche die Kamera an die OGL state machine weitergibt) die Rotation mit glRotate durchgeführt habe. Ich käme dabei auf insgesamt 3 Aufrufe. Zweimal glRotate und dann noch glTranslate für die Verschiebung der Kamera.
Eine andere Methode wäre ja noch die Positionierung der Kamera über die GLU Bibliothek. Ich habe mir das mal angeguckt, da die Funktion in vielen OGL Applikationen benutzt wird, um die virtuelle Kamera richtig zu positionieren. Der Sourcecode ist ja erhältlich und was da passiert ist ja einfach nur das Zusammenbasteln einer passenden Matrix.

Ich habe mir das weiter angeguckt und rausgefunden, dass eigentlich alle Vektoren, die in dieser Matrix stecken bei mir schon fast fertig berechnet sind. Ich habe deshalb meinen toGL Code umgeändert, sodass er glMultMatrix benutzt. Sind deshalb nur noch zwei GL Aufrufe (glMultMatrix und glTranslate).
Frage, ist das jetzt besser, oder ist es schneller die Rotation über glRotate durchzuführen? Ich meine irgendwo gelesen zu haben, dass es besser wäre die normalen Funktion (ala glTranslate/Rotate) zu benutzen, als sich selbst die Rotationsmatrix zusammenzubasteln. Aber... warum GL die Matrix in mehr Schritten zusammenbasteln lassen, wenn sie ja schon fertig serviert werden kann.

Außerdem wäre es ja gar kein Problem, wenn ich jetzt noch Z-Rotation haben wollte, auch den Up-Vektor entsprechnend zu modifizieren. An der Matrix-Bastelei müsste ich da ja gar nichts ändern.
Mit glRotate käme dann ja noch ein Rotationskommando hinzu. Und dazu muss dieses noch Sinus und Cosinus durchführen, um den Winkel entsprechend zu verwenden. Das habe ich allerdings alles schon getan, warum also das doppelt durchführen lassen?

Was meint ihr? Ich bin eigentlich der Meinung, dass man das mit glMultMatrix ruhig machen sollte, schließlich hat man schon alle Werte und übersichtlicher ist es auch.

Ahja, was mit gerade einfällt. Es ist mit Sicherheit möglich (ich weiß, dass es möglich ist) die Verschiebung (glTranslate) auch in der Matrix unterzubringen (ist ja schließlich homogen, damit die Rechnung einheitlicher ist, nur Multis schließlich, bei 3x3 Matrizen bräuchte man ne Addition).
Wo genau kommt der Positionsvektor hin?
[00][04][08][12]
[01][05][09][13]
[02][06][10][14]
[03][07][11][15]

Ist doch richtig so mit den Indizes (bei DirectX wäre es transponiert)?
Also der "quadratische" Bereich von 00|02|08|10 ist ja mit den right, front und up-Vektoren "besetzt".
15 ist 1.0
Wo kommt der Pos-Vektor hin? Auf 03|07|11 oder 12|13|14 ?

Schonmal besten Dank für Antworten!!

cya
liquid

micki
2003-06-06, 13:49:47
12|13|14 bei ogl

mach ich jedenfalls :)

MfG
micki

liquid
2003-06-06, 14:51:58
Thanks micki, das werde ich gleich mal ausprobieren.

liquid
2003-06-06, 15:37:33
Hmm, mickis Vorschlag ist zwar insofern richtig, dass es in gewisser Sicher "funktioniert", aber nun rotiert die Kamera nicht um ihre eigene Achse, sondern immer um die Achse des Nullpunktes.
Ich denke das Problem ist die Reihenfolge der GL Kommandos:

normal:
glMultMatrixf(_mtx.get());
glTranslatef(-pos.getX(), -pos.getY(), -pos.getZ());

jetzt:
glMultMatrixf(_mtx.get());

Wobei bei "jetzt" die Translation mit einiger Sicherheit (solche Effekte hatte ich vorher auch schon mal) vor der Multiplikation stattfindet (also genau anders herum, als bei "normal").
Hmm, schade, einer eine Idee, wie man das entsprechend zurechtbiegen könnte?

cya
liquid

Xmas
2003-06-06, 16:15:34
Du könntest die Matrizenmultiplikation auf Papier ausführen und das Ergebnis dann "von Hand" einprogrammieren...

Ernsthaft, es bringt wirklich nichts, das Setzen der Kameramatrix optimieren zu wollen. Und die GL-Funktionen sind dabei IMO übersichtlicher, als selbst eine Matrix zusammenzubasteln.

liquid
2003-06-06, 16:21:12
Original geschrieben von Xmas
Du könntest die Matrizenmultiplikation auf Papier ausführen und das Ergebnis dann "von Hand" einprogrammieren...
Hab ich mir auch schon überlegt, ist denke ich die beste Möglichkeit. Werde ich mal durchrechnen. Kann mich ja nachher noch entscheiden, ob ich es dann einbaue oder nicht.

Original geschrieben von Xmas
Ernsthaft, es bringt wirklich nichts, das Setzen der Kameramatrix optimieren zu wollen. Und die GL-Funktionen sind dabei IMO übersichtlicher, als selbst eine Matrix zusammenzubasteln.
Macht aber mehr Spaß!! :P ;D

cya
liquid

Xmas
2003-06-06, 16:58:29
Original geschrieben von liquid
Macht aber mehr Spaß!! :P ;D
Kann ich ja absolut verstehen ;)
Aber das "Alles-selber-und-optimiert-machen-wollen" bringt einfach viele Probleme mit sich.
Stell dir vor Spiele würden heute noch ihre Transformationen selbst ausführen statt die API-Funktionen zu verwenden, weil man da ja besser "optimieren" kann - dabei kann es die Hardware noch viel effizienter.

Demirug
2003-06-06, 17:20:12
Xmas, ich glaube liquid ist einfach noch in der Phase wo man alles noch einmal selber ausprobieren will und um jeden eingesparten Takt kämpft. :)

Bei uns beiden siegt dann in solchen Fällen schon eher die natürliche Faulheit des Programmiers.

liquid
2003-06-06, 17:37:17
Ich glaube Demi versteht mich. :bier: *g*

Nasenbaer
2003-06-07, 11:46:24
Ich hatte mit der Kamera auch zu kämpfen, wenn auch unter Direct3D. Da für mich Matrizen aber noch ein Buch mit sieben Siegeln ist verwende ich gerne die API-Funktionen. Diese sind sicher auf auf die einzelnen Prozessoren optimiert und damit x-mal schneller als was selbst geschriebenes. Der Code ist zudem noch leichter lesbar, da (für mich noch) komplizierte Rechnungen nicht enthalten sind.

Andererseits ist es sicher gut zu wissen was diese Funktionen eigentlich machen und das lernt man am besten, wenn man es mal selbst geschrieben hat. In sofern muss ich da liquid jedenfalls recht geben. Wobei man in einem finalen Programm unbedingt drauf verzichten sollte. Schließt ja auch Fehler aus, die man in die eigene Routine vielleicht eingebaut hat. :)

Mfg Nasenbaer

liquid
2003-06-07, 12:47:09
Das Problem ist, dass ich spätestens bei einer auf Quaternionen basierenden Cam die "normalen" API-Funtionen nicht mehr aufrufen kann, da mir die Winkel fehlen.
Alles kann man das Quaternion sehr schnell in eine passende Matrix zerlegen, die dann mit dem Matrix-Stack gemulted wird.
Das wird einfacher sein, als durch kompilizierte Berechnungen die Achse und den Winkel aus dem Quaternion zu extrahieren. Ich denke da bringt die Matrix-Methode doch viel bei.
Zumal ich sowieso am Überlegen bin, ob ich meinen Scenegraph nicht vollständig mit Quaternionen aufbaue. Der Speicherplatz ist geringer, man führt im Vergleich zu Matrixmults weniger Rechenoperationen durch. Ich denke das bringt schon was, außer halt dem Nachteil, dass ich das Quaternion nachher für OGL in eine passende Matrixform konvertieren muss und Quaternion allgemein schlecht zu visualieren sind (wer kann soch schon eine Einheitssphäre im 4D-Raum vorstellen... :kratz: )
Naja, ich könnte ja mal einen Speedtest durchführen, welches der beiden Verfahren (Matrix, API-Funktione), denn nun besser und schneller funktioniert.
Allerdings fehlt mir noch der Kommentar von zecki, auf den ich schon so lange warte... :flöt:

cya
liquid

zeckensack
2003-06-09, 22:57:41
Original geschrieben von Xmas
Ernsthaft, es bringt wirklich nichts, das Setzen der Kameramatrix optimieren zu wollen.Full ack ;)

Aber was sehe ich da: :|
//yM x pM= ( cy sp*sy -sy*cp 0)
// ( 0 cp sp 0)
// ( sy -sp*cy cp*cy 0)
// ( 0 0 0 1)
// ( cr -sr 0 0 )
//rollMat= ( sr cr 0 0 )
// ( 0 0 1 0 )
// ( 0 0 0 1 )
void
Matrix_4x4::set_rotation(const float pitch,const float yaw,const float roll)
{
__asm {
//calculate sines and cosines for all three rotational angles
//this is obviously much more efficient than six bloated library calls
FLD DWORD PTR [DEG_TO_RAD]
FLD DWORD PTR [pitch]
FMUL ST(0),ST(1)
FSINCOS
FSTP DWORD PTR [p] //store cosine
FSTP DWORD PTR [p+4] //store sine
FLD DWORD PTR [yaw]
FMUL ST(0),ST(1)
FSINCOS
FSTP DWORD PTR [y]
FSTP DWORD PTR [y+4]
FLD DWORD PTR [roll]
FMULP ST(1),ST(0)
FSINCOS
FSTP DWORD PTR [r]
FSTP DWORD PTR [r+4]
}
//(yM x pM)x rM =( cy*cr+sp*sy*sr -cy*sr+sp*sy*cr -sy*cp 0 )
// ( cp*sr cp*cr sp 0 )
// ( sy*cr-sp*cy*sr -sy*sr-sp*cy*cr cp*cy 0 )
// ( 0 0 0 1 )
a[0]=r.cos*y.cos+r.sin*y.sin*p.sin;
a[1]=p.cos*r.sin;
a[2]=y.sin*r.cos-p.sin*y.cos*r.sin;

a[4]=p.sin*y.sin*r.sin-y.cos*r.sin;
a[5]=r.cos*p.cos;
a[6]=-y.sin*r.sin-p.sin*y.cos*r.cos;

a[8]=-y.sin*p.cos;
a[9]=p.sin;
a[10]=p.cos*y.cos;
}

Nur yaw und pitch? Haben wir auch im Angebot:
//pitch x yaw= ( cy 0 -sy 0 )
// ( sy*sp cp sp*cy 0 )
// ( cp*sy -sp cy*cp 0 )
// ( 0 0 0 1 )
void
Orientation2::set(const float pitch,const float yaw,Vector3* const move_x,Vector3* const move_z)
{
#ifndef _MATH_FOR_UTILITY_PROJECT_

if (sys_cpu_caps==_3dnow)
{
__asm {
PUSH EAX
PUSH EBX
PUSH ECX

MOV EAX,[this]
MOV EBX,[move_x] //get adress of move_x' components
MOV ECX,[move_z] //get adress of move_z's components

//calculate sines and cosines for bopth rotational angles
//this is obviously much more efficient than four bloated library calls
FLD DWORD PTR [DEG_TO_RAD]
FLD DWORD PTR [pitch]
FMUL ST(0),ST(1)
PREFETCHW [EAX] //prefetch the matrix for updating
FSINCOS
FSTP DWORD PTR [p] //store cosine
FSTP DWORD PTR [p+4] //store sine
FLD DWORD PTR [yaw]
FMULP ST(1),ST(0)
PREFETCHW [EBX] //prefetch movement vector 1
PREFETCHW [ECX] //prefetch movement vector 2
FSINCOS
FSTP DWORD PTR [y]
FSTP DWORD PTR [y+4]

FEMMS
PCMPEQD mm7,mm7
PSLLD mm7,31
MOVQ mm0,QWORD PTR [p] //mm0=(p.sin|p.cos)
MOVQ mm1,QWORD PTR [y] //mm1=(y.sin|y.cos)

//set local x-axis
MOVQ mm2,[ones]
MOVQ mm3,mm1 //mm3=(y.sin|y.cos)
PUNPCKHDQ mm2,mm0 //mm2=(p.sin| 1 )
PSRLQ mm3,32 //mm3=( 0 |y.sin)
PFMUL mm2,mm1 //mm2=(p.sin*y.sin|y.cos)
PFMUL mm3,mm0 //mm3=( 0 |p.cos*y.sin)
MOVQ [EAX],mm2
MOVQ [EAX+8],mm3
//set local y-axis
MOVQ mm5,mm0
MOVQ mm4,mm0
PXOR mm5,mm7
PSLLQ mm4,32 //mm4=(p.cos| 0 )
PSRLQ mm5,32 //mm5=( 0 |-p.sin)
MOVQ [EAX+16],mm4
MOVQ [EAX+24],mm5
//set local z-axis
MOVQ mm3,mm1
PUNPCKLDQ mm3,mm3
PFMUL mm3,mm0
MOVQ mm2,mm1
PXOR mm2,mm7
PUNPCKHDQ mm2,mm3
MOVQ [EAX+32],mm2
MOVD [EAX+40],mm3
//set new x-movement axis (strafe)
//set new z-movement axis (forward)
MOVQ mm3,mm1
PXOR mm4,mm4
MOVQ mm5,mm1
PUNPCKLDQ mm3,mm4
MOVQ mm6,mm1
MOVQ [EBX],mm3
PUNPCKHDQ mm1,mm1
PUNPCKHDQ mm5,[ones]
PXOR mm1,mm7
PFMUL mm5,mm0
PSLLQ mm7,32
MOVD [EBX+8],mm1
PXOR mm5,mm7
PFMUL mm6,mm0
MOVQ [ECX],mm5
MOVD [ECX+8],mm6

FEMMS

POP ECX
POP EBX
POP EAX
}
}
else

#endif

{
__asm {
//calculate sines and cosines for all three rotational angles
//this is obviously much more efficient than six bloated library calls
FLD DWORD PTR [DEG_TO_RAD]
FLD DWORD PTR [pitch]
FMUL ST(0),ST(1)
FSINCOS
FSTP DWORD PTR [p] //store cosine
FSTP DWORD PTR [p+4] //store sine
FLD DWORD PTR [yaw]
FMULP ST(1),ST(0)
FSINCOS
FSTP DWORD PTR [y]
FSTP DWORD PTR [y+4]
}
a[0]=y.cos;
a[1]=y.sin*p.sin;
a[2]=p.cos*y.sin;

a[4]=0;
a[5]=p.cos;
a[6]=-p.sin;

a[8]=-y.sin;
a[9]=p.sin*y.cos;
a[10]=p.cos*y.cos;

move_x->x=y.cos;
move_x->y=0.0f;
move_x->z=-y.sin;

move_z->x=y.sin*p.cos;
move_z->y=-p.sin;
move_z->z=y.cos*p.cos;
}
}

Speed-Gewinn: null, nada, nix *eg*
Aber das sollte man durchaus mal selbst versucht haben ;)

zeckensack
2003-06-09, 23:15:47
Original geschrieben von liquid
Das Problem ist, dass ich spätestens bei einer auf Quaternionen basierenden Cam die "normalen" API-Funtionen nicht mehr aufrufen kann, da mir die Winkel fehlen.
Alles kann man das Quaternion sehr schnell in eine passende Matrix zerlegen, die dann mit dem Matrix-Stack gemulted wird.Ein Quaternion brauchst du nicht in eine Matrix umzubasteln, jedenfalls nicht für OpenGL :)

glRotatef nimmt eine Achse, und einen Winkel entgegen, gell? =)
Sehen wir uns doch mal das hier an:
void
uQuaternion::from_axis_angle(const Vector3& axis,const float degrees)
{
//we need a scale to make the axis unit length
float as=1.0f/axis.length();
//and the sine and cosine of half the rotational angle
sincos aa(degrees*0.5f);
//to construct a proper unit quaternion
w=aa.cos;
x=as*aa.sin*axis.x;
y=as*aa.sin*axis.y;
z=as*aa.sin*axis.z;
}

Und jetzt das ganze umkehren ...

void
uQuaternion::extract_axis(Vector3* target)const
{
target->x=x;
target->y=y;
target->z=z;
target->normalize();
}

float
uQuaternion::extract_angle()const
{
return(2*acos(w));
}

Das ist nicht wirklich schnell, weil ein inverser Kosinus, und eine Wurzel (in normalize()) im Spiel ist. In den meisten Fällen ist das aber IMO schnurzpiepe.

Ansonsten natürlich gerne so:Rotation_matrix::Rotation_matrix(const uQuaternion& q)
{
a[0]=1.0f-2.0f*(q.y*q.y+q.z*q.z);
a[1]=2.0f*(q.x*q.y+q.w*q.z);
a[2]=2.0f*(q.x*q.z-q.w*q.y);
a[3]=0.0f;

a[4]=2.0f*(q.x*q.y-q.w*q.z);
a[5]=1.0f-2.0f*(q.x*q.x+q.z*q.z);
a[6]=2.0f*(q.y*q.z+q.w*q.x);
a[7]=0.0f;

a[8]=2.0f*(q.x*q.z+q.w*q.y);
a[9]=2.0f*(q.y*q.z-q.w*q.x);
a[10]=1.0f-2.0f*(q.x*q.x+q.y*q.y);
a[11]=0.0f;

a[12]=0.0f; a[13]=0.0f; a[14]=0.0f; a[15]=1.0f;
}Durchaus machbare weitere Optimierungen dieser Funktion sollte man IMO dem Compiler überlassen.