618 lines
19 KiB
C++
618 lines
19 KiB
C++
// main.cpp
|
||
#include <GL/glew.h>
|
||
#include <GL/glut.h>
|
||
#include <iostream>
|
||
#include <vector>
|
||
#include <cmath> // For sin, cos
|
||
|
||
// GLM Headers
|
||
#define GLM_ENABLE_EXPERIMENTAL
|
||
#include <glm/glm.hpp>
|
||
#include <glm/gtc/matrix_transform.hpp>
|
||
#include <glm/gtc/type_ptr.hpp>
|
||
#include <glm/gtx/transform.hpp>
|
||
#include <glm/gtc/constants.hpp> // 包含 glm::pi
|
||
|
||
#include "shader_utils.hpp" // 我们创建的着色器加载工具
|
||
|
||
// 窗口尺寸
|
||
int windowWidth = 1024;
|
||
int windowHeight = 768;
|
||
|
||
// 着色器程序ID
|
||
GLuint programID;
|
||
// GLuint pickingProgramID; // (可选,如果实现拾取)
|
||
|
||
// Uniform ID
|
||
GLuint MatrixID_MVP, MatrixID_M, MatrixID_V;
|
||
GLuint LightPosID, LightColorID, LightPowerID, ViewPosID;
|
||
GLuint TextureSamplerID;
|
||
|
||
// VAO 和 VBO
|
||
GLuint CubeVAO, CubeVBO_vertices, CubeVBO_normals, CubeVBO_uvs;
|
||
// GLuint LightVAO; // 用于表示光源的球体 (使用 GLUT 绘制,可能不需要单独VAO)
|
||
|
||
// 纹理
|
||
GLuint checkerboardTextureID;
|
||
|
||
// 相机参数
|
||
glm::vec3 cameraPosition = glm::vec3(3.0f, 3.0f, 3.0f);
|
||
float cameraHorizontalAngle = glm::pi<float>(); // 初始水平角 (pi)
|
||
float cameraVerticalAngle = 0.0f; // 初始垂直角
|
||
float initialFoV = 45.0f;
|
||
float cameraSpeed = 3.0f; // 单位/秒
|
||
float mouseSpeed = 0.002f;
|
||
|
||
// 鼠标状态
|
||
int lastMouseX = windowWidth / 2;
|
||
int lastMouseY = windowHeight / 2;
|
||
bool mouseLeftDown = false;
|
||
|
||
// 光源属性
|
||
glm::vec3 lightPos = glm::vec3(0.0f, 0.0f, 3.0f);
|
||
glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
|
||
float lightPower = 2.0f; // 修改光照强度
|
||
|
||
// 立方体旋转
|
||
float cubeRotationAngle = 0.0f;
|
||
|
||
|
||
// 函数声明
|
||
void initGL();
|
||
void display();
|
||
void reshape(int, int);
|
||
void keyboard(unsigned char, int, int);
|
||
void mouseButton(int button, int state, int x, int y);
|
||
void mouseMotion(int x, int y);
|
||
void idle();
|
||
void createGradientTexture();
|
||
void setupCubeBuffers();
|
||
void renderText(const char* text, float x, float y, float r = 1.0f, float g = 1.0f, float b = 1.0f);
|
||
|
||
// 立方体顶点数据 (位置, 法线, UV)
|
||
// 36个顶点,因为每个面有不同法线或UV,不能共享顶点
|
||
const GLfloat cube_vertices[] = {
|
||
// Back face
|
||
-0.5f, -0.5f, -0.5f, // Bottom-left
|
||
0.5f, 0.5f, -0.5f, // top-right
|
||
0.5f, -0.5f, -0.5f, // bottom-right
|
||
0.5f, 0.5f, -0.5f, // top-right
|
||
-0.5f, -0.5f, -0.5f, // bottom-left
|
||
-0.5f, 0.5f, -0.5f, // top-left
|
||
// Front face
|
||
-0.5f, -0.5f, 0.5f, // bottom-left
|
||
0.5f, -0.5f, 0.5f, // bottom-right
|
||
0.5f, 0.5f, 0.5f, // top-right
|
||
0.5f, 0.5f, 0.5f, // top-right
|
||
-0.5f, 0.5f, 0.5f, // top-left
|
||
-0.5f, -0.5f, 0.5f, // bottom-left
|
||
// Left face
|
||
-0.5f, 0.5f, 0.5f, // top-right
|
||
-0.5f, 0.5f, -0.5f, // top-left
|
||
-0.5f, -0.5f, -0.5f, // bottom-left
|
||
-0.5f, -0.5f, -0.5f, // bottom-left
|
||
-0.5f, -0.5f, 0.5f, // bottom-right
|
||
-0.5f, 0.5f, 0.5f, // top-right
|
||
// Right face
|
||
0.5f, 0.5f, 0.5f, // top-left
|
||
0.5f, -0.5f, -0.5f, // bottom-right
|
||
0.5f, 0.5f, -0.5f, // top-right
|
||
0.5f, -0.5f, -0.5f, // bottom-right
|
||
0.5f, 0.5f, 0.5f, // top-left
|
||
0.5f, -0.5f, 0.5f, // bottom-left
|
||
// Bottom face
|
||
-0.5f, -0.5f, -0.5f, // top-right
|
||
0.5f, -0.5f, -0.5f, // top-left
|
||
0.5f, -0.5f, 0.5f, // bottom-left
|
||
0.5f, -0.5f, 0.5f, // bottom-left
|
||
-0.5f, -0.5f, 0.5f, // bottom-right
|
||
-0.5f, -0.5f, -0.5f, // top-right
|
||
// Top face
|
||
-0.5f, 0.5f, -0.5f, // top-left
|
||
-0.5f, 0.5f, 0.5f, // bottom-left
|
||
0.5f, 0.5f, 0.5f, // bottom-right
|
||
0.5f, 0.5f, 0.5f, // bottom-right
|
||
0.5f, 0.5f, -0.5f, // top-right
|
||
-0.5f, 0.5f, -0.5f, // top-left
|
||
};
|
||
|
||
const GLfloat cube_normals[] = {
|
||
// Back face
|
||
0.0f, 0.0f, -1.0f,
|
||
0.0f, 0.0f, -1.0f,
|
||
0.0f, 0.0f, -1.0f,
|
||
0.0f, 0.0f, -1.0f,
|
||
0.0f, 0.0f, -1.0f,
|
||
0.0f, 0.0f, -1.0f,
|
||
// Front face
|
||
0.0f, 0.0f, 1.0f,
|
||
0.0f, 0.0f, 1.0f,
|
||
0.0f, 0.0f, 1.0f,
|
||
0.0f, 0.0f, 1.0f,
|
||
0.0f, 0.0f, 1.0f,
|
||
0.0f, 0.0f, 1.0f,
|
||
// Left face
|
||
-1.0f, 0.0f, 0.0f,
|
||
-1.0f, 0.0f, 0.0f,
|
||
-1.0f, 0.0f, 0.0f,
|
||
-1.0f, 0.0f, 0.0f,
|
||
-1.0f, 0.0f, 0.0f,
|
||
-1.0f, 0.0f, 0.0f,
|
||
// Right face
|
||
1.0f, 0.0f, 0.0f,
|
||
1.0f, 0.0f, 0.0f,
|
||
1.0f, 0.0f, 0.0f,
|
||
1.0f, 0.0f, 0.0f,
|
||
1.0f, 0.0f, 0.0f,
|
||
1.0f, 0.0f, 0.0f,
|
||
// Bottom face
|
||
0.0f, -1.0f, 0.0f,
|
||
0.0f, -1.0f, 0.0f,
|
||
0.0f, -1.0f, 0.0f,
|
||
0.0f, -1.0f, 0.0f,
|
||
0.0f, -1.0f, 0.0f,
|
||
0.0f, -1.0f, 0.0f,
|
||
// Top face
|
||
0.0f, 1.0f, 0.0f,
|
||
0.0f, 1.0f, 0.0f,
|
||
0.0f, 1.0f, 0.0f,
|
||
0.0f, 1.0f, 0.0f,
|
||
0.0f, 1.0f, 0.0f,
|
||
0.0f, 1.0f, 0.0f,
|
||
};
|
||
|
||
const GLfloat cube_uvs[] = {
|
||
// Back face
|
||
0.0f, 0.0f,
|
||
1.0f, 1.0f,
|
||
1.0f, 0.0f,
|
||
1.0f, 1.0f,
|
||
0.0f, 0.0f,
|
||
0.0f, 1.0f,
|
||
// Front face
|
||
0.0f, 0.0f,
|
||
1.0f, 0.0f,
|
||
1.0f, 1.0f,
|
||
1.0f, 1.0f,
|
||
0.0f, 1.0f,
|
||
0.0f, 0.0f,
|
||
// Left face
|
||
1.0f, 1.0f,
|
||
0.0f, 1.0f,
|
||
0.0f, 0.0f,
|
||
0.0f, 0.0f,
|
||
1.0f, 0.0f,
|
||
1.0f, 1.0f,
|
||
// Right face
|
||
1.0f, 1.0f,
|
||
0.0f, 0.0f,
|
||
1.0f, 0.0f,
|
||
0.0f, 0.0f,
|
||
1.0f, 1.0f,
|
||
0.0f, 1.0f,
|
||
// Bottom face
|
||
0.0f, 1.0f,
|
||
1.0f, 1.0f,
|
||
1.0f, 0.0f,
|
||
1.0f, 0.0f,
|
||
0.0f, 0.0f,
|
||
0.0f, 1.0f,
|
||
// Top face
|
||
0.0f, 1.0f,
|
||
0.0f, 0.0f,
|
||
1.0f, 0.0f,
|
||
1.0f, 0.0f,
|
||
1.0f, 1.0f,
|
||
0.0f, 1.0f,
|
||
};
|
||
|
||
|
||
void createGradientTexture() {
|
||
const int texWidth = 128;
|
||
const int texHeight = 128;
|
||
GLubyte gradientTexture[texHeight][texWidth][3]; // RGB
|
||
|
||
for (int i = 0; i < texHeight; i++) {
|
||
for (int j = 0; j < texWidth; j++) {
|
||
// 创建漂亮的蓝紫色渐变
|
||
float u = (float)j / texWidth;
|
||
float v = (float)i / texHeight;
|
||
|
||
GLubyte r = (GLubyte)(180 + 75 * sin(u * 6.28));
|
||
GLubyte g = (GLubyte)(100 + 50 * sin(v * 6.28));
|
||
GLubyte b = (GLubyte)(200 + 55 * cos(u * v * 10));
|
||
|
||
gradientTexture[i][j][0] = r;
|
||
gradientTexture[i][j][1] = g;
|
||
gradientTexture[i][j][2] = b;
|
||
}
|
||
}
|
||
|
||
glGenTextures(1, &checkerboardTextureID);
|
||
glBindTexture(GL_TEXTURE_2D, checkerboardTextureID);
|
||
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||
|
||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, gradientTexture);
|
||
glGenerateMipmap(GL_TEXTURE_2D);
|
||
|
||
glBindTexture(GL_TEXTURE_2D, 0);
|
||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||
}
|
||
|
||
void setupCubeBuffers() {
|
||
glGenVertexArrays(1, &CubeVAO);
|
||
glBindVertexArray(CubeVAO);
|
||
|
||
// 顶点位置
|
||
glGenBuffers(1, &CubeVBO_vertices);
|
||
glBindBuffer(GL_ARRAY_BUFFER, CubeVBO_vertices);
|
||
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW);
|
||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
|
||
glEnableVertexAttribArray(0);
|
||
|
||
// 法线
|
||
glGenBuffers(1, &CubeVBO_normals);
|
||
glBindBuffer(GL_ARRAY_BUFFER, CubeVBO_normals);
|
||
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_normals), cube_normals, GL_STATIC_DRAW);
|
||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
|
||
glEnableVertexAttribArray(1);
|
||
|
||
// UV坐标
|
||
glGenBuffers(1, &CubeVBO_uvs);
|
||
glBindBuffer(GL_ARRAY_BUFFER, CubeVBO_uvs);
|
||
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_uvs), cube_uvs, GL_STATIC_DRAW);
|
||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
|
||
glEnableVertexAttribArray(2);
|
||
|
||
// 解绑VAO
|
||
glBindVertexArray(0);
|
||
}
|
||
|
||
|
||
void initGL() {
|
||
glewExperimental = GL_TRUE;
|
||
if (glewInit() != GLEW_OK) {
|
||
std::cerr << "Failed to initialize GLEW" << std::endl;
|
||
exit(-1);
|
||
}
|
||
std::cout << "Using GLEW Version: " << glewGetString(GLEW_VERSION) << std::endl;
|
||
std::cout << "OpenGL Renderer: " << glGetString(GL_RENDERER) << std::endl;
|
||
std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
|
||
std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
|
||
|
||
// 修改清除颜色为浅灰色,这样如果渲染失败更容易看出来
|
||
glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
|
||
|
||
// 确保深度测试正确设置
|
||
glEnable(GL_DEPTH_TEST);
|
||
glDepthFunc(GL_LESS);
|
||
glDepthMask(GL_TRUE);
|
||
|
||
// 禁用背面剔除,这样我们可以看到立方体的所有面
|
||
glDisable(GL_CULL_FACE);
|
||
|
||
programID = LoadShaders("shaders/phong.vert", "shaders/phong.frag");
|
||
if (programID == 0) {
|
||
std::cerr << "Failed to load shaders. Exiting." << std::endl;
|
||
exit(-1);
|
||
}
|
||
|
||
// 添加着色器编译状态检查
|
||
GLint success;
|
||
GLchar infoLog[512];
|
||
glGetProgramiv(programID, GL_LINK_STATUS, &success);
|
||
if (!success) {
|
||
glGetProgramInfoLog(programID, 512, NULL, infoLog);
|
||
std::cerr << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
|
||
}
|
||
|
||
// 检查uniform变量位置
|
||
MatrixID_MVP = glGetUniformLocation(programID, "MVP");
|
||
if (MatrixID_MVP == -1) std::cerr << "MVP uniform not found" << std::endl;
|
||
MatrixID_M = glGetUniformLocation(programID, "M");
|
||
if (MatrixID_M == -1) std::cerr << "M uniform not found" << std::endl;
|
||
MatrixID_V = glGetUniformLocation(programID, "V");
|
||
if (MatrixID_V == -1) std::cerr << "V uniform not found" << std::endl;
|
||
LightPosID = glGetUniformLocation(programID, "lightPos_worldspace");
|
||
if (LightPosID == -1) std::cerr << "lightPos_worldspace uniform not found" << std::endl;
|
||
LightColorID = glGetUniformLocation(programID, "lightColor");
|
||
if (LightColorID == -1) std::cerr << "lightColor uniform not found" << std::endl;
|
||
LightPowerID = glGetUniformLocation(programID, "lightPower");
|
||
if (LightPowerID == -1) std::cerr << "lightPower uniform not found" << std::endl;
|
||
ViewPosID = glGetUniformLocation(programID, "viewPos");
|
||
if (ViewPosID == -1) std::cerr << "viewPos uniform not found" << std::endl;
|
||
TextureSamplerID = glGetUniformLocation(programID, "myTextureSampler");
|
||
if (TextureSamplerID == -1) std::cerr << "myTextureSampler uniform not found" << std::endl;
|
||
|
||
createGradientTexture();
|
||
setupCubeBuffers();
|
||
|
||
glutSetCursor(GLUT_CURSOR_NONE);
|
||
glutWarpPointer(windowWidth / 2, windowHeight / 2);
|
||
lastMouseX = windowWidth / 2;
|
||
lastMouseY = windowHeight / 2;
|
||
}
|
||
|
||
void renderText(const char* text, float x, float y, float r, float g, float b) {
|
||
glUseProgram(0); // 禁用着色器
|
||
glMatrixMode(GL_PROJECTION);
|
||
glPushMatrix();
|
||
glLoadIdentity();
|
||
gluOrtho2D(0, windowWidth, 0, windowHeight);
|
||
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glPushMatrix();
|
||
glLoadIdentity();
|
||
|
||
glDisable(GL_DEPTH_TEST); // 禁用深度测试以便文字总是在前面
|
||
|
||
glColor3f(r, g, b);
|
||
glRasterPos2f(x, y);
|
||
|
||
for (const char* c = text; *c != '\0'; c++) {
|
||
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *c);
|
||
}
|
||
|
||
glEnable(GL_DEPTH_TEST); // 恢复深度测试
|
||
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glPopMatrix();
|
||
|
||
glMatrixMode(GL_PROJECTION);
|
||
glPopMatrix();
|
||
}
|
||
|
||
void display() {
|
||
// 检查OpenGL错误
|
||
GLenum err;
|
||
while((err = glGetError()) != GL_NO_ERROR) {
|
||
std::cerr << "OpenGL error before rendering: " << err << std::endl;
|
||
}
|
||
|
||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||
|
||
// 检查深度测试状态
|
||
GLboolean depthTestEnabled;
|
||
glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
|
||
if (!depthTestEnabled) {
|
||
std::cerr << "深度测试未启用" << std::endl;
|
||
}
|
||
|
||
// 先绑定着色器程序
|
||
glUseProgram(programID);
|
||
|
||
// 检查着色器程序绑定状态
|
||
GLint currentProgram;
|
||
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
|
||
if (currentProgram != programID) {
|
||
std::cerr << "着色器程序未正确绑定" << std::endl;
|
||
return;
|
||
}
|
||
|
||
glm::vec3 direction(
|
||
cos(cameraVerticalAngle) * sin(cameraHorizontalAngle),
|
||
sin(cameraVerticalAngle),
|
||
cos(cameraVerticalAngle) * cos(cameraHorizontalAngle)
|
||
);
|
||
glm::vec3 right = glm::vec3(
|
||
sin(cameraHorizontalAngle - glm::pi<float>()/2.0f),
|
||
0,
|
||
cos(cameraHorizontalAngle - glm::pi<float>()/2.0f)
|
||
);
|
||
glm::vec3 up = glm::cross(right, direction);
|
||
|
||
glm::mat4 ProjectionMatrix = glm::perspective(glm::radians(initialFoV), (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);
|
||
glm::mat4 ViewMatrix = glm::lookAt(
|
||
cameraPosition,
|
||
glm::vec3(0.0f, 0.0f, 0.0f), // 看向原点
|
||
up
|
||
);
|
||
|
||
glm::mat4 ModelMatrixCube = glm::mat4(1.0);
|
||
ModelMatrixCube = glm::translate(ModelMatrixCube, glm::vec3(0.0f, 0.0f, 0.0f));
|
||
ModelMatrixCube = glm::rotate(ModelMatrixCube, cubeRotationAngle, glm::vec3(0.0f, 1.0f, 0.0f));
|
||
ModelMatrixCube = glm::scale(ModelMatrixCube, glm::vec3(1.0f, 1.0f, 1.0f));
|
||
|
||
glm::mat4 MVP_Cube = ProjectionMatrix * ViewMatrix * ModelMatrixCube;
|
||
|
||
// 设置uniform变量
|
||
if (MatrixID_MVP != -1) {
|
||
glUniformMatrix4fv(MatrixID_MVP, 1, GL_FALSE, &MVP_Cube[0][0]);
|
||
}
|
||
if (MatrixID_M != -1) {
|
||
glUniformMatrix4fv(MatrixID_M, 1, GL_FALSE, &ModelMatrixCube[0][0]);
|
||
}
|
||
if (MatrixID_V != -1) {
|
||
glUniformMatrix4fv(MatrixID_V, 1, GL_FALSE, &ViewMatrix[0][0]);
|
||
}
|
||
if (LightPosID != -1) {
|
||
glUniform3fv(LightPosID, 1, &lightPos[0]);
|
||
}
|
||
if (LightColorID != -1) {
|
||
glUniform3fv(LightColorID, 1, &lightColor[0]);
|
||
}
|
||
if (LightPowerID != -1) {
|
||
glUniform1f(LightPowerID, lightPower);
|
||
}
|
||
if (ViewPosID != -1) {
|
||
glUniform3fv(ViewPosID, 1, &cameraPosition[0]);
|
||
}
|
||
|
||
// 检查纹理绑定状态
|
||
GLint textureBound;
|
||
glActiveTexture(GL_TEXTURE0);
|
||
glBindTexture(GL_TEXTURE_2D, checkerboardTextureID);
|
||
if (TextureSamplerID != -1) {
|
||
glUniform1i(TextureSamplerID, 0);
|
||
}
|
||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBound);
|
||
if (textureBound != checkerboardTextureID) {
|
||
std::cerr << "纹理未正确绑定" << std::endl;
|
||
}
|
||
|
||
// 先绑定VAO
|
||
glBindVertexArray(CubeVAO);
|
||
|
||
// 检查VAO绑定状态
|
||
GLint vaoBound;
|
||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vaoBound);
|
||
if (vaoBound != CubeVAO) {
|
||
std::cerr << "VAO未正确绑定" << std::endl;
|
||
return;
|
||
}
|
||
|
||
// 绘制
|
||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||
|
||
// 解绑VAO
|
||
glBindVertexArray(0);
|
||
|
||
// 检查绘制后的OpenGL错误
|
||
while((err = glGetError()) != GL_NO_ERROR) {
|
||
std::cerr << "OpenGL error after rendering: " << err << std::endl;
|
||
}
|
||
|
||
// 绘制光源 (使用 GLUT 绘制一个白色小球)
|
||
glUseProgram(0);
|
||
glMatrixMode(GL_PROJECTION);
|
||
glLoadMatrixf(glm::value_ptr(ProjectionMatrix));
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glm::mat4 MV_Light = ViewMatrix * glm::translate(glm::mat4(1.0f), lightPos) * glm::scale(glm::mat4(1.0f), glm::vec3(0.1f));
|
||
glLoadMatrixf(glm::value_ptr(MV_Light));
|
||
|
||
glColor3f(lightColor.r, lightColor.g, lightColor.b);
|
||
glutSolidSphere(0.1, 10, 10);
|
||
|
||
// 渲染操作说明文字
|
||
renderText("操作说明:", 10, windowHeight - 20, 1.0f, 1.0f, 0.0f);
|
||
renderText("W/S: 前进/后退", 10, windowHeight - 40);
|
||
renderText("A/D: 左移/右移", 10, windowHeight - 60);
|
||
renderText("空格/C: 上升/下降", 10, windowHeight - 80);
|
||
renderText("鼠标左键拖拽: 视角旋转", 10, windowHeight - 100);
|
||
renderText("ESC: 退出程序", 10, windowHeight - 120);
|
||
|
||
glutSwapBuffers();
|
||
}
|
||
|
||
void reshape(int w, int h) {
|
||
windowWidth = w;
|
||
windowHeight = h;
|
||
if (h == 0) h = 1;
|
||
glViewport(0, 0, w, h);
|
||
glutPostRedisplay();
|
||
}
|
||
|
||
void keyboard(unsigned char key, int x, int y) {
|
||
float deltaTime = 0.016f; // 假设大约60FPS, 更精确的delta time计算会更好
|
||
|
||
glm::vec3 direction(
|
||
cos(cameraVerticalAngle) * sin(cameraHorizontalAngle),
|
||
sin(cameraVerticalAngle),
|
||
cos(cameraVerticalAngle) * cos(cameraHorizontalAngle)
|
||
);
|
||
glm::vec3 right = glm::vec3(
|
||
sin(cameraHorizontalAngle - glm::pi<float>()/2.0f),
|
||
0,
|
||
cos(cameraHorizontalAngle - glm::pi<float>()/2.0f)
|
||
);
|
||
|
||
switch (key) {
|
||
case 'w': case 'W': cameraPosition += direction * deltaTime * cameraSpeed; break;
|
||
case 's': case 'S': cameraPosition -= direction * deltaTime * cameraSpeed; break;
|
||
case 'a': case 'A': cameraPosition -= right * deltaTime * cameraSpeed; break;
|
||
case 'd': case 'D': cameraPosition += right * deltaTime * cameraSpeed; break;
|
||
case ' ': cameraPosition.y += deltaTime * cameraSpeed; break; // 空格键向上
|
||
case 'c': case 'C': cameraPosition.y -= deltaTime * cameraSpeed; break; // C键向下
|
||
case 27: // ESC 键
|
||
glDeleteBuffers(1, &CubeVBO_vertices);
|
||
glDeleteBuffers(1, &CubeVBO_normals);
|
||
glDeleteBuffers(1, &CubeVBO_uvs);
|
||
glDeleteVertexArrays(1, &CubeVAO);
|
||
glDeleteProgram(programID);
|
||
glDeleteTextures(1, &checkerboardTextureID);
|
||
exit(0);
|
||
break;
|
||
}
|
||
glutPostRedisplay();
|
||
}
|
||
|
||
void mouseButton(int button, int state, int x, int y) {
|
||
if (button == GLUT_LEFT_BUTTON) {
|
||
if (state == GLUT_DOWN) {
|
||
mouseLeftDown = true;
|
||
lastMouseX = x;
|
||
lastMouseY = y;
|
||
} else if (state == GLUT_UP) {
|
||
mouseLeftDown = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
void mouseMotion(int x, int y) {
|
||
// 仅当鼠标在窗口内时处理,避免跳动
|
||
if (x < 0 || x >= windowWidth || y < 0 || y >= windowHeight) {
|
||
// 当鼠标移出窗口时,可以选择不更新视角或者将鼠标重置回中心
|
||
// 如果选择重置,可能需要更复杂的逻辑来避免在窗口边缘的抖动
|
||
// mouseLeftDown = false; // 取消注释此行可以在鼠标移出时停止拖动视角
|
||
return;
|
||
}
|
||
|
||
if (mouseLeftDown) {
|
||
float deltaX = float(x - lastMouseX);
|
||
float deltaY = float(y - lastMouseY);
|
||
|
||
cameraHorizontalAngle += mouseSpeed * deltaX;
|
||
cameraVerticalAngle += mouseSpeed * deltaY;
|
||
|
||
// 限制垂直角度
|
||
cameraVerticalAngle = glm::clamp(cameraVerticalAngle, -glm::pi<float>()/2.0f + 0.01f, glm::pi<float>()/2.0f - 0.01f);
|
||
|
||
lastMouseX = x;
|
||
lastMouseY = y;
|
||
|
||
// FPS游戏风格的鼠标控制 (可选):
|
||
// if (x != windowWidth/2 || y != windowHeight/2) {
|
||
// glutWarpPointer(windowWidth/2, windowHeight/2);
|
||
// lastMouseX = windowWidth/2;
|
||
// lastMouseY = windowHeight/2;
|
||
// }
|
||
glutPostRedisplay();
|
||
}
|
||
}
|
||
|
||
|
||
void idle() {
|
||
cubeRotationAngle += 0.005f;
|
||
if (cubeRotationAngle > 2.0f * glm::pi<float>()) {
|
||
cubeRotationAngle -= 2.0f * glm::pi<float>();
|
||
}
|
||
glutPostRedisplay();
|
||
}
|
||
|
||
int main(int argc, char** argv) {
|
||
glutInit(&argc, argv);
|
||
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
|
||
glutInitWindowSize(windowWidth, windowHeight);
|
||
glutInitWindowPosition(100, 100);
|
||
glutCreateWindow("OpenGL Course Project - 3D Scene");
|
||
|
||
initGL();
|
||
|
||
glutDisplayFunc(display);
|
||
glutReshapeFunc(reshape);
|
||
glutKeyboardFunc(keyboard);
|
||
glutMouseFunc(mouseButton);
|
||
glutMotionFunc(mouseMotion);
|
||
// glutPassiveMotionFunc(mouseMotion); // 如果希望鼠标不按下也触发 warp (如果使用)
|
||
glutIdleFunc(idle);
|
||
|
||
glutMainLoop();
|
||
|
||
return 0;
|
||
}
|