#include "RenderManager.h" #include #include "Texture.h" RenderManager::RenderManager(SDL_Window *window) { _window = window; // 创建OpenGL上下文 _ctx = SDL_GL_CreateContext(window); if (!_ctx) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "GL context create failed: %s\n", SDL_GetError()); } // 初始化GLAD if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "GLAD init failed!\n"); } // 获取窗口大小 int width, height; SDL_GetWindowSize(window, &width, &height); // 设置视口 SetViewport(SDL_Rect{0, 0, width, height}); // 设置窗口尺寸 _windowWidth = width; _windowHeight = height; // 游戏的初始正交投影矩阵 _GameOrthoMatrix = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f); // UI的初始正交投影矩阵 _UIOrthoMatrix = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f); // 设置清屏颜色 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 启用混合模式 glEnable(GL_BLEND); // 默认设置标准alpha混合 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 初始化着色器程序 InitShaderProgram(); // 初始化绘制2D纹理缓冲程序 Init2DTextureProgram(); // 初始化绘制矩形缓冲程序 InitRectProgram(); // 设定默认使用的着色器程序和缓冲程序 SetCurrentShaderProgram("normal"); SetCurrentBufferObject("2DTexture"); } RenderManager::~RenderManager() { // 释放着色器程序 for (auto &[name, program] : _shaderProgramMap) { glDeleteProgram(program); } // 释放缓冲对象 for (auto &[name, program] : _RenderParamsMap) { glDeleteVertexArrays(1, &program.VAO); glDeleteBuffers(1, &program.VBO); glDeleteBuffers(1, &program.EBO); } // 释放OpenGL上下文 SDL_GL_DeleteContext(_ctx); } void RenderManager::InitShaderProgram() { std::ifstream shaderFile("shader/config.json"); nlohmann::json shaderConfig; shaderFile >> shaderConfig; for (auto &[groupName, groupData] : shaderConfig["shader"].items()) { if (!groupData.is_object()) { SDL_LogError(0, "着色器组 %s 格式错误,跳过\n", groupName.c_str()); continue; } if (!groupData.contains("vertex") || !groupData["vertex"].is_string()) { SDL_LogError(0, "组 %s 缺少 vertex 路径,跳过\n", groupName.c_str()); continue; } if (!groupData.contains("fragment") || !groupData["fragment"].is_string()) { SDL_LogError(0, "组 %s 缺少 fragment 路径,跳过\n", groupName.c_str()); continue; } std::string vertPath = groupData["vertex"].get(); std::string fragPath = groupData["fragment"].get(); GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertPath); GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragPath); if (vertexShader == 0 || fragmentShader == 0) { SDL_LogError(0, "组 %s 着色器编译失败,跳过\n", groupName.c_str()); continue; } GLuint program = glCreateProgram(); glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glLinkProgram(program); GLint programLinksuccess; glGetProgramiv(program, GL_LINK_STATUS, &programLinksuccess); if (!programLinksuccess) { char infoLog[512]; glGetProgramInfoLog(program, 512, nullptr, infoLog); SDL_LogError(0, "组 %s 着色器链接失败,失败原因: %s\n", groupName.c_str(), infoLog); } _shaderProgramMap[groupName] = program; glDeleteShader(vertexShader); glDeleteShader(fragmentShader); } } void RenderManager::Init2DTextureProgram() { // 初始化顶点数据(矩形的四个顶点) float vertices[] = { 0.0f, 0.0f, 0.0f, 0.0f, // 左下(单位坐标) 1.0f, 0.0f, 1.0f, 0.0f, // 右下 1.0f, 1.0f, 1.0f, 1.0f, // 右上 0.0f, 1.0f, 0.0f, 1.0f // 左上 }; // 索引数组 unsigned int indices[] = {0, 1, 2, 2, 3, 0}; GLuint VAO, VBO, EBO; // 创建VAO/VBO/EBO glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STREAM_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); glEnableVertexAttribArray(1); // 创建完解绑 glBindVertexArray(0); SetCurrentShaderProgram("normal"); glUseProgram(_currentShaderProgram); // 获取uniform变量位置 GLint _uModelLoc = glGetUniformLocation(_currentShaderProgram, "uModel"); GLint _uViewProjLoc = glGetUniformLocation(_currentShaderProgram, "uViewProj"); GLint _uTextureLoc = glGetUniformLocation(_currentShaderProgram, "uTexture"); GLint _uOpacityLoc = glGetUniformLocation(_currentShaderProgram, "uOpacity"); if (_uModelLoc == -1 || _uViewProjLoc == -1 || _uTextureLoc == -1 || _uOpacityLoc == -1) { SDL_LogError(0, "无法获取着色器 uniform 变量位置\n"); } std::vector uniformLocs = {_uModelLoc, _uViewProjLoc, _uTextureLoc, _uOpacityLoc}; GL_RenderParams bufferObject = {VAO, VBO, EBO, uniformLocs, nullptr}; AddBufferObject("2DTexture", bufferObject); } void RenderManager::InitRectProgram() { // 单位矩形顶点(仅位置,左下角(0,0),右上角(1,1)) float vertices[] = { 0.0f, 0.0f, // 左下 1.0f, 0.0f, // 右下 1.0f, 1.0f, // 右上 0.0f, 1.0f // 左上 }; unsigned int indices[] = {0, 1, 2, 2, 3, 0}; // 固定索引 GLuint VAO, VBO, EBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); // 绑定并上传数据(GL_STATIC_DRAW:数据仅初始化一次,后续不修改) glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 静态数据 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 静态数据 // 配置顶点属性(仅位置,2个float,步长2*sizeof(float)) glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); // 解绑(恢复默认状态) glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // 获取矩形着色器的Uniform位置(需提前准备矩形专用着色器) SetCurrentShaderProgram("rect"); // 假设着色器配置中“rect”为矩形专用着色器 glUseProgram(_currentShaderProgram); GLint uModelLoc = glGetUniformLocation(_currentShaderProgram, "uModel"); GLint uViewProjLoc = glGetUniformLocation(_currentShaderProgram, "uViewProj"); GLint uColorLoc = glGetUniformLocation(_currentShaderProgram, "uColor"); if (uModelLoc == -1 || uViewProjLoc == -1 || uColorLoc == -1) { SDL_LogError(0, "矩形着色器Uniform变量获取失败\n uModelLoc: %d, uViewProjLoc: %d, uColorLoc: %d\n", uModelLoc, uViewProjLoc, uColorLoc); return; } std::vector uniformLocs = {uModelLoc, uViewProjLoc, uColorLoc}; // 将矩形渲染参数加入管理器 GL_RenderParams rectBuffer = {VAO, VBO, EBO, uniformLocs, nullptr}; AddBufferObject("Rect", rectBuffer); } void RenderManager::SetCurrentShaderProgram(std::string name) { if (_shaderProgramMap.find(name) != _shaderProgramMap.end()) { this->_currentShaderProgram = _shaderProgramMap[name]; } else SDL_LogError(0, "找不到着色器程序: %s\n", name.c_str()); } void RenderManager::SetCurrentBufferObject(std::string name) { if (_RenderParamsMap.find(name) != _RenderParamsMap.end()) { this->_currentRenderParams = _RenderParamsMap[name]; } // else // SDL_LogError(0, "找不到渲染参数: %s\n", name.c_str()); } void RenderManager::ClearScreen() { glClear(GL_COLOR_BUFFER_BIT); _frameRenderCount = 0; } void RenderManager::SwapBuffer() { SDL_GL_SwapWindow(_window); } void RenderManager::SetClipRect(const SDL_Rect *rect) { glEnable(GL_SCISSOR_TEST); glScissor(rect->x, _windowHeight - rect->y - rect->h, rect->w, rect->h); } void RenderManager::CloseClipRect() { glDisable(GL_SCISSOR_TEST); } void RenderManager::SetOpacity(float opacity) { this->opacity_ = opacity; } float RenderManager::GetOpacity() { return this->opacity_; } void RenderManager::SetMatrix(glm::mat4 matrix) { this->_currentMatrix = matrix; } glm::mat4 RenderManager::GetMatrix() { return this->_currentMatrix; } void RenderManager::SetBlendMode(LE_BlEND_MODE blendMode) { switch (blendMode) { /**常规混合 */ case NONE: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; /**线性减淡 */ case LINEARDODGE: glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); break; } } void RenderManager::DrawTexture(RefPtr textureObj) { _frameRenderCount++; // 统计渲染次数 glUseProgram(_currentShaderProgram); GLuint texture = textureObj->getID(); float texWidth = (float)textureObj->getSize().width; float texHeight = (float)textureObj->getSize().height; glm::mat4 modelMat = _currentMatrix; modelMat = glm::scale(modelMat, glm::vec3(texWidth, texHeight, 1.0f)); // 设置着色器 uniforms glUniformMatrix4fv(_currentRenderParams.UnimLocs[0], 1, GL_FALSE, glm::value_ptr(modelMat)); glUniformMatrix4fv(_currentRenderParams.UnimLocs[1], 1, GL_FALSE, glm::value_ptr(_OrthoMatrix)); // 绑定纹理并设置采样器 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glUniform1i(_currentRenderParams.UnimLocs[2], 0); glUniform1f(_currentRenderParams.UnimLocs[3], opacity_); // 计算纹理坐标 float minu, minv, maxu, maxv; if (false) { // minu = (float)srcrect->x / texWidth; // minv = (float)srcrect->y / texHeight; // maxu = (float)(srcrect->x + srcrect->w) / texWidth; // maxv = (float)(srcrect->y + srcrect->h) / texHeight; } else { minu = 0.0f; minv = 0.0f; maxu = 1.0f; maxv = 1.0f; } // 计算顶点位置(单位矩形,通过模型变换进行缩放和平移) float minx = 0.0f; float miny = 0.0f; float maxx = 1.0f; float maxy = 1.0f; // 创建顶点数据(位置 + 纹理坐标) float vertices[] = { // 位置 // 纹理坐标 minx, miny, minu, minv, // 左上 maxx, miny, maxu, minv, // 右上 maxx, maxy, maxu, maxv, // 右下 minx, maxy, minu, maxv // 左下 }; // 更新VBO数据 glBindBuffer(GL_ARRAY_BUFFER, _currentRenderParams.VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); // 绘制纹理 glBindVertexArray(_currentRenderParams.VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); // 解绑纹理 } void RenderManager::DrawRect(const SDL_Rect *rect, glm::vec4 color) { // 1. 合法性检查(补充VAO/EBO存在性检查) if (!rect || _currentShaderProgram == 0 || _currentRenderParams.VAO == 0 || _currentRenderParams.EBO == 0) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "DrawRect:参数无效或缓冲未初始化\n"); return; } SetCurrentShaderProgram("rect"); SetCurrentBufferObject("Rect"); _frameRenderCount++; // 绑定VAO和EBO(GPU获取顶点数据和格式的关键) glBindVertexArray(_currentRenderParams.VAO); // 3. 计算模型矩阵(通过缩放+平移,将单位矩形变换到目标位置和大小) glm::mat4 model = glm::mat4(1.0f); // 先平移到rect的左上角(OpenGL正交投影下Y轴向下,与SDL_Rect一致) model = glm::translate(model, glm::vec3((float)rect->x, (float)rect->y, 0.0f)); // 再缩放到rect的宽高 model = glm::scale(model, glm::vec3((float)rect->w, (float)rect->h, 1.0f)); // 4. 激活着色器并设置Uniform glUseProgram(_currentShaderProgram); // 4.1 模型矩阵(传递变换后的矩阵) glUniformMatrix4fv(_currentRenderParams.UnimLocs[0], 1, GL_FALSE, glm::value_ptr(model)); // 4.2 投影矩阵(复用当前正交矩阵) glUniformMatrix4fv(_currentRenderParams.UnimLocs[1], 1, GL_FALSE, glm::value_ptr(_OrthoMatrix)); // 4.3 颜色 glUniform4fv(_currentRenderParams.UnimLocs[2], 1, glm::value_ptr(color)); // 5. 绘制矩形(6个索引对应2个三角形) glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // 6. 解绑资源(避免影响后续绘制) glBindVertexArray(0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); SetCurrentShaderProgram("normal"); SetCurrentBufferObject("2DTexture"); } void RenderManager::SetOrthoMatrixType(int type) { if (type == 0) _OrthoMatrix = _GameOrthoMatrix; else _OrthoMatrix = _UIOrthoMatrix; } glm::mat4 RenderManager::GetOrthoMatrix() { return _OrthoMatrix; } void RenderManager::SetOrthoMatrix(glm::mat4 matrix) { _OrthoMatrix = matrix; } void RenderManager::SetViewport(SDL_Rect viewport) { _viewport = viewport; glViewport(viewport.x, viewport.y, viewport.w, viewport.h); } SDL_Rect RenderManager::GetViewport() { return _viewport; } GLuint RenderManager::CompileShader(GLenum type, std::string Path) { std::ifstream Source("shader/" + Path); if (!Source.is_open()) { SDL_LogError(0, "Failed to open shader file: %s\n", Path.c_str()); return 0; } std::string ShaderSource((std::istreambuf_iterator(Source)), std::istreambuf_iterator()); const char *SourcePtr = ShaderSource.c_str(); GLuint ShaderId = glCreateShader(type); glShaderSource(ShaderId, 1, &SourcePtr, NULL); glCompileShader(ShaderId); // 检查编译错误 GLint success; glGetShaderiv(ShaderId, GL_COMPILE_STATUS, &success); if (!success) { char infoLog[512]; glGetShaderInfoLog(ShaderId, 512, nullptr, infoLog); SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Shader compile error: %s\n", infoLog); glDeleteShader(ShaderId); return 0; } return ShaderId; } void RenderManager::AddBufferObject(std::string name, GL_RenderParams bufferObject) { _RenderParamsMap[name] = bufferObject; }