// main.cpp #include #include #include #include #include // For sin, cos // GLM Headers #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include // 包含 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(); // 初始水平角 (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()/2.0f), 0, cos(cameraHorizontalAngle - glm::pi()/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()/2.0f), 0, cos(cameraHorizontalAngle - glm::pi()/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()/2.0f + 0.01f, glm::pi()/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()) { cubeRotationAngle -= 2.0f * glm::pi(); } 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; }