SwitchGame/source/EngineFrame/Render/GLESRenderer.cpp

282 lines
7.5 KiB
C++

// GLESRenderer.cpp
#include "EngineFrame/Render/GLESRenderer.h"
#include <iostream>
#include <algorithm>
#include <glm/ext/matrix_float4x4.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/gtc/type_ptr.hpp>
// 顶点着色器源码
const char *vertexShaderSource = R"(
#version 300 es
precision mediump float;
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
out vec2 vTexCoord;
uniform mat4 uProjection;
uniform mat4 uModel;
void main() {
gl_Position = uProjection * uModel * vec4(aPosition, 0.0, 1.0);
vTexCoord = aTexCoord;
}
)";
// 片段着色器源码
const char *fragmentShaderSource = R"(
#version 300 es
precision mediump float;
in vec2 vTexCoord;
out vec4 FragColor;
uniform sampler2D uTexture;
uniform vec4 uColor;
void main() {
FragColor = texture(uTexture, vTexCoord) * uColor;
}
)";
GLESRenderer::GLESRenderer(SDL_Window *window) : m_window(window), m_glContext(nullptr)
{
}
GLESRenderer::~GLESRenderer()
{
if (m_glContext)
{
SDL_GL_DeleteContext(m_glContext);
}
glDeleteProgram(m_shaderProgram);
glDeleteShader(m_vertexShader);
glDeleteShader(m_fragmentShader);
glDeleteBuffers(1, &m_vbo);
glDeleteVertexArrays(1, &m_vao);
}
bool GLESRenderer::Initialize()
{
// 设置 OpenGL ES 属性
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
// 创建 OpenGL ES 上下文
m_glContext = SDL_GL_CreateContext(m_window);
if (!m_glContext)
{
std::cerr << "Failed to create OpenGL ES context: " << SDL_GetError() << std::endl;
return false;
}
// 初始化 GLEW (如果需要) 或者直接使用 OpenGL ES 函数
// 创建着色器程序
if (!CreateShaderProgram())
{
std::cerr << "Failed to create shader program" << std::endl;
return false;
}
// 设置视口
int w, h;
SDL_GetWindowSize(m_window, &w, &h);
glViewport(0, 0, w, h);
// 设置正交投影
SetOrthographicProjection(w, h);
// 启用混合
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 创建VAO和VBO
glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo);
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
// 设置顶点属性指针
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);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
return true;
}
void GLESRenderer::Clear()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 清空批处理
m_renderBatch.clear();
}
void GLESRenderer::Present()
{
// 按Z顺序排序
std::stable_sort(m_renderBatch.begin(), m_renderBatch.end(),
[](const RenderCommand &a, const RenderCommand &b)
{
return a.z_order < b.z_order;
});
// 使用着色器程序
glUseProgram(m_shaderProgram);
// 设置投影矩阵
GLint projLoc = glGetUniformLocation(m_shaderProgram, "uProjection");
// 这里需要设置正交投影矩阵
// 渲染批处理中的所有命令
for (const auto &cmd : m_renderBatch)
{
// 绑定纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cmd.texture);
glUniform1i(glGetUniformLocation(m_shaderProgram, "uTexture"), 0);
// 计算模型矩阵(位置、旋转、缩放)
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(cmd.dstrect.x, cmd.dstrect.y, 0.0f));
if (cmd.angle != 0)
{
// 应用旋转
model = glm::translate(model, glm::vec3(cmd.center.x, cmd.center.y, 0.0f));
model = glm::rotate(model, glm::radians(static_cast<float>(cmd.angle)), glm::vec3(0.0f, 0.0f, 1.0f));
model = glm::translate(model, glm::vec3(-cmd.center.x, -cmd.center.y, 0.0f));
}
model = glm::scale(model, glm::vec3(cmd.dstrect.w, cmd.dstrect.h, 1.0f));
// 设置模型矩阵
GLint modelLoc = glGetUniformLocation(m_shaderProgram, "uModel");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// 绘制矩形
glBindVertexArray(m_vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
// 交换缓冲区
SDL_GL_SwapWindow(m_window);
}
void GLESRenderer::RenderCopy(GLuint texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect)
{
RenderCommand cmd;
cmd.texture = texture;
if (dstrect)
cmd.dstrect = *dstrect;
cmd.angle = 0;
cmd.flip = SDL_FLIP_NONE;
cmd.z_order = 0; // 需要从Sprite获取Z顺序
m_renderBatch.push_back(cmd);
}
void GLESRenderer::RenderCopyEx(GLuint texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect,
double angle, const SDL_Point *center, SDL_RendererFlip flip)
{
RenderCommand cmd;
cmd.texture = texture;
if (dstrect)
cmd.dstrect = *dstrect;
cmd.angle = angle;
if (center)
cmd.center = *center;
cmd.flip = flip;
cmd.z_order = 0; // 需要从Sprite获取Z顺序
m_renderBatch.push_back(cmd);
}
bool GLESRenderer::CompileShader(const char *source, GLenum type, GLuint &shader)
{
shader = glCreateShader(type);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
GLchar infoLog[512];
glGetShaderInfoLog(shader, 512, NULL, infoLog);
std::cerr << "Shader compilation failed: " << infoLog << std::endl;
return false;
}
return true;
}
bool GLESRenderer::LinkProgram(GLuint program)
{
glLinkProgram(program);
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success)
{
GLchar infoLog[512];
glGetProgramInfoLog(program, 512, NULL, infoLog);
std::cerr << "Program linking failed: " << infoLog << std::endl;
return false;
}
return true;
}
bool GLESRenderer::CreateShaderProgram()
{
if (!CompileShader(vertexShaderSource, GL_VERTEX_SHADER, m_vertexShader))
{
return false;
}
if (!CompileShader(fragmentShaderSource, GL_FRAGMENT_SHADER, m_fragmentShader))
{
return false;
}
m_shaderProgram = glCreateProgram();
glAttachShader(m_shaderProgram, m_vertexShader);
glAttachShader(m_shaderProgram, m_fragmentShader);
if (!LinkProgram(m_shaderProgram))
{
return false;
}
glDeleteShader(m_vertexShader);
glDeleteShader(m_fragmentShader);
return true;
}
void GLESRenderer::SetOrthographicProjection(int width, int height)
{
// 设置正交投影矩阵
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(width),
static_cast<float>(height), 0.0f,
-1.0f, 1.0f);
glUseProgram(m_shaderProgram);
GLint projLoc = glGetUniformLocation(m_shaderProgram, "uProjection");
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
}