2016-10-17 5 views
0

фонOpenGL - Вращение не работает

я в настоящее время окружение я создал, к которому я загрузить некоторые формы (для справки, где я), и я пытаюсь получить элементы управления, чтобы двигаться вперед, назад, и вращайтесь там, где я смотрю.

У меня есть вперед и назад, с использованием точки зрения и точки положения камеры. Я пытаюсь поворачивать на 360 градусов, но, похоже, получает некоторые странные ограничения, основанные на том, что у меня есть до сих пор. Я выводил свой xangle, чтобы видеть, где я нахожусь всегда.

Чтобы повернуть, я изменяю переменную lookat (x, y и z), сохраняя при этом положение (x, y, z) одинаковым.

Проблема

При непрерывном увеличении угла, я ударил две точки отражения, где появился поворот меняет направление. По какой причине они, кажется, происходят в 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

Чтобы повернуть, я изменяю переменную lookat (x, y и z), сохраняя при этом позицию (x, y, z). –

+0

Но как вы его меняете? Это решающий момент всего вопроса. – derhass

ответ

1

Не используйте углы Эйлера для этого, поскольку у них есть много вопросов, как тот, который вы получили. Вместо этого используйте кумулятивные матрицы преобразования. Похоже, этот вопрос задают снова и снова ... в течение некоторого времени. Поэтому я решил сделать пример, как сделать это с чистым OpenGL 1.0 no GLM или смешные вещи.

  1. Определение

    Позволяет иметь игрок управление, в состоянии объекта под названием obj и камеры eye. Каждый из них должен быть представлен отдельной матрицей преобразования 4x4. OpenGL хранит их как 1D-массивы.Для получения дополнительной информации см

    Мы хотим контролировать obj в своей локальной системе координат, независимой от камеры. Если вы используете как матрицы, так и объекты, умноженные вместе в GL_MODELVIEW, чтобы избежать злоупотребления матрицей GL_PROJECTION, вы быстро поймете, что это не разрешимо просто glRotate/glTranslate звонков обычным способом.

    Из-за этого многие люди переключаются на углы Эйлера, которые могут справиться с этим легко, но поднимают целую кучу других проблем (многие современные игры все еще используют их там, где они не должны, и есть множество ошибок и проблем из-за Это).

    Так добавьте в ваш проект:

    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 
    

    С этим мы можем использовать GL вызывает наши матрицы вне цикла рендеринга. (например, в обработчике клавиатуры или в некотором таймере).

  3. Rendering матрицы петли

    Теперь, если мы хотим, чтобы сделать наш объект с нашими матрицами, то мы должны установить матрицы GL правильно. Пусть предполагается, что матрица проекции задана, а только вопрос модели. Видовая матрица должна быть:

    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 ... 
    } 
//--------------------------------------------------------------------------- 

Это просто одно приложение форм VCL с одним 20ms таймера в нем. Поэтому переносите события в код стиля среды. Вы можете игнорировать прагмы VCL. Этот пример управляется стрелками. Если сдвиг нажат, стрелки поворачивают камеру в противном случае. Пространство перемещает объект вперед.

Здесь компилируется Win32 автономный демо:

Этот подход имеет один недостаток

С сложении преобразований вы теряете точность. Чтобы исправить это, вы можете использовать векторное умножение (перекрестное произведение).Просто подсчитайте количество операций, выполняемых на такой матрице, и если достигнут порог, нормализуйте матрицу и сбросьте счетчик.

По нормализации я имею в виду обеспечение того, чтобы все оси были единичными и перпендикулярными друг другу. Оставляя направление основной оси (обычно вперед или вид), как есть. Перекрестное произведение двух векторов возвращает перпендикулярный вектор каждому. Так, например, если вы извлечь X,Y,Z осях (места описаны в ссылке в # 1) и Z является главной осью, то:

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

Где:

// 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# или VB .NET, так как вы написали его на C++. Мне не нравится спрашивать, так как вы приложили много усилий в этот ответ, но можете ли вы сделать пример на любом из этих языков? –

+0

@EricF Я не кодирую ни в одном из них, так что нет. Думаю, вам нужна только обратная матрица и события, которые достаточно просты. Код C# должен быть почти идентичным, за исключением того, что он обрабатывает массивы несколько иначе. Важным материалом является 'ogl_draw()' и 'tim_updateTimer (TObject * Sender)' – Spektre

Смежные вопросы