// 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(4.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(2.0f, 2.0f, 2.0f); glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f); float lightPower = 50.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 createCheckerboardTexture(); void setupCubeBuffers(); // 立方体顶点数据 (位置, 法线, 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 createCheckerboardTexture() { const int texWidth = 64; const int texHeight = 64; GLubyte checkerboard[texHeight][texWidth][3]; // RGB for (int i = 0; i < texHeight; i++) { for (int j = 0; j < texWidth; j++) { // 创建更明显的棋盘格图案 int c = (((i / 8) % 2) == ((j / 8) % 2)) ? 255 : 100; checkerboard[i][j][0] = (GLubyte)c; checkerboard[i][j][1] = (GLubyte)c; checkerboard[i][j][2] = (GLubyte)c; } } 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_NEAREST); // 使用NEAREST以便清晰看到像素块 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); // 使用mipmap glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, checkerboard); 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); 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.1f, 0.1f, 0.2f, 0.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // glEnable(GL_CULL_FACE); // 如果需要,可以开启背面剔除 programID = LoadShaders("shaders/phong.vert", "shaders/phong.frag"); if (programID == 0) { std::cerr << "Failed to load shaders. Exiting." << std::endl; // getchar(); // 暂停查看错误 exit(-1); } MatrixID_MVP = glGetUniformLocation(programID, "MVP"); MatrixID_M = glGetUniformLocation(programID, "M"); MatrixID_V = glGetUniformLocation(programID, "V"); LightPosID = glGetUniformLocation(programID, "lightPos_worldspace"); LightColorID = glGetUniformLocation(programID, "lightColor"); LightPowerID = glGetUniformLocation(programID, "lightPower"); ViewPosID = glGetUniformLocation(programID, "viewPos_worldspace"); TextureSamplerID = glGetUniformLocation(programID, "myTextureSampler"); createCheckerboardTexture(); setupCubeBuffers(); glutSetCursor(GLUT_CURSOR_NONE); glutWarpPointer(windowWidth / 2, windowHeight / 2); // 确保在第一次鼠标移动前 lastMouseX/Y 已被正确设置 lastMouseX = windowWidth / 2; lastMouseY = windowHeight / 2; } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(programID); 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, cameraPosition + direction, 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; glUniformMatrix4fv(MatrixID_MVP, 1, GL_FALSE, &MVP_Cube[0][0]); glUniformMatrix4fv(MatrixID_M, 1, GL_FALSE, &ModelMatrixCube[0][0]); glUniformMatrix4fv(MatrixID_V, 1, GL_FALSE, &ViewMatrix[0][0]); glUniform3fv(LightPosID, 1, &lightPos[0]); glUniform3fv(LightColorID, 1, &lightColor[0]); glUniform1f(LightPowerID, lightPower); // 传递光照强度 glUniform3fv(ViewPosID, 1, &cameraPosition[0]); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, checkerboardTextureID); glUniform1i(TextureSamplerID, 0); glBindVertexArray(CubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); // 绘制光源 (使用 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); 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; }