#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <vector>
#include <string>
// 窗口尺寸
const unsigned int SCR_WIDTH = 1280;
const unsigned int SCR_HEIGHT = 720;
// 菜单栏常量
const int MENU_HEIGHT = 30;
const int MENU_ITEM_WIDTH = 100;
// 相机参数
glm::vec3 cameraPos = glm::vec3(0.0f, 50.0f, 150.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
float yaw = -90.0f; // 偏航角
float pitch = 0.0f; // 俯仰角
float fov = 75.0f; // 视野角
bool firstMouse = true;
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
float deltaTime = 0.0f;
float lastFrame = 0.0f;
bool mousePressed = false;
bool isDragging = false;
glm::vec2 dragStart;
// 菜单状态
enum MenuItem {
MENU_NONE,
MENU_FILE,
MENU_VIEW,
MENU_HELP
};
enum FileSubmenu {
FILE_NONE,
FILE_NEW,
FILE_EXIT
};
enum ViewSubmenu {
VIEW_NONE,
VIEW_RESET_CAMERA,
VIEW_WIREFRAME,
VIEW_SOLID
};
MenuItem activeMenu = MENU_NONE;
FileSubmenu activeFileSubmenu = FILE_NONE;
ViewSubmenu activeViewSubmenu = VIEW_NONE;
bool wireframeMode = false;
// 物体选择相关
enum SelectedObject {
SELECT_NONE,
SELECT_DAM,
SELECT_WATER,
SELECT_MOUNTAIN
};
SelectedObject selectedObject = SELECT_NONE;
std::string objectInfo = "未选择任何物体";
// 鼠标回调函数
void mouseCallback(GLFWwindow* window, double xpos, double ypos) {
// 检查是否在菜单栏区域
if (ypos < MENU_HEIGHT) {
// 处理菜单鼠标移动
int x = static_cast<int>(xpos);
int y = static_cast<int>(ypos);
if (mousePressed) {
// 处理菜单点击
if (x < MENU_ITEM_WIDTH) {
activeMenu = MENU_FILE;
activeViewSubmenu = VIEW_NONE;
} else if (x < 2 * MENU_ITEM_WIDTH) {
activeMenu = MENU_VIEW;
activeFileSubmenu = FILE_NONE;
} else if (x < 3 * MENU_ITEM_WIDTH) {
activeMenu = MENU_HELP;
activeFileSubmenu = FILE_NONE;
activeViewSubmenu = VIEW_NONE;
} else {
activeMenu = MENU_NONE;
activeFileSubmenu = FILE_NONE;
activeViewSubmenu = VIEW_NONE;
}
}
return;
}
// 处理3D场景鼠标交互
if (firstMouse) {
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
if (!isDragging) {
isDragging = true;
dragStart = glm::vec2(xpos, ypos);
} else {
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
if (pitch > 89.0f) pitch = 89.0f;
if (pitch < -89.0f) pitch = -89.0f;
glm::vec3 front;
front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(pitch));
front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
cameraFront = glm::normalize(front);
}
} else {
if (isDragging) {
isDragging = false;
// 检测点击选择(简化版)
glm::vec2 dragEnd(xpos, ypos);
if (glm::length(dragEnd - dragStart) < 5.0f) {
// 这里使用简单的位置判断模拟射线检测
float clickX = (2.0f * xpos) / SCR_WIDTH - 1.0f;
float clickY = 1.0f - (2.0f * ypos) / SCR_HEIGHT;
// 简化的物体选择逻辑
if (clickY > -0.5f && clickY < 0.5f && clickX > -0.3f && clickX < 0.3f) {
selectedObject = SELECT_DAM;
objectInfo = "大坝: 混凝土结构,高度50米";
} else if (clickY < -0.2f && clickX > -0.6f && clickX < 0.6f) {
selectedObject = SELECT_WATER;
objectInfo = "水体: 库容约500万立方米";
} else if ((clickX < -0.5f || clickX > 0.5f) && clickY < 0.3f) {
selectedObject = SELECT_MOUNTAIN;
objectInfo = "山体: 岩石结构,高度80米";
} else {
selectedObject = SELECT_NONE;
objectInfo = "未选择任何物体";
}
}
}
firstMouse = true;
}
}
// 鼠标按钮回调
void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (action == GLFW_PRESS) {
mousePressed = true;
} else if (action == GLFW_RELEASE) {
mousePressed = false;
// 检查子菜单点击
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
int x = static_cast<int>(xpos);
int y = static_cast<int>(ypos);
if (activeMenu == MENU_FILE && y > MENU_HEIGHT && y < MENU_HEIGHT + 30 * 2) {
if (x < MENU_ITEM_WIDTH) {
if (y < MENU_HEIGHT + 30) {
activeFileSubmenu = FILE_NEW;
// 重置场景
cameraPos = glm::vec3(0.0f, 50.0f, 150.0f);
cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
yaw = -90.0f;
pitch = 0.0f;
fov = 75.0f;
selectedObject = SELECT_NONE;
objectInfo = "未选择任何物体";
} else {
activeFileSubmenu = FILE_EXIT;
glfwSetWindowShouldClose(window, true);
}
}
} else if (activeMenu == MENU_VIEW && y > MENU_HEIGHT && y < MENU_HEIGHT + 30 * 3) {
if (x < MENU_ITEM_WIDTH) {
if (y < MENU_HEIGHT + 30) {
activeViewSubmenu = VIEW_RESET_CAMERA;
cameraPos = glm::vec3(0.0f, 50.0f, 150.0f);
cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
yaw = -90.0f;
pitch = 0.0f;
fov = 75.0f;
} else if (y < MENU_HEIGHT + 60) {
activeViewSubmenu = VIEW_WIREFRAME;
wireframeMode = true;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
activeViewSubmenu = VIEW_SOLID;
wireframeMode = false;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
}
// 点击其他区域关闭菜单
if (y > MENU_HEIGHT + 30 || x > MENU_ITEM_WIDTH * (activeMenu == MENU_FILE ? 1 : 1)) {
activeMenu = MENU_NONE;
activeFileSubmenu = FILE_NONE;
activeViewSubmenu = VIEW_NONE;
}
}
}
}
// 滚轮回调函数
void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
fov -= (float)yoffset;
if (fov < 1.0f) fov = 1.0f;
if (fov > 120.0f) fov = 120.0f;
}
// 处理键盘输入
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
float cameraSpeed = 2.5f * deltaTime;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cameraPos += cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cameraPos -= cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
cameraPos += cameraSpeed * cameraUp;
if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
cameraPos -= cameraSpeed * cameraUp;
}
// 编译着色器
unsigned int compileShader(unsigned int type, const char* source) {
unsigned int id = glCreateShader(type);
glShaderSource(id, 1, &source, nullptr);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) {
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << " shader!" << std::endl;
std::cout << message << std::endl;
glDeleteShader(id);
return 0;
}
return id;
}
// 创建着色器程序
unsigned int createShaderProgram(const char* vertexSource, const char* fragmentSource) {
unsigned int vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
unsigned int fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
unsigned int program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
int result;
glGetProgramiv(program, GL_LINK_STATUS, &result);
if (result == GL_FALSE) {
int length;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetProgramInfoLog(program, length, &length, message);
std::cout << "Failed to link shader program!" << std::endl;
std::cout << message << std::endl;
glDeleteProgram(program);
return 0;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
// 创建立方体
unsigned int createCube(float width, float height, float depth, const glm::vec3& color) {
float w = width / 2.0f;
float h = height / 2.0f;
float d = depth / 2.0f;
float vertices[] = {
// 位置 // 颜色
-w, -h, -d, color.r, color.g, color.b,
w, -h, -d, color.r, color.g, color.b,
w, h, -d, color.r, color.g, color.b,
w, h, -d, color.r, color.g, color.b,
-w, h, -d, color.r, color.g, color.b,
-w, -h, -d, color.r, color.g, color.b,
-w, -h, d, color.r, color.g, color.b,
w, -h, d, color.r, color.g, color.b,
w, h, d, color.r, color.g, color.b,
w, h, d, color.r, color.g, color.b,
-w, h, d, color.r, color.g, color.b,
-w, -h, d, color.r, color.g, color.b,
-w, h, d, color.r, color.g, color.b,
-w, h, -d, color.r, color.g, color.b,
-w, -h, -d, color.r, color.g, color.b,
-w, -h, -d, color.r, color.g, color.b,
-w, -h, d, color.r, color.g, color.b,
-w, h, d, color.r, color.g, color.b,
w, h, d, color.r, color.g, color.b,
w, h, -d, color.r, color.g, color.b,
w, -h, -d, color.r, color.g, color.b,
w, -h, -d, color.r, color.g, color.b,
w, -h, d, color.r, color.g, color.b,
w, h, d, color.r, color.g, color.b,
-w, -h, -d, color.r, color.g, color.b,
w, -h, -d, color.r, color.g, color.b,
w, -h, d, color.r, color.g, color.b,
w, -h, d, color.r, color.g, color.b,
-w, -h, d, color.r, color.g, color.b,
-w, -h, -d, color.r, color.g, color.b,
-w, h, -d, color.r, color.g, color.b,
w, h, -d, color.r, color.g, color.b,
w, h, d, color.r, color.g, color.b,
w, h, d, color.r, color.g, color.b,
-w, h, d, color.r, color.g, color.b,
-w, h, -d, color.r, color.g, color.b
};
unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
return VAO;
}
// 创建平面
unsigned int createPlane(float width, float height, const glm::vec3& color) {
float w = width / 2.0f;
float h = height / 2.0f;
float vertices[] = {
// 位置 // 颜色
-w, 0.0f, -h, color.r, color.g, color.b,
w, 0.0f, -h, color.r, color.g, color.b,
w, 0.0f, h, color.r, color.g, color.b,
w, 0.0f, h, color.r, color.g, color.b,
-w, 0.0f, h, color.r, color.g, color.b,
-w, 0.0f, -h, color.r, color.g, color.b
};
unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
return VAO;
}
// 创建锥体(简化版山体)
unsigned int createCone(float radius, float height, int segments, const glm::vec3& color) {
std::vector<float> vertices;
// 底部中心
vertices.insert(vertices.end(), {0.0f, 0.0f, 0.0f, color.r, color.g, color.b});
// 底部边缘
for (int i = 0; i < segments; i++) {
float angle = 2.0f * glm::pi<float>() * (float)i / (float)segments;
float x = radius * cos(angle);
float z = radius * sin(angle);
vertices.insert(vertices.end(), {x, 0.0f, z, color.r, color.g, color.b});
}
// 顶部点
vertices.insert(vertices.end(), {0.0f, height, 0.0f, color.r, color.g, color.b});
// 底部三角形
for (int i = 0; i < segments; i++) {
int next = (i + 1) % segments;
vertices.insert(vertices.end(), {
0.0f, 0.0f, 0.0f, color.r, color.g, color.b,
radius * cos(2.0f * glm::pi<float>() * (float)next / (float)segments), 0.0f, radius * sin(2.0f * glm::pi<float>() * (float)next / (float)segments), color.r, color.g, color.b,
radius * cos(2.0f * glm::pi<float>() * (float)i / (float)segments), 0.0f, radius * sin(2.0f * glm::pi<float>() * (float)i / (float)segments), color.r, color.g, color.b
});
}
// 侧面三角形
for (int i = 0; i < segments; i++) {
int next = (i + 1) % segments;
vertices.insert(vertices.end(), {
0.0f, height, 0.0f, color.r, color.g, color.b,
radius * cos(2.0f * glm::pi<float>() * (float)i / (float)segments), 0.0f, radius * sin(2.0f * glm::pi<float>() * (float)i / (float)segments), color.r, color.g, color.b,
radius * cos(2.0f * glm::pi<float>() * (float)next / (float)segments), 0.0f, radius * sin(2.0f * glm::pi<float>() * (float)next / (float)segments), color.r, color.g, color.b
});
}
unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
return VAO;
}
// 渲染2D矩形(用于菜单)
void renderRect(float x, float y, float width, float height, const glm::vec3& color, unsigned int shaderProgram) {
float vertices[] = {
x, y, 0.0f, color.r, color.g, color.b,
x + width, y, 0.0f, color.r, color.g, color.b,
x + width, y + height, 0.0f, color.r, color.g, color.b,
x + width, y + height, 0.0f, color.r, color.g, color.b,
x, y + height, 0.0f, color.r, color.g, color.b,
x, y, 0.0f, color.r, color.g, color.b
};
unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
}
// 渲染菜单
void renderMenu(unsigned int shaderProgram) {
// 保存当前矩阵状态
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, SCR_WIDTH, SCR_HEIGHT, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// 禁用深度测试以正确显示UI
glDisable(GL_DEPTH_TEST);
// 渲染主菜单条
renderRect(0, 0, SCR_WIDTH, MENU_HEIGHT, glm::vec3(0.3f, 0.3f, 0.3f), shaderProgram);
// 渲染菜单项
renderRect(0, 0, MENU_ITEM_WIDTH, MENU_HEIGHT, glm::vec3(0.4f, 0.4f, 0.4f), shaderProgram);
renderRect(MENU_ITEM_WIDTH, 0, MENU_ITEM_WIDTH, MENU_HEIGHT, glm::vec3(0.4f, 0.4f, 0.4f), shaderProgram);
renderRect(2 * MENU_ITEM_WIDTH, 0, MENU_ITEM_WIDTH, MENU_HEIGHT, glm::vec3(0.4f, 0.4f, 0.4f), shaderProgram);
// 恢复着色器程序的模型矩阵
glm::mat4 model = glm::mat4(1.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glm::mat4 view = glm::mat4(1.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
glm::mat4 projection = glm::ortho(0.0f, (float)SCR_WIDTH, (float)SCR_HEIGHT, 0.0f, -1.0f, 1.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// 渲染文件子菜单
if (activeMenu == MENU_FILE) {
renderRect(0, MENU_HEIGHT, MENU_ITEM_WIDTH, 60, glm::vec3(0.4f, 0.4f, 0.4f), shaderProgram);
renderRect(0, MENU_HEIGHT, MENU_ITEM_WIDTH, 30, glm::vec3(0.5f, 0.5f, 0.5f), shaderProgram);
renderRect(0, MENU_HEIGHT + 30, MENU_ITEM_WIDTH, 30, glm::vec3(0.5f, 0.5f, 0.5f), shaderProgram);
}
// 渲染视图子菜单
if (activeMenu == MENU_VIEW) {
renderRect(MENU_ITEM_WIDTH, MENU_HEIGHT, MENU_ITEM_WIDTH, 90, glm::vec3(0.4f, 0.4f, 0.4f), shaderProgram);
renderRect(MENU_ITEM_WIDTH, MENU_HEIGHT, MENU_ITEM_WIDTH, 30, glm::vec3(0.5f, 0.5f, 0.5f), shaderProgram);
renderRect(MENU_ITEM_WIDTH, MENU_HEIGHT + 30, MENU_ITEM_WIDTH, 30, glm::vec3(0.5f, 0.5f, 0.5f), shaderProgram);
renderRect(MENU_ITEM_WIDTH, MENU_HEIGHT + 60, MENU_ITEM_WIDTH, 30, glm::vec3(0.5f, 0.5f, 0.5f), shaderProgram);
}
// 渲染帮助子菜单
if (activeMenu == MENU_HELP) {
renderRect(2 * MENU_ITEM_WIDTH, MENU_HEIGHT, MENU_ITEM_WIDTH, 30, glm::vec3(0.4f, 0.4f, 0.4f), shaderProgram);
}
// 渲染物体信息面板
renderRect(SCR_WIDTH - 300, MENU_HEIGHT, 300, 30, glm::vec3(0.2f, 0.2f, 0.2f), shaderProgram);
// 恢复深度测试
glEnable(GL_DEPTH_TEST);
// 恢复矩阵状态
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
int main() {
// 初始化GLFW
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
return -1;
}
// 设置OpenGL版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 创建窗口
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "大坝3D模型", NULL, NULL);
if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// 设置回调函数
glfwSetCursorPosCallback(window, mouseCallback);
glfwSetMouseButtonCallback(window, mouseButtonCallback);
glfwSetScrollCallback(window, scrollCallback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// 初始化GLEW
if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialize GLEW" << std::endl;
return -1;
}
// 启用深度测试
glEnable(GL_DEPTH_TEST);
// 启用混合(用于半透明效果)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 顶点着色器代码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"out vec3 ourColor;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main()\n"
"{\n"
" gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
" ourColor = aColor;\n"
"}\0";
// 片段着色器代码
const char* fragmentShaderSource = "#version 330 core\n"
"in vec3 ourColor;\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(ourColor, 1.0);\n"
"}\0";
// 创建着色器程序
unsigned int shaderProgram = createShaderProgram(vertexShaderSource, fragmentShaderSource);
if (!shaderProgram) return -1;
// 创建场景元素
// 地面
unsigned int groundVAO = createPlane(500, 500, glm::vec3(0.545f, 0.271f, 0.075f)); // 棕色
// 水体
unsigned int waterVAO = createPlane(300, 200, glm::vec3(0.275f, 0.509f, 0.706f)); // 钢蓝色
// 左侧山体
unsigned int leftMountainVAO = createCone(100, 80, 4, glm::vec3(0.133f, 0.545f, 0.133f)); // 绿色
// 右侧山体
unsigned int rightMountainVAO = createCone(100, 70, 4, glm::vec3(0.133f, 0.545f, 0.133f)); // 绿色
// 远处山体
unsigned int farMountainVAO = createCone(135, 50, 4, glm::vec3(0.133f, 0.4f, 0.133f)); // 深绿色
// 大坝基础
unsigned int damBaseVAO = createCube(100, 20, 40, glm::vec3(0.533f, 0.533f, 0.533f)); // 灰色
// 大坝正面倾斜部分
unsigned int damSlopeVAO = createCube(100, 30, 20, glm::vec3(0.533f, 0.533f, 0.533f)); // 灰色
// 坝顶结构
unsigned int damTopVAO = createCube(100, 5, 10, glm::vec3(0.533f, 0.533f, 0.533f)); // 灰色
// 水闸塔(使用浅灰色)
glm::vec3 towerColor = glm::vec3(0.8f, 0.8f, 0.8f);
unsigned int leftTower1VAO = createCube(6, 20, 10, towerColor);
unsigned int leftTower2VAO = createCube(6, 20, 10, towerColor);
unsigned int centerTower1VAO = createCube(6, 25, 10, towerColor);
unsigned int centerTower2VAO = createCube(6, 25, 10, towerColor);
unsigned int rightTower1VAO = createCube(6, 20, 10, towerColor);
unsigned int rightTower2VAO = createCube(6, 20, 10, towerColor);
// 右侧建筑
unsigned int rightBuildingVAO = createCube(15, 15, 10, towerColor);
// 渲染循环
while (!glfwWindowShouldClose(window)) {
// 计算时间差
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// 处理输入
processInput(window);
// 清空颜色缓冲和深度缓冲
glClearColor(0.533f, 0.808f, 0.922f, 1.0f); // 天空蓝
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 使用着色器程序
glUseProgram(shaderProgram);
// 视图矩阵
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
// 投影矩阵
glm::mat4 projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 2000.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// 绘制地面
glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(groundVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
// 绘制远处山体
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0, 0, -200));
model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(farMountainVAO);
glDrawArrays(GL_TRIANGLES, 0, 4*3 + 4*3); // 底部4个三角形 + 侧面4个三角形
// 绘制左侧山体
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-150, 20, -50));
model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(45.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(leftMountainVAO);
glDrawArrays(GL_TRIANGLES, 0, 4*3 + 4*3);
// 绘制右侧山体
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(150, 15, -50));
model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(-45.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(rightMountainVAO);
glDrawArrays(GL_TRIANGLES, 0, 4*3 + 4*3);
// 绘制水体(半透明)
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0, 5, -100));
model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(waterVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
// 绘制大坝基础
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0, 10, 0));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(damBaseVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// 绘制大坝倾斜部分
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0, 25, -10));
model = glm::rotate(model, glm::radians(-17.19f), glm::vec3(1.0f, 0.0f, 0.0f)); // -0.3弧度 ≈ -17.19度
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(damSlopeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// 绘制坝顶结构
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0, 30, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(damTopVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// 绘制水闸塔
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-30, 40, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(leftTower1VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-15, 40, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(leftTower2VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-5, 45, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(centerTower1VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(5, 45, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(centerTower2VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(15, 40, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(rightTower1VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(30, 40, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(rightTower2VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// 绘制右侧建筑
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(55, 35, 20));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(rightBuildingVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// 渲染菜单
renderMenu(shaderProgram);
// 交换缓冲并轮询事件
glfwSwapBuffers(window);
glfwPollEvents();
}
// 清理资源
glDeleteVertexArrays(1, &groundVAO);
glDeleteVertexArrays(1, &waterVAO);
glDeleteVertexArrays(1, &leftMountainVAO);
glDeleteVertexArrays(1, &rightMountainVAO);
glDeleteVertexArrays(1, &farMountainVAO);
glDeleteVertexArrays(1, &damBaseVAO);
glDeleteVertexArrays(1, &damSlopeVAO);
glDeleteVertexArrays(1, &damTopVAO);
glDeleteVertexArrays(1, &leftTower1VAO);
glDeleteVertexArrays(1, &leftTower2VAO);
glDeleteVertexArrays(1, ¢erTower1VAO);
glDeleteVertexArrays(1, ¢erTower2VAO);
glDeleteVertexArrays(1, &rightTower1VAO);
glDeleteVertexArrays(1, &rightTower2VAO);
glDeleteVertexArrays(1, &rightBuildingVAO);
glDeleteProgram(shaderProgram);
// 终止GLFW
glfwTerminate();
return 0;
}