Files
DNF_DEV/source/EngineFrame/Render/RenderManager.cpp
2026-02-08 16:20:50 +08:00

476 lines
15 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "RenderManager.h"
#include <fstream>
#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>();
std::string fragPath = groupData["fragment"].get<std::string>();
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<GLint> 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<GLint> 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<Texture> 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和EBOGPU获取顶点数据和格式的关键
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<char>(Source)), std::istreambuf_iterator<char>());
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;
}