2016-10-17 13 views
0

背景のOpenGL - ローテーションが

を働いていない私は現在、後方、私は(私がどこまで参考のために)いくつかの形をロードする作成し、コントロールが前方に移動するために取得しようとしていた環境を持っています私が探しているところで回転する。

私は、前方視点と後方視点を私の視点とカメラ位置点を使って計算しました。私は360度回転しようとしていますが、私がこれまでに持っているものに基づいていくつかの奇妙な制限があるように見えます。

回転させるために、位置(x、y、z)を同じに保ちながら変数(x、y、z)を変更します。

通報

、Iが現れ回転方向を変える2つの反射点を打つ連続角度を増加させます。何らかの理由で、これらは以下に示すように60度と300度で起こるように見える:

enter image description here

明らかにこれらの角度が正しくありません。私は、回転が発生し、計算されたデカルト座標が正しいように見えるが、角度の表示はオフになるべきである動作を評価した。

マイsetupviewportサブ:

Private Sub SetupViewport() 
    Dim w As Integer = GLcontrol1.Width 
    Dim h As Integer = GLcontrol1.Height 

    Dim perspective1 As Matrix4 = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3F, GLcontrol1.Width/CSng(GLcontrol1.Height), 0.1F, 2000.0F) 


    GL.MatrixMode(MatrixMode.Projection) 
    GL.LoadIdentity() 
    GL.Ortho(0, w, h, 0, -1, 1) 
    GL.LoadMatrix(perspective1) 
    GL.MatrixMode(MatrixMode.Modelview) 
    GL.LoadIdentity() 
    GL.Viewport(0, 0, w, h) 
    GL.Enable(EnableCap.DepthTest) 
    GL.DepthFunc(DepthFunction.Less) 

End Sub 

マイカメラクラス:

Class Camera 
Public Position As Vector3 = Vector3.Zero 
Public Orientation As New Vector3(0.0F, 0.0F, 0.0F) 
Public MoveSpeed As Single = 0.2F 
Public MouseSensitivity As Single = 0.01F 
Public lookat As New Vector3() 

Public manual_lookat As Boolean = False 

Public invert_y As Boolean = False 

Public Function aim_at_origin() 
    Position.X = 0 
    Position.Y = 0 
    Position.Z = 2 

    If invert_y = False Then 
     Return Matrix4.LookAt(Position, Position + lookat, Vector3.UnitY) 
    Else 
     Return Matrix4.LookAt(Position, Position + lookat, -Vector3.UnitY) 
    End If 
End Function 




Public Function GetViewMatrix() As Matrix4 


    If invert_y = False Then 
     Return Matrix4.LookAt(Position, lookat, Vector3.UnitY) 
    Else 
     Return Matrix4.LookAt(Position, lookat, -Vector3.UnitY) 
    End If 

End Function 

End Class 

カメラクラスは現在のものに対して乗算する行列を確立します。乗算は、setupviewportが呼び出されるたびにフレームごとに発生します。

反射点が300度と60度の理由は分かりません。私には180度か360度が理にかなっています。回転領域は視覚的に見て合計45度のように見えます。

これは、MATH、C#、およびVB .NETでタグ付けしています。ほとんどのプログラミング言語で回答が受け入れられるためです。

回転させるために、私はこのクラスを呼び出す:

Private Sub rotate_view(ByVal delta_camanglex As Single, ByVal delta_camangley As Single) 
    Dim curdistance As Single = 1 
    curdistance = Math.Sqrt((cam.Position.X - cam.lookat.X)^2 + (cam.Position.Y - cam.lookat.Y)^2 + (cam.Position.Z - cam.lookat.Z)^2) 

    Dim invertx As Boolean = False 
    Dim inverty As Boolean = False 

    camanglex = camanglex + delta_camanglex 
    camangley = camangley + delta_camangley 

    If camanglex >= 360 Then 
     camanglex = camanglex - 360 
    End If 

    If camangley >= 360 Then 
     camangley = camangley - 360 
    End If 


    If camanglex < 0 Then 
     camanglex = camanglex + 360 
    End If 

    If camangley < 0 Then 
     camangley = camangley + 360 
    End If 

    cam.manual_lookat = True 

    Dim sigma As Single = camanglex 
    Dim theda As Single = camangley 

    lookatx = curdistance * Sin(sigma * (PI/180)) * Cos((theda) * (PI/180)) 



    lookaty = curdistance * Sin((sigma) * (PI/180)) * Sin((theda) * (PI/180)) 

    lookatz = curdistance * Cos((sigma) * (PI/180)) 

    cam.lookat.X = lookatx 
    cam.lookat.Y = lookaty 
    cam.lookat.Z = lookatz 

End Sub 
+2

。そのコードのどこであなたのローテーションが起こるはずですか?なぜあなたはビュー行列を投影行列スタックに入れていますか?なぜあなたはその順序でビューと投影を掛けていますか?その「glOrtho」はそこで何をしていますか? – derhass

+0

回転させるために、位置(x、y、z)を同じに保ちながら、変数lookat(x、y、z)を変更します。 –

+0

しかし、どうやってそれを変えますか?それは全体の問題の重要なポイントです。 – derhass

答えて

1

彼らはあなたが得たもののような多くの問題を持っているとして、オイラーはこのために角度は使用しないでください。代わりに、累積変換行列を使用します。この質問は何度も何度も尋ねられるようです。だから私は、純粋にそれを行う方法の例を作ることに決めたOpenGL 1.0いいえGLMか面白いもの。

  1. 定義

    objとカメラeyeと呼ばれるプレイヤーコントロール可能なオブジェクトを持っています。それらの各々は、別個の4x4変換マトリックスによって表されるべきである。 OpenGLはそれらを1D配列として格納します。詳細情報については当社がカメラビューに依存しない、そのローカル座標系でobjを制御したい

    参照してください。 GL_MODELVIEWでカメラとオブジェクトの両方の行列を掛け合わせて、GL_PROJECTION行列乱用を避ける場合は、通常の方法でglRotate/glTranslateの呼び出しで解決できないことをすぐに認識します。

    多くの人がこれを簡単に扱うことができるオイラー角に切り替えるが、他の多くの問題を引き起こしている(今日のゲームの多くは、今もそうでない場所で使用しているが、たくさんのバグや問題があるそれ)。

    だからあなたのプロジェクトにこれを追加します。GLを使用して

    GLfloat mobj[16],meye[16]; 
    
  2. たちの行列に対して

    これは簡単ですちょうどこの操作を行います。私たちはGLを使用することができます。これにより

    glMatrixMode(GL_MODELVIEW); // set matrix target we want to work with does not matter really which) 
    glPushMatrix(); // store original matrix so we do not mess something up 
    glLoadMatrixf(mobj); // load our matrix into GL 
    //here do your stuff like glRotatef(10.0,0.0,1.0,0.0); 
    glGetFloatv(GL_MODELVIEW_MATRIX,mobj); // get our updated matrix from GL 
    glPopMatrix(); // restore original state 
    

    をレンダリングループの外で行列を呼び出す。 (たとえば、キーボードハンドラまたは一部のタイマーで)。

  3. レンダリングループ行列

    は今、私たちが行列を持つ私たちのオブジェクトをレンダリングしたいならば、我々は適切GL行列を設定する必要があります。 Matrixviewが問題になっている場合、投影行列が設定されているとします。モデルビュー行列は次のようになります。

    GL_MODELVIEW = Inverse(meye) * mobj 
    

    しかしのOpenGLは、任意の行列の逆の機能を持っていません。だから私たちがコード化する必要があるのはこれだけです。行列は常に4x4なので、それほど難しくありません。

私はこの単純なGL/C++/VCL例に一緒にこのすべてを置く:

//--------------------------------------------------------------------------- 
#include <vcl.h>  // you can ignore this 
#include <gl/gl.h> 
#include <gl/glu.h> 
#pragma hdrstop   // you can ignore this 
#include "Unit1.h"  // you can ignore this 
//--------------------------------------------------------------------------- 
// you can ignore this 
#pragma package(smart_init) 
#pragma resource "*.dfm" 
TForm1 *Form1; 
//--------------------------------------------------------------------------- 
// here some global variables 
int  xs,ys;   // window resolution 
HDC  hdc;   // device context 
HGLRC hrc;   // rendering context 
// 4x4 transform matrices 
GLfloat mobj[16]; // object transform matrix 
GLfloat meye[16]; // camera transform matrix 
// key codes for controling (Arrows + Space) 
WORD key_left =37; 
WORD key_right=39; 
WORD key_up =38; 
WORD key_down =40; 
WORD key_forw =32; 
// key pressed state 
bool _left =false; 
bool _right=false; 
bool _up =false; 
bool _down =false; 
bool _forw =false; 
bool _shift=false; 
// sceene need repaint? 
bool _redraw=true; 
//--------------------------------------------------------------------------- 
// here inverse matrix computation 
GLfloat matrix_subdet (  GLfloat *a,int r,int s) 
     { 
     GLfloat c,q[9]; 
     int  i,j,k; 
     k=0;       // q = sub matrix 
     for (j=0;j<4;j++) 
     if (j!=s) 
      for (i=0;i<4;i++) 
      if (i!=r) 
       { 
       q[k]=a[i+(j<<2)]; 
       k++; 
       } 
     c=0; 
     c+=q[0]*q[4]*q[8]; 
     c+=q[1]*q[5]*q[6]; 
     c+=q[2]*q[3]*q[7]; 
     c-=q[0]*q[5]*q[7]; 
     c-=q[1]*q[3]*q[8]; 
     c-=q[2]*q[4]*q[6]; 
     if (int((r+s)&1)) c=-c;  // add signum 
     return c; 
     } 
void matrix_subdet (GLfloat *c,GLfloat *a) 
     { 
     GLfloat q[16]; 
     int  i,j; 
     for (i=0;i<4;i++) 
     for (j=0;j<4;j++) 
      q[j+(i<<2)]=matrix_subdet(a,i,j); 
     for (i=0;i<16;i++) c[i]=q[i]; 
     } 
GLfloat matrix_det  (  GLfloat *a) 
     { 
     GLfloat c=0; 
     c+=a[ 0]*matrix_subdet(a,0,0); 
     c+=a[ 4]*matrix_subdet(a,0,1); 
     c+=a[ 8]*matrix_subdet(a,0,2); 
     c+=a[12]*matrix_subdet(a,0,3); 
     return c; 
     } 
GLfloat matrix_det  (  GLfloat *a,GLfloat *b) 
     { 
     GLfloat c=0; 
     c+=a[ 0]*b[ 0]; 
     c+=a[ 4]*b[ 1]; 
     c+=a[ 8]*b[ 2]; 
     c+=a[12]*b[ 3]; 
     return c; 
     } 
void matrix_inv  (GLfloat *c,GLfloat *a) 
     { 
     GLfloat d[16],D; 
     matrix_subdet(d,a); 
     D=matrix_det(a,d); 
     if (D) D=1.0/D; 
     for (int i=0;i<16;i++) c[i]=d[i]*D; 
     } 
//--------------------------------------------------------------------------- 
// here OpenGL stuff 
//--------------------------------------------------------------------------- 
int TForm1::ogl_init() 
    { 
    // just init OpenGL 
    if (ogl_inicialized) return 1; 
    hdc = GetDC(Form1->Handle);    // get device context 
    PIXELFORMATDESCRIPTOR pfd; 
    ZeroMemory(&pfd, sizeof(pfd));  // set the pixel format for the DC 
    pfd.nSize = sizeof(pfd); 
    pfd.nVersion = 1; 
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 
    pfd.iPixelType = PFD_TYPE_RGBA; 
    pfd.cColorBits = 24; 
    pfd.cDepthBits = 24; 
    pfd.iLayerType = PFD_MAIN_PLANE; 
    SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd); 
    hrc = wglCreateContext(hdc);   // create current rendering context 
    if(hrc == NULL) 
      { 
      ShowMessage("Could not initialize OpenGL Rendering context !!!"); 
      ogl_inicialized=0; 
      return 0; 
      } 
    if(wglMakeCurrent(hdc, hrc) == false) 
      { 
      ShowMessage("Could not make current OpenGL Rendering context !!!"); 
      wglDeleteContext(hrc);   // destroy rendering context 
      ogl_inicialized=0; 
      return 0; 
      } 
    ogl_resize(); 
    glEnable(GL_DEPTH_TEST); 
    glDisable(GL_CULL_FACE); 
    glDisable(GL_TEXTURE_2D); 
    glDisable(GL_BLEND); 
    glShadeModel(GL_SMOOTH); 
    ogl_inicialized=1; 
    return 1; 
    } 
//--------------------------------------------------------------------------- 
void TForm1::ogl_exit() 
    { 
    // just exit from OpneGL 
    if (!ogl_inicialized) return; 
    wglMakeCurrent(NULL, NULL);  // release current rendering context 
    wglDeleteContext(hrc);   // destroy rendering context 
    ogl_inicialized=0; 
    } 
//--------------------------------------------------------------------------- 
void TForm1::ogl_draw() 
    { 
    // rendering routine 
    _redraw=false; 

    // here the whole rendering 
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // background color 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

    GLfloat ieye[16]; // inverse camera transform matrix 
    matrix_inv(ieye,meye); 

    glMatrixMode(GL_MODELVIEW); 
    glLoadMatrixf(ieye); 
    glMultMatrixf(mobj); 

    // render player controlable object 
    // centered by (0,0,0) 
    // +z forward, +x right, +y up 
    float x=0.5,y=0.1,z=0.7; // half sizes of object 
    glColor3f(0.7,0.7,0.7); 
    glBegin(GL_TRIANGLE_FAN); 
    glVertex3f(0.0,0.0,+z); 
    glVertex3f(-x,-y,-z); 
    glVertex3f(+x,-y,-z); 
    glVertex3f(0.0,+y,-z); 
    glVertex3f(-x,-y,-z); 
    glEnd(); 
    glColor3f(0.5,0.5,0.5); 
    glBegin(GL_TRIANGLES); 
    glVertex3f(-x,-y,-z); 
    glVertex3f(+x,-y,-z); 
    glVertex3f(0.0,+y,-z); 
    glEnd(); 
    // render x,y,z axises as r,g,b lines 
    glBegin(GL_LINES); 
    glColor3f(1.0,0.0,0.0); glVertex3f(0.0,0.0,0.0); glVertex3f(1.0,0.0,0.0); 
    glColor3f(0.0,1.0,0.0); glVertex3f(0.0,0.0,0.0); glVertex3f(0.0,1.0,0.0); 
    glColor3f(0.0,0.0,1.0); glVertex3f(0.0,0.0,0.0); glVertex3f(0.0,0.0,1.0); 
    glEnd(); 

    glFlush(); 
    SwapBuffers(hdc); 
    } 
//--------------------------------------------------------------------------- 
void TForm1::ogl_resize() 
    { 
    xs=ClientWidth; 
    ys=ClientHeight; 
    if (xs<=0) xs = 1;     // Prevent a divide by zero 
    if (ys<=0) ys = 1; 
    if (!ogl_inicialized) return; 
    glViewport(0,0,xs,ys);    // Set Viewport to window dimensions 
    glMatrixMode(GL_PROJECTION);  // use projection matrix 
    glLoadIdentity();     // set it to unit matrix 
    gluPerspective(30,float(xs)/float(ys),0.1,100.0); // perspective projection 30 degrees FOV and 0.1 focal length view depth 100-0.1 
    glMatrixMode(GL_TEXTURE);   // use texture matrix 
    glLoadIdentity();     // set it to unit matrix 
    glMatrixMode(GL_MODELVIEW);   // use modelview marix 
    glLoadIdentity();     // set it to unit matrix 
    } 
//--------------------------------------------------------------------------- 
// here window stuff 
//--------------------------------------------------------------------------- 
// window constructor 
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) 
    { 
    ogl_inicialized=0; 
    hdc=NULL; 
    hrc=NULL; 
    ogl_init(); 

    // init matrices 
    glMatrixMode(GL_MODELVIEW); 
    // object is at (0,0,0) rotatet so Z+ is pointing to screen 
    glLoadIdentity(); 
    glRotatef(180.0,0.0,1.0,0.0); 
    glGetFloatv(GL_MODELVIEW_MATRIX,mobj); 
    // camera is behind object looking at object 
    glLoadIdentity(); 
    glTranslatef(0.0,0.0,+20.0); 
    glGetFloatv(GL_MODELVIEW_MATRIX,meye); 
    } 
//--------------------------------------------------------------------------- 
// window destructor 
void __fastcall TForm1::FormDestroy(TObject *Sender) 
    { 
    ogl_exit(); 
    } 
//--------------------------------------------------------------------------- 
// common window events 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormResize(TObject *Sender) 
    { 
    ogl_resize(); 
    _redraw=true; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormPaint(TObject *Sender) 
    { 
    _redraw=true; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::tim_updateTimer(TObject *Sender) 
    { 
    // here movement and repaint timer handler (I have 20ms interval) 

    GLfloat da=5.0; // angular turn speed in [deg/timer_iteration] 
    GLfloat dp=0.1; // movement speed in [world_units/timer_iteration] 

    glMatrixMode(GL_MODELVIEW); 
    glPushMatrix(); 

    if (_shift) // if Shift pressed control camera 
     { 
     // copy meye to GL 
     glLoadMatrixf(meye); 
     // handle keyboard with GL functions 
     if (_left) { _redraw=true; glRotatef(+da,0.0,1.0,0.0); } 
     if (_right) { _redraw=true; glRotatef(-da,0.0,1.0,0.0); } 
     if (_up ) { _redraw=true; glRotatef(-da,1.0,0.0,0.0); } 
     if (_down) { _redraw=true; glRotatef(+da,1.0,0.0,0.0); } 
     // obtain meye from GL 
     glGetFloatv(GL_MODELVIEW_MATRIX,meye); 
     } 
    else{ // else control object 
     // copy meye to GL 
     glLoadMatrixf(mobj); 
     // handle keyboard with GL functions 
     if (_left) { _redraw=true; glRotatef(+da,0.0,1.0,0.0); } 
     if (_right) { _redraw=true; glRotatef(-da,0.0,1.0,0.0); } 
     if (_up ) { _redraw=true; glRotatef(-da,1.0,0.0,0.0); } 
     if (_down) { _redraw=true; glRotatef(+da,1.0,0.0,0.0); } 
     // obtain mobj from GL 
     glGetFloatv(GL_MODELVIEW_MATRIX,mobj); 
     } 

    glPopMatrix(); 
    // handle keyboard directly 
    if (_forw) 
     { 
     _redraw=true; 
     mobj[12]+=dp*mobj[8];  // mobj[12,13,14] is object position 
     mobj[13]+=dp*mobj[9];  // mobj[8,9,10] is object Z axis direction vector 
     mobj[14]+=dp*mobj[10];  // if not glScale is used then it is unit in size 
     } 

    // render if needed 
    if (_redraw) ogl_draw(); 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormMouseWheelDown(TObject *Sender, TShiftState Shift, TPoint &MousePos, bool &Handled) 
    { 
    // move camera matrix forward 
    glMatrixMode(GL_MODELVIEW); 
    glPushMatrix(); 
    glLoadMatrixf(meye); 
    glTranslatef(0,0,+2.0); 
    glGetFloatv(GL_MODELVIEW_MATRIX,meye); 
    glPopMatrix(); 
    Handled=true; 
    _redraw=true; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormMouseWheelUp(TObject *Sender, TShiftState Shift, TPoint &MousePos, bool &Handled) 
    { 
    // move camera matrix backward 
    glMatrixMode(GL_MODELVIEW); 
    glPushMatrix(); 
    glLoadMatrixf(meye); 
    glTranslatef(0,0,-2.0); 
    glGetFloatv(GL_MODELVIEW_MATRIX,meye); 
    glPopMatrix(); 
    Handled=true; 
    _redraw=true; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,TShiftState Shift) 
    { 
    _shift=Shift.Contains(ssShift); 
    // on key down event 
    if (Key==key_left) _left =true; 
    if (Key==key_right) _right=true; 
    if (Key==key_up ) _up =true; 
    if (Key==key_down) _down =true; 
    if (Key==key_forw) _forw =true; 
    Key=0; // key is handled 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) 
    { 
    _shift=Shift.Contains(ssShift); 
    // on key release event 
    if (Key==key_left) _left =false; 
    if (Key==key_right) _right=false; 
    if (Key==key_up ) _up =false; 
    if (Key==key_down) _down =false; 
    if (Key==key_forw) _forw =false; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormActivate(TObject *Sender) 
    { 
    _left =false; // clear key flags after focus change 
    _right=false; // just to avoid constantly "pressed" keys 
    _up =false; // after window focus swaping during key press 
    _down =false; // many games are ignoring this and you need to 
    _forw =false; // press&release the stuck key again to stop movement ... 
    } 
//--------------------------------------------------------------------------- 

それはそれで一つの20msのタイマーとの単純な単一のフォームVCLアプリケーションです。イベントを環境スタイルのコードに移植します。 VCLプラグマを含めることはできません。この例は矢印によって駆動されます。シフトが押されると、矢印はカメラを回転させ、そうでなければオブジェクトを回転させる。スペースはオブジェクトを前方に移動しています。

ここでコンパイルするWin32スタンドアロンのデモ:

このアプローチでは、精度を失うされているトランスフォームを累積の1つの欠点

を持っています。あなたがベクトル乗法(クロス積)を利用することを是正するために。このような行列に対して実行された演算の数を数えるだけであり、閾値に達すると行列を正規化してカウンタをリセットする。

正規化とは、すべての軸が単位と垂直であることを保証することです。主軸の方向(通常は前方またはオブジェクトの前方)をそのままにします。 2つのベクトルの積はそれぞれ垂直ベクトルを返します。

X = Y x Z 
Y = Z x X 
Z = Z/|Z| 
X = X/|X| 
Y = Y/|Y| 

:あなたはX,Y,Z車軸(場所は#1のリンクで説明されている)とZを抽出あれば、たとえば、メイン軸である場合には

// cross product: W = U x V 
W.x=(U.y*V.z)-(U.z*V.y) 
W.y=(U.z*V.x)-(U.x*V.z) 
W.z=(U.x*V.y)-(U.y*V.x) 
// dot product: a = (U.V) 
a=U.x*V.x+U.y*V.y+U.z*V.z 
// abs of vector a = |U| 
a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z)) 

あなたの座標システムが単位行列から派生していない場合は、ある軸をネゲートするか、クロスプロダクトのオペランドの順序を変更して、軸の方向が変わらないようにする必要があります。

は、より多くの情報について見てみましょう:これは非常に混乱して

+0

である必要があります計算私はあなたが表示している概念を理解していますが、私は本当にあなたがC + +でそれを書いて以来、それをC#またはVB .NETに変換すると苦労しています。あなたはこの答えに多大な努力を払っていますので、私は尋ねることは嫌ですが、これらの言語のいずれかで例を挙げることができますか? –

+0

@EricF私はそれらのどれもコードしていません。あなたは逆行列と、私が思っているほど簡単な事象だけを必要とします。 C#コードは、配列の処理方法が少し違っている点を除いて、ほとんど同じです。重要なことは 'ogl_draw()'と 'tim_updateTimer(TObject * Sender)'です。 – Spektre

関連する問題