如何在openGL中编写FPS相机代码

时间:2012-02-11 04:55:29

标签: c++ opengl

我一直在尝试使用C ++在openGL中开发一个可用的FPS相机。

使用欧拉角并查看来自openGL superBible v.5的示例代码,我能够获得良好的翻译工作,并且如果我将旋转限制为单个维度(例如,只是俯仰,或者只是偏航) ,这也很好。但是当我组合俯仰和偏航时,我最终会产生奇怪的结果旋转以及滚动的变化(即地平线不会保持水平)。这是迄今为止我所拥有的最好的系统,如果不是滚动,那将是完美的。但是,我对这方面的情况并不是很了解,因为我正在做的就是调用方法来翻译和旋转GLFrame。

我读到了欧拉角,发现它们遇到了一个叫做万向节锁的问题,所以我尝试转而使用四元数。我存储俯仰和偏航变量并从中导出四元数,然后从四元数构建矩阵并将其推送到MODELVIEW堆栈。发生的事情是它似乎旋转整个世界而不是相机的POV,并且非常奇怪地拉伸出形状。

这是源代码:

#include <GLTools.h>
#include <GLShaderManager.h> 
#include <GL/glut.h>
#include <GLBatch.h>
#include <GLFrustum.h>
#include <GLFrame.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <math.h>
#include <stdio.h>
#include <iostream>
using namespace std;

#define CUBE_COUNT 400
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define ESCAPE_KEY 27

//CLASSES
GLShaderManager     shaderManager;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLFrame             cameraFrame;
GLFrame             objectFrame;
GLFrame             cubes[CUBE_COUNT];
GLFrustum           viewFrustum;

GLBatch             triangleBatch;
GLBatch             cubeBatch;

GLGeometryTransform transformPipeline;
M3DMatrix44f        shadowMatrix;
M3DMatrix44f        camMatrix;

GLfloat vGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
GLfloat vBlack[] = {0.0f, 0.0f, 0.0f, 1.0f};

float pitch;
float yaw;
float roll;

float mouse_x;
float mouse_y;

bool w_pressed = false;
bool a_pressed = false;
bool s_pressed = false;
bool d_pressed = false;

float cam_x = 0.0f;
float cam_y = 0.0f;
float cam_z = 0.0f;

///////////////////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering context. 
// This is the first opportunity to do any OpenGL related tasks.

typedef struct {
    GLfloat x;
    GLfloat y;
    GLfloat z;
    GLfloat w;
} Quaternion3D;

static inline void Quaternion3DNormalise(Quaternion3D *quaternion) {
    GLfloat magnitude;

    magnitude = sqrtf((quaternion->x * quaternion->x) +
                      (quaternion->y * quaternion->y) +
                      (quaternion->z * quaternion->z) +
                      (quaternion->w * quaternion->w));

    quaternion->x /= magnitude;
    quaternion->y /= magnitude;
    quaternion->z /= magnitude;
    quaternion->w /= magnitude;
}

static inline void Quaternion3DMultiplication(Quaternion3D *newQ, Quaternion3D *q1, Quaternion3D *q2) {
    newQ->w = q1->w * q2->w - q1->x * q2->x - q1->y * q2->y - q1->z * q2->z;
    newQ->x = q1->w * q2->x + q1->x * q2->w + q1->y * q2->z - q1->z * q2->y;
    newQ->y = q1->w * q2->y - q1->x * q2->z + q1->y * q2->w + q1->z * q2->x;
    newQ->z = q1->w * q2->z + q1->x * q2->y - q1->y * q2->x + q1->z * q2->w;
}

static inline void Quaternion3DMultIdentity(Quaternion3D *q) {
    q->x = 0;
    q->y = 0;
    q->z = 0;
    q->w = 1;
}

static inline void Quaternion3DInverse(Quaternion3D *q) {
    q->x *= -1;
    q->y *= -1;
    q->z *= -1;
}

static inline void EulerToQuaternion3D(Quaternion3D *q, float pitch, float yaw, float roll) {
    float radiansPitch = m3dDegToRad(pitch);
    float radiansYaw   = m3dDegToRad(yaw);
    float radiansRoll  = m3dDegToRad(roll);

    float sinPitch = sin(radiansPitch * 0.5);
    float cosPitch = cos(radiansPitch * 0.5);
    float sinYaw = sin(radiansYaw * 0.5);
    float cosYaw = cos(radiansYaw * 0.5);
    float sinRoll = sin(radiansRoll * 0.5);
    float cosRoll = cos(radiansRoll * 0.5);

    q->w = cosYaw * cosRoll * cosPitch - sinYaw * sinRoll * sinPitch;
    q->x = sinYaw * sinRoll * cosPitch + cosYaw * cosRoll * sinPitch;
    q->y = sinYaw * cosRoll * cosPitch + cosYaw * sinRoll * sinPitch;
    q->z = cosYaw * sinRoll * cosPitch - sinYaw * cosRoll * sinPitch;

    Quaternion3DNormalise(q);
}

static inline void QuatToMatrix(M3DMatrix44f matrix, Quaternion3D *q) {

    //first column
    matrix[0] = 1 - 2 * (q->y * q->y + q->z + q->z);
    matrix[1] = 2 * (q->x * q->y * q->z * q->w);
    matrix[2] = 2 * (q->x * q->z * q->y * q->w);
    matrix[3] = 0;

    //second column
    matrix[4] = 2 * (q->x * q->y - q->z * q->w);
    matrix[5] = 1 - 2 * (q->x * q->x + q->z * q->z);
    matrix[6] = 2 * (q->y * q->z + q->x * q->w);
    matrix[7] = 0;

    //third column
    matrix[8] = 2 * (q->x * q->z + q->y * q->w);
    matrix[9] = 2 * (q->y * q->z - q->x * q->w);
    matrix[10] = 1 - 2 * (q->x * q->x - q->y * q->y);
    matrix[11] = 0;

    //fourth column
    matrix[12] = 0;
    matrix[13] = 0;
    matrix[14] = 0;
    matrix[15] = 1;
}

void setupRC()
    {
    // Black background
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    shaderManager.InitializeStockShaders();

    glEnable(GL_DEPTH_TEST);

    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

    cameraFrame.MoveForward(-15.0f);
    cam_x -= 15.0f;
    cameraFrame.TranslateWorld(0.0f, 5.0f, 0.0f);
    cam_y += 5.0f;

    GLfloat cVerts[] = {

        //FRONT FACE
        0.0f, 0.0f, 0.0f, 
        1.0f, 0.0f, 0.0f,
        1.0f, 1.0f, 0.0f,
        0.0f, 1.0f, 0.0f,

        //BACK FACE
        0.0f, 0.0f, 1.0f, 
        0.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f,

        //LEFT FACE
        0.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 1.0f, 1.0f,

        //RIGHT FACE
        1.0f, 0.0f, 0.0f,
        1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 0.0f,

        //TOP FACE
        0.0f, 1.0f, 0.0f,
        1.0f, 1.0f, 0.0f,
        1.0f, 1.0f, 1.0f,
        0.0f, 1.0f, 1.0f,

        //BOTTOM FACE
        0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f,
        1.0f, 0.0f, 1.0f,
        1.0f, 0.0f, 0.0f
        };

    cubeBatch.Begin(GL_QUADS, 24);
    cubeBatch.CopyVertexData3f(cVerts);
    cubeBatch.End();

    for(int i=0; i < CUBE_COUNT; i++) {
        GLfloat x = (GLfloat)(i / int(sqrt(double(CUBE_COUNT))));
        GLfloat z = (GLfloat)(i % int(sqrt(double(CUBE_COUNT))));
        cubes[i].SetOrigin(x, 0.0f, z);
    }
}

void drawWireFramedBatch(GLBatch* pBatch) {
    //Draws the batch solid green
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();

    //Draws the outline black
    glPolygonOffset(-1.0f, -1.0f);
    glEnable(GL_POLYGON_OFFSET_LINE);

    //Draws the lines antialiased
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    //Draws black wireframe version of geometry
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glLineWidth(1.0f);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();

    //Reset everything
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}

///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void renderScene(void) {
    //Cubes are green
    static GLfloat vCubeColour[] = {0.0f, 1.0f, 0.0f, 1.0f};

    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //This pushes the identity matrix onto the matrix stack
    modelViewMatrix.PushMatrix();
    //printf("%i, %i, %i\n", cam_x, cam_y, cam_z);
    //cout << "PITCH: " << pitch << " YAW: " << yaw << " ROLL: " << roll << endl;

    float forwards = cam_x * cos(m3dDegToRad(yaw)) + cam_z * -sin(m3dDegToRad(yaw));
    float sideways = cam_x * sin(m3dDegToRad(yaw)) + cam_z * cos(m3dDegToRad(yaw));
    float upwards  = cam_x * sin(m3dDegToRad(pitch));

    //cameraFrame.MoveForward(forwards);
    //cameraFrame.MoveUp(upwards);
    //cameraFrame.MoveRight(sideways);

    //cameraFrame.RotateLocal(m3dDegToRad(yaw), 0.0f, 1.0f, 0.0f);
    //cameraFrame.RotateLocal(m3dDegToRad(-pitch), 1.0f, 0.0f, 0.0f);
    //cameraFrame.RotateLocal(m3dDegToRad(roll), 0.0f, 0.0f, 1.0f);

    //create matrix for camera
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);

    modelViewMatrix.MultMatrix(mCamera);
    modelViewMatrix.MultMatrix(camMatrix);

    M3DVector4f vLightPos = {0.0f, 10.0f, 5.0f, 1.0f };
    M3DVector4f vLightEyePos;
    m3dTransformVector4(vLightEyePos, vLightPos, mCamera);

    //create matrix for object handling
    M3DMatrix44f mObjectFrame;
    objectFrame.GetMatrix(mObjectFrame);
    modelViewMatrix.MultMatrix(mObjectFrame);
    //use a basic stock shader - pass in modelview projection matrix
    //shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);

    for(int i=0; i < CUBE_COUNT; i++) {
        modelViewMatrix.PushMatrix();
        modelViewMatrix.MultMatrix(cubes[i]);
        drawWireFramedBatch(&cubeBatch);
        modelViewMatrix.PopMatrix();
    }

    modelViewMatrix.PopMatrix();
    // Perform the buffer swap to display back buffer
    glutSwapBuffers();
}

void resetCameraFrame() {
    cameraFrame = GLFrame();
    cameraFrame.MoveForward(-15.0f);
    cameraFrame.TranslateWorld(0.0f, 5.0f, 0.0f);
}

void getMouseCoords(int x, int y) {
    mouse_x = x;
    mouse_y = y;
}

void update(void) {
    float turn_angle = 0.005f;
    float move_speed = 0.5f;
    bool rotate = false;
    float rotation;

    //HANDLE CAMERA ROTATION

    yaw   += turn_angle * (WINDOW_WIDTH / 2 - mouse_x);
    pitch += turn_angle * (WINDOW_HEIGHT / 2 - mouse_y);

    pitch = pitch > 360 ? pitch - 360.0f : pitch;
    pitch = pitch < -360 ? pitch + 360.0f : pitch;
    yaw   = yaw > 360 ? yaw - 360.0f : yaw;
    yaw   = yaw < -360 ? yaw + 360.0f : yaw;
    roll  = roll > 360 ? roll - 360.0f : roll;
    roll  = roll < -360 ? roll + 360.0f : roll;

    Quaternion3D q = Quaternion3D();
    EulerToQuaternion3D(&q, pitch, yaw, roll);
    QuatToMatrix(camMatrix, &q);


    //cameraFrame.RotateWorld(m3dDegToRad(yaw), 0.0f, 1.0f, 0.0f);
    //cameraFrame.RotateWorld(m3dDegToRad(pitch), 1.0f, 0.0f, 0.0f);
    //cameraFrame.RotateWorld(m3dDegToRad(roll), 0.0f, 0.0f, 1.0f);

    //cout << "PITCH: " << pitch << " YAW: " << yaw << " ROLL: " << roll << endl;

    //resetCameraFrame();

    //HANDLE CAMERA POSITION
    if (w_pressed) { cameraFrame.MoveForward(move_speed);  cout << "FORWARD" << endl;  }
    if (a_pressed) { cameraFrame.MoveRight(move_speed);    cout << "LEFT" << endl;     }
    if (s_pressed) { cameraFrame.MoveForward(-move_speed); cout << "RIGHT" << endl;    }
    if (d_pressed) { cameraFrame.MoveRight(-move_speed);   cout << "BACKWARD" << endl; }
    /*
    if (w_pressed) { cam_x += move_speed; cout << "FORWARD" << endl;  }
    if (a_pressed) { cam_z += move_speed; cout << "LEFT" << endl;     }
    if (s_pressed) { cam_x -= move_speed; cout << "BACKWARD" << endl;    }
    if (d_pressed) { cam_z -= move_speed; cout << "RIGHT" << endl; }

    */
    glutPostRedisplay();
}

void specialKeyPress(int key, int x, int y) {
    float linear = 0.1f;
    float angular = float(m3dDegToRad(5.0f));

    if(key == GLUT_KEY_UP)
        cameraFrame.MoveForward(linear);

    if(key == GLUT_KEY_DOWN)
        cameraFrame.MoveForward(-linear);

    if(key == GLUT_KEY_LEFT)
        cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);

    if(key == GLUT_KEY_RIGHT)
        cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);        
    }

void specialKeyRelease(int key, int x, int y) {
}

void normalKeyPress(unsigned char key, int x, int y) {

    printf ("The '%c' key was pressed\n", key);

    switch(key) {
        case 'w':
            w_pressed = true;
            break;
        case 'a':
            a_pressed = true;
            break;
        case 's':
            s_pressed = true;
            break;
        case 'd':
            d_pressed = true;
            break;
        case ESCAPE_KEY:
            exit(0);
            break;
    }
}

void normalKeyRelease(unsigned char key, int x, int y) {

    printf ("The '%c' key was released\n", key);

    switch(key) {
        case 'w':
            w_pressed = false;
            break;
        case 'a':
            a_pressed = false;
            break;
        case 's':
            s_pressed = false;
            break;
        case 'd':
            d_pressed = false;
            break;
    }
}

///////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void changeSize(int w, int h) {
    glViewport(0, 0, w, h);
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}

///////////////////////////////////////////////////////////////////////////////
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
    {
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutCreateWindow("Cube");
    glutReshapeFunc(changeSize);
    glutKeyboardFunc(normalKeyPress);
    glutKeyboardUpFunc(normalKeyRelease);
    glutSpecialFunc(specialKeyPress);
    glutSpecialUpFunc(specialKeyRelease);
    glutPassiveMotionFunc(getMouseCoords);
    glutDisplayFunc(renderScene);
    glutIdleFunc(update);

    GLenum err = glewInit();

    if(GLEW_OK != err){
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }

    setupRC();

    glutMainLoop();
    return 0;
}

0 个答案:

没有答案