You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现C++ OpenGL相机的左右旋转功能?

问题描述

我在项目中创建了一个3D立方体并添加了相机,可通过W、A、S、D键实现前后左右移动,Q、E键控制相机上下移动,但无法实现左右旋转(例如无法查看立方体背面)。尝试调整相机目标点变量时出现诸多问题:

  • 按下W键时移动方向与视角不符
  • 无法旋转超过90度
  • 旋转速度逐渐变慢

以下是我的代码:

#include <Windows.h>

#include <gl/GL.h>
#include <gl/GLU.h>
#include <GLFW/glfw3.h>

#include <cstdio>
#include <iostream>
#include <cmath>
#include <math.h>

int width = 1280;
int height = 720;

float camera_z = 5;
float camera_y = 0;
float camera_x = 0;

float fov = 60;

GLFWwindow* window;

float speed = 0.01;


GLfloat vertices[] = {
    -1, -1, -1,   -1, -1,  1,   -1,  1,  1,   -1,  1, -1,
    1, -1, -1,    1, -1,  1,    1,  1,  1,    1,  1, -1,
    -1, -1, -1,   -1, -1,  1,    1, -1,  1,    1, -1, -1,
    -1,  1, -1,   -1,  1,  1,    1,  1,  1,    1,  1, -1,
    -1, -1, -1,   -1,  1, -1,    1,  1, -1,    1, -1, -1,
    -1, -1,  1,   -1,  1,  1,    1,  1,  1,    1, -1,  1
};

GLfloat colors[] = {
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   0, 1, 1,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   0, 1, 1,   0, 1, 0,   0, 1, 0
};



void keyboard() {

    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {

        glfwSetWindowShouldClose(window, GL_TRUE);

    }
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {

        camera_z = camera_z - speed;

    }
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {

        camera_x = camera_x - speed;

    }
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {

        camera_z = camera_z + speed;

    }
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {

        camera_x = camera_x + speed;
    }
    if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) {

        camera_y = camera_y + speed;
    }
    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) {

        camera_y = camera_y - speed;
    }

    
    if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {

        // need help
       
    }
    if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
       
        // need help
    }

   
}
       



void drawCube() {

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glColorPointer(3, GL_FLOAT, 0, colors);
    
    glDrawArrays(GL_QUADS, 0, 24);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);

}



int main(void)
{
    

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(width, height, "C++ OpenGL ", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    
    glEnable(GL_DEPTH_TEST); 

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        glViewport(0, 0, width, height);

        /* Render here */
        glClearColor(0.0, 192/256, 1, 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        float aspect = (float)width / (float)height;
        float fov = 60;
        gluPerspective(fov, aspect, 0.1, 1000);
        
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        keyboard();
        
                                                      
        gluLookAt(
            camera_x,
            camera_y,
            camera_z,
            camera_x + 1, // need help
            camera_y + 1, // need help
            camera_z - fov ,
            0,
            1, 
            0
        );

        glTranslatef(0, 0,-3);
        drawCube();


        glFlush();

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}
解决方案

核心问题在于你直接修改相机位置而没有维护相机朝向,导致移动和旋转逻辑脱节。我们需要用**偏航角(yaw)**记录相机左右旋转角度,通过三角函数计算视线方向,同时调整WASD移动逻辑使其跟随当前视角。

关键修改说明

  1. 添加朝向变量:新增yaw变量记录相机偏航角,初始值设为-90°,让初始朝向与你原有逻辑一致(沿-z轴)
  2. 重构移动逻辑:WASD不再直接修改x/z坐标,而是根据当前yaw角计算移动方向,保证移动方向与视角匹配
  3. 实现旋转控制:左右方向键修改yaw值,通过三角函数计算gluLookAt的目标点,解决旋转限制和速度问题
  4. 修复gluLookAt参数:目标点改为相机位置加上视线方向向量,不再使用固定偏移

修改后的完整代码

#include <Windows.h>

#include <gl/GL.h>
#include <gl/GLU.h>
#include <GLFW/glfw3.h>

#include <cstdio>
#include <iostream>
#include <cmath>
#include <math.h>

int width = 1280;
int height = 720;

// 相机位置
float camera_x = 0;
float camera_y = 0;
float camera_z = 5;

// 相机朝向:偏航角(左右旋转),单位为角度
float yaw = -90.0f;

float fov = 60;
GLFWwindow* window;

float move_speed = 0.05f;
float rotate_speed = 1.0f; // 旋转速度(度/帧)


GLfloat vertices[] = {
    -1, -1, -1,   -1, -1,  1,   -1,  1,  1,   -1,  1, -1,
    1, -1, -1,    1, -1,  1,    1,  1,  1,    1,  1, -1,
    -1, -1, -1,   -1, -1,  1,    1, -1,  1,    1, -1, -1,
    -1,  1, -1,   -1,  1,  1,    1,  1,  1,    1,  1, -1,
    -1, -1, -1,   -1,  1, -1,    1,  1, -1,    1, -1, -1,
    -1, -1,  1,   -1,  1,  1,    1,  1,  1,    1, -1,  1
};

GLfloat colors[] = {
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   0, 1, 1,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   0, 1, 1,   0, 1, 0,   0, 1, 0
};

// 计算视线方向向量
void calculateLookDirection(float& look_x, float& look_z) {
    float yaw_rad = yaw * M_PI / 180.0f;
    look_x = cos(yaw_rad);
    look_z = sin(yaw_rad);
}

void keyboard() {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
        glfwSetWindowShouldClose(window, GL_TRUE);
    }

    float look_x, look_z;
    calculateLookDirection(look_x, look_z);

    // 前后移动(W/S):沿视线方向
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
        camera_x += look_x * move_speed;
        camera_z += look_z * move_speed;
    }
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
        camera_x -= look_x * move_speed;
        camera_z -= look_z * move_speed;
    }

    // 左右移动(A/D):垂直于视线方向
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
        camera_x += look_z * move_speed;
        camera_z -= look_x * move_speed;
    }
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
        camera_x -= look_z * move_speed;
        camera_z += look_x * move_speed;
    }

    // 上下移动(Q/E)
    if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) {
        camera_y += move_speed;
    }
    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) {
        camera_y -= move_speed;
    }

    // 左右旋转(方向键)
    if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
        yaw -= rotate_speed;
    }
    if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
        yaw += rotate_speed;
    }
}

void drawCube() {
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glColorPointer(3, GL_FLOAT, 0, colors);
    
    glDrawArrays(GL_QUADS, 0, 24);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
}

int main(void) {
    if (!glfwInit())
        return -1;

    window = glfwCreateWindow(width, height, "C++ OpenGL ", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glEnable(GL_DEPTH_TEST); 

    while (!glfwWindowShouldClose(window)) {
        glViewport(0, 0, width, height);
        glClearColor(0.0, 192.0/256.0, 1.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        float aspect = (float)width / (float)height;
        gluPerspective(fov, aspect, 0.1, 1000);
        
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        keyboard();

        // 计算相机看向的目标点
        float look_x, look_z;
        calculateLookDirection(look_x, look_z);
        float target_x = camera_x + look_x;
        float target_z = camera_z + look_z;
        
        gluLookAt(
            camera_x, camera_y, camera_z,
            target_x, camera_y, target_z,
            0.0f, 1.0f, 0.0f
        );

        glTranslatef(0, 0,-3);
        drawCube();

        glFlush();
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

额外说明

  • 代码中直接用yaw * M_PI / 180.0f将角度转为弧度,无需额外依赖库
  • 旋转速度和移动速度可以通过rotate_speed和move_speed调整,适配你的需求
  • 现在WASD移动会跟随相机当前朝向,左右旋转可以360°无限制,不会出现速度变慢的问题

内容的提问来源于stack exchange,提问作者Bilal Akol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 06:09:20