Opengl 相机在某些方向上不起作用

Opengl camera doesn't work in some directions

我的目标是用 C 语言用 OpenGL 和 SDL 1.2 制作一个相机。 我希望能够在所有方向上移动(向前、向后、向左、向右、上下)。我希望能够在所有方向上自由旋转相机:从左到右(就像在 space 中使用 spacecraft)。在第一个版本中,我只使用键盘,但在未来,我希望能够使用鼠标移动相机的方向,使用键盘移动 spacecraft。

这是我的完整代码wrote/recover。

#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>

#define WINDOWS_WIDTH 1280.0
#define WINDOWS_HEIGHT 720.0

#define SPEED_MOVE 0.1
#define SPEED_CAMERA 0.005

typedef struct strucvect{
    double x;
    double y;
    double z;
}vect;

double phi=0, theta=0;

vect position, orientation, lateral, vertical, target;

int continuing=1;

vect scaleVector(vect v){
    v.x *= SPEED_MOVE;
    v.y *= SPEED_MOVE;
    v.z *= SPEED_MOVE;
    return v;
}

vect unitVector(vect v){
    double norm = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
    v.x = v.x / norm;
    v.y = v.y / norm;
    v.z = v.z / norm;
    return v;
}
vect productVector(vect v1 ,vect v2){
    vect new;
    new.x = v1.y * v2.z - v1.z * v2.y;
    new.y = v1.z * v2.x - v1.x * v2.z;
    new.z = v1.x * v2.y - v1.y * v2.x;
    return new ;
}
vect addVector(vect v1 ,vect v2){
    v1.x += v2.x;
    v1.y += v2.y;
    v1.z += v2.z;
    return v1;
}
vect subVector(vect v1 ,vect v2){
    v1.x -= v2.x;
    v1.y -= v2.y;
    v1.z -= v2.z;
    return v1;
}

void computeOrientation(){
    orientation.x = cos(phi) * sin(theta); 
    orientation.y = cos(phi) * cos(theta);
    orientation.z = sin(phi);
}

void manageKeys(Uint8 *keys){
    if(keys[SDLK_ESCAPE] == SDL_PRESSED){
        continuing = 0;
        return;
    }
    if(keys[SDLK_w] == SDL_PRESSED) // move forward
        position = addVector(position,scaleVector(orientation));
    if(keys[SDLK_s] == SDL_PRESSED) // move backward
        position = subVector(position,scaleVector(orientation));
    if(keys[SDLK_a] == SDL_PRESSED){ // moveleft
        lateral = unitVector(productVector(vertical,orientation));
        position = addVector(position,scaleVector(lateral));
    }
    if(keys[SDLK_d] == SDL_PRESSED){ // move right
        lateral = unitVector(productVector(vertical,orientation));
        position = subVector(position,scaleVector(lateral));
    }
    if(keys[SDLK_SPACE] == SDL_PRESSED){ // move up
        vertical =  unitVector(productVector(orientation,lateral));
        position = addVector(position,scaleVector(vertical));
    }
    if(keys[SDLK_q] == SDL_PRESSED){ // move bottom
        vertical = unitVector(productVector(orientation,lateral));
        position = subVector(position,scaleVector(vertical));
    }
    if(keys[SDLK_z] == SDL_PRESSED){ // turn the camera to look at the right
        theta += SPEED_CAMERA;
        if(theta > 6.28318530)
            theta = 0;
        computeOrientation();
        lateral = unitVector(productVector(vertical,orientation));
    }
    if(keys[SDLK_x] == SDL_PRESSED){ // turn the camera to look at the left
        theta -= SPEED_CAMERA;
        if(theta < -6.28318530)
            theta = 0;
        computeOrientation();
        lateral = unitVector(productVector(vertical,orientation));
    }
    if(keys[SDLK_c] == SDL_PRESSED){ // turn the camera to look up
        phi += SPEED_CAMERA;
        if(phi > 6.28318530)
            phi = 0;
        computeOrientation();   
        vertical = unitVector(productVector(orientation,lateral));
    }
    if(keys[SDLK_v] == SDL_PRESSED){ // turn the camera to look bottom
        phi -= SPEED_CAMERA;
        if(phi < -6.28318530)
            phi = 0;
        computeOrientation();   
        vertical = unitVector(productVector(orientation,lateral));
    }
}

void display(){
    double ratio= WINDOWS_WIDTH/WINDOWS_HEIGHT;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(70,ratio,1,2000);
    target = addVector(position ,orientation); // we look a point in front of us : in the direction of the orientation vector
    gluLookAt(position.x, position.y, position.z,  target.x,target.y,target.z,   vertical.x,vertical.y,vertical.z);

    glBegin(GL_QUADS); // we draw the 6 face of a cube
    // bottom wall
    glColor3f(0.1, 0.1, 0.6);
    glVertex3f(-10,-10,-10);
    glVertex3f(-10,10,-10);
    glVertex3f(10,10,-10);
    glVertex3f(10,-10,-10); 
    // up wall
    glColor3f(0.6, 0.1, 0.6);
    glVertex3f(-10,-10,10);
    glVertex3f(-10,10,10);
    glVertex3f(10,10,10);
    glVertex3f(10,-10,10);      
    // right wall
    glColor3f(0.6, 0.1, 0.1);
    glVertex3f(-10,-10,-10);
    glVertex3f(-10,-10,10);
    glVertex3f(10,-10,10);
    glVertex3f(10,-10,-10);
    // left wall
    glColor3f(0.6, 0.6, 0.1);
    glVertex3f(-10,10,-10);
    glVertex3f(-10,10,10);
    glVertex3f(10,10,10);
    glVertex3f(10,10,-10);      
    // front wall
    glColor3f(0.1, 0.6, 0.1);
    glVertex3f(-10,-10,-10);
    glVertex3f(-10,-10,10);
    glVertex3f(-10,10,10);
    glVertex3f(-10,10,-10);
    // behind wall
    glColor3f(0.1, 0.6, 0.6);
    glVertex3f(10,-10,-10);
    glVertex3f(10,-10,10);
    glVertex3f(10,10,10);
    glVertex3f(10,10,-10);      
    glEnd();

    glFlush();
    SDL_GL_SwapBuffers();
}

int main(int argc, char* argv[]){
    SDL_Event event;    
    SDL_Init(SDL_INIT_VIDEO);
    SDL_WM_SetCaption("Camera",NULL);
    SDL_SetVideoMode(WINDOWS_WIDTH, WINDOWS_HEIGHT, 32, SDL_OPENGL);

    glEnable(GL_DEPTH_TEST);

    position.x = position.y = position.z = 0; // we are in the middle of the cube

    orientation.x = cos(phi) * sin(theta); 
    orientation.y = cos(phi) * cos(theta);
    orientation.z = sin(phi);

    vertical.x = 0;
    vertical.y = 0;
    vertical.z = 1;

    lateral = unitVector(productVector(vertical,orientation));

    while (continuing){
        SDL_PollEvent(&event);
        switch(event.type){
        case SDL_QUIT:
            continuing = 0;
            break;
        }
        manageKeys(SDL_GetKeyState(NULL));
        display();
    }
    SDL_Quit();
    return EXIT_SUCCESS;
}

以及我用来编译的命令:

gcc -Wall -lGL -lGLU -lm -lSDL main.c -o main

在程序中,我为6面画了一个不同颜色的立方体,并将相机放在立方体的中间。

我使用这些键使相机移动 w(forward) a(left) s(backward) d(right) space(up) q(down)

我可以毫无问题地沿两个方向之一旋转(使用 z(右)和 x(左)或 c(上)和 v(下))。我什至可以水平移动相机,然后垂直移动相机。

首先是垂直旋转,然后是水平旋转。

我在网上读到一些关于欧拉角万向节锁的问题,但我不知道这是否是我的问题。 我还阅读了有关带四元数的相机的信息,如果我无法完成第一个工作,我打算使用此方法。

你能告诉我我的问题是什么以及如何解决吗?

我给相机植入了四元数(我用这个website来做公式)。我拥有我想要的一切:在 6 个方向上移动,并旋转相机 up/down 和 left/right。我什至可以转动 left/right 相机。

完整代码如下:

main.c

#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "camera.h"

#define WINDOWS_WIDTH 1280.0
#define WINDOWS_HEIGHT 720.0

#define SPEED_MOVE 0.1
#define SPEED_CAMERA 0.005

vector position, orientation, lateral, vertical, target;

int continuing=1;

void manageKeys(Uint8 *keys){
    if(keys[SDLK_ESCAPE] == SDL_PRESSED){
        continuing = 0;
        return;
    }

    if(keys[SDLK_w] == SDL_PRESSED) // move forward
      position = addVector(position, scaleVector(orientation, SPEED_MOVE));
    if(keys[SDLK_s] == SDL_PRESSED) // move backward
      position = subVector(position, scaleVector(orientation, SPEED_MOVE));
    if(keys[SDLK_a] == SDL_PRESSED) // move left
      position = addVector(position, scaleVector(lateral, SPEED_MOVE));
    if(keys[SDLK_d] == SDL_PRESSED) // move right
      position = subVector(position, scaleVector(lateral, SPEED_MOVE));
    if(keys[SDLK_SPACE] == SDL_PRESSED) // move up
      position = addVector(position, scaleVector(vertical, SPEED_MOVE));
    if(keys[SDLK_q] == SDL_PRESSED) // move bottom
      position = subVector(position, scaleVector(vertical, SPEED_MOVE));


    if(keys[SDLK_z] == SDL_PRESSED){ // turn the camera to look at the left
      orientation = makeRotation(orientation, vertical, SPEED_CAMERA);
      lateral = unitVector(productVector(vertical,orientation));
    }
    if(keys[SDLK_x] == SDL_PRESSED){ // turn the camera to look at the right
      orientation = makeRotation(orientation, vertical, -SPEED_CAMERA);
      lateral = unitVector(productVector(vertical,orientation));
    }
    if(keys[SDLK_c] == SDL_PRESSED){ // turn the camera to look up
      orientation = makeRotation(orientation, lateral, -SPEED_CAMERA);
      vertical = unitVector(productVector(orientation,lateral));
    }
    if(keys[SDLK_v] == SDL_PRESSED){ // turn the camera to look bottom
      orientation = makeRotation(orientation, lateral, SPEED_CAMERA);
      vertical = unitVector(productVector(orientation,lateral));        
    }
    if(keys[SDLK_f] == SDL_PRESSED){// turn the camera with a roll to the left
      lateral = makeRotation(lateral, orientation, -SPEED_CAMERA);
      vertical = unitVector(productVector(orientation,lateral));
    }
    if(keys[SDLK_g] == SDL_PRESSED){// turn the camera with a roll to the right
      lateral = makeRotation(lateral, orientation, SPEED_CAMERA);
      vertical = unitVector(productVector(orientation,lateral));        
    }
}

void display(){
    double ratio= WINDOWS_WIDTH/WINDOWS_HEIGHT;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(70,ratio,1,2000);

    target = addVector(position, orientation);
    gluLookAt(position.x, position.y, position.z,      target.x,target.y,target.z,     vertical.x,vertical.y,vertical.z);

    glBegin(GL_QUADS);
    // bottom wall
    glColor3f(0.1, 0.1, 0.6);
    glVertex3f(-10,-10,-10);
    glVertex3f(-10,10,-10);
    glVertex3f(10,10,-10);
    glVertex3f(10,-10,-10); 
    // up wall
    glColor3f(0.6, 0.1, 0.6);
    glVertex3f(-10,-10,10);
    glVertex3f(-10,10,10);
    glVertex3f(10,10,10);
    glVertex3f(10,-10,10);      
    // right wall
    glColor3f(0.6, 0.1, 0.1);
    glVertex3f(-10,-10,-10);
    glVertex3f(-10,-10,10);
    glVertex3f(10,-10,10);
    glVertex3f(10,-10,-10);
    // left wall
    glColor3f(0.6, 0.6, 0.1);
    glVertex3f(-10,10,-10);
    glVertex3f(-10,10,10);
    glVertex3f(10,10,10);
    glVertex3f(10,10,-10);      
    // front wall
    glColor3f(0.1, 0.6, 0.1);
    glVertex3f(-10,-10,-10);
    glVertex3f(-10,-10,10);
    glVertex3f(-10,10,10);
    glVertex3f(-10,10,-10);
    // behind wall
    glColor3f(0.1, 0.6, 0.6);
    glVertex3f(10,-10,-10);
    glVertex3f(10,-10,10);
    glVertex3f(10,10,10);
    glVertex3f(10,10,-10);      
    glEnd();

    glFlush();
    SDL_GL_SwapBuffers();
}

int main(int argc, char* argv[]){
    SDL_Event event;    
    SDL_Init(SDL_INIT_VIDEO);
    SDL_WM_SetCaption("Camera",NULL);
    SDL_SetVideoMode(WINDOWS_WIDTH, WINDOWS_HEIGHT, 32, SDL_OPENGL);

    glEnable(GL_DEPTH_TEST);

    position.x = position.y = position.z = 0; // we are in the middle of the cube

    orientation.x = 1;
    orientation.y = 0;
    orientation.z = 0;

    vertical.x = 0;
    vertical.y = 0;
    vertical.z = 1;

    lateral = unitVector(productVector(vertical,orientation));

    while (continuing){
        SDL_PollEvent(&event);
        switch(event.type){
        case SDL_QUIT:
            continuing = 0;
            break;
        }
        manageKeys(SDL_GetKeyState(NULL));
        display();
    }
    SDL_Quit();
    return EXIT_SUCCESS;
}

camera.h

#ifndef CAMERA
#define CAMERA

#include <math.h>

typedef struct structVector{
    double x, y, z;
}vector;

vector addVector(vector u, vector v);

vector subVector(vector u, vector v);

vector unitVector(vector v);

vector productVector(vector u ,vector v);

vector scaleVector(vector u, double scale);



typedef struct structQuaternion{
    double x, y, z, w;
}quaternion;

quaternion conjugateQuaternion(quaternion a);

quaternion multiplyingQuaternion(quaternion a, quaternion b);

quaternion vector2quaternion(vector v);

quaternion createRotation(vector v, double angle);

vector makeRotation(vector v, vector rotationAxis, double angle);

#endif

camera.c

#include "camera.h"

vector addVector(vector u, vector v){
  u.x += v.x;
  u.y += v.y;
  u.z += v.z;
  return u;
}
vector subVector(vector u, vector v){
  u.x -= v.x;
  u.y -= v.y;
  u.z -= v.z;
  return u;
}
vector unitVector(vector v){
  double norm = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
  v.x /= norm;
  v.y /= norm;
  v.z /= norm;
  return v;
}
vector productVector(vector u ,vector v){
  vector a;
  a.x = u.y * v.z - u.z * v.y;
  a.y = u.z * v.x - u.x * v.z;
  a.z = u.x * v.y - u.y * v.x;
  return a;
}
vector scaleVector(vector u, double scale){
  u.x *= scale;
  u.y *= scale;
  u.z *= scale;
  return u;
}

quaternion conjugateQuaternion(quaternion a){
  a.x = -a.x;
  a.y = -a.y;
  a.z = -a.z;
  return a;
}
quaternion multiplyingQuaternion(quaternion a, quaternion b){
  quaternion c;
  c.x = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
  c.y = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
  c.z = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
  c.w = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
  return c;
}
quaternion vector2quaternion(vector v){
  quaternion a;
  a.x = v.x;
  a.y = v.y;
  a.z = v.z;
  a.w = 0;
  return a;
}
quaternion createRotation(vector v, double angle){
  quaternion a;
  a.x = v.x * sin(angle/2);
  a.y = v.y * sin(angle/2);
  a.z = v.z * sin(angle/2);
  a.w = cos(angle/2);
  return a;
}
vector makeRotation(vector v, vector rotationAxis, double angle){
  quaternion rotation = createRotation(rotationAxis, angle);
  quaternion quaternionOfV  = vector2quaternion(v);
  quaternion a = multiplyingQuaternion(rotation, quaternionOfV );
  a = multiplyingQuaternion(a, conjugateQuaternion(rotation));
  v.x = a.x;
  v.y = a.y;
  v.z = a.z;
  return v;
}

即使我不看东西,我仍然使用函数 gluLookAt。也许还有其他更好的方法。