我一直在尝试使用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;
}