feat(渲染器): 添加基础2D渲染系统

实现核心渲染功能,包括着色器管理、批处理系统、相机控制和纹理加载

- 添加着色器管理器用于加载和管理GLSL着色器程序
- 实现批处理系统优化绘制调用
- 添加相机控制支持视图和投影矩阵
- 实现纹理加载和管理功能
- 添加基础2D渲染API包括绘制四边形和精灵
- 集成到应用系统中,支持自动初始化和清理
- 添加示例着色器用于彩色四边形和纹理精灵
- 更新构建系统包含新的渲染相关文件
This commit is contained in:
Lenheart 2026-02-26 13:16:31 +08:00
parent 3870220fe8
commit 6f8d1f1255
25 changed files with 1475 additions and 11 deletions

View File

@ -127,12 +127,18 @@ public:
* @return * @return
*/ */
const AppConfig& getConfig() const; const AppConfig& getConfig() const;
/** /**
* @brief * @brief
* @return * @return
*/ */
Window* getWindow() const { return window_; } Window* getWindow() const { return window_; }
/**
* @brief
* @return
*/
class Renderer* getRenderer() const { return renderer_; }
/** /**
* @brief delta time * @brief delta time
@ -182,6 +188,7 @@ private:
std::vector<Module*> modules_; std::vector<Module*> modules_;
Window* window_ = nullptr; Window* window_ = nullptr;
class Renderer* renderer_ = nullptr;
AppConfig config_; AppConfig config_;
bool initialized_ = false; bool initialized_ = false;

View File

@ -0,0 +1,66 @@
#pragma once
#include <frostbite2D/graphics/types.h>
#include <frostbite2D/graphics/texture.h>
#include <frostbite2D/graphics/shader.h>
#include <frostbite2D/types/type_alias.h>
namespace frostbite2D {
struct BatchKey {
uint32_t shaderID;
uint32_t textureID;
uint32_t blendMode;
bool operator<(const BatchKey& other) const {
if (shaderID != other.shaderID) return shaderID < other.shaderID;
if (textureID != other.textureID) return textureID < other.textureID;
return blendMode < other.blendMode;
}
};
class Batch {
public:
static constexpr int MAX_QUADS = 2048;
static constexpr int MAX_VERTICES = MAX_QUADS * 4;
static constexpr int MAX_INDICES = MAX_QUADS * 6;
Batch();
~Batch();
bool init();
void shutdown();
void begin();
void end();
void submitQuad(const Quad& quad, const Transform2D& transform,
Ptr<Texture> texture, Shader* shader,
BlendMode blendMode = BlendMode::Normal);
void flush();
void flushIfNeeded(const BatchKey& newKey, Shader* shader, Ptr<Texture> texture);
private:
void setupMesh();
void flushCurrentBatch();
struct BatchData {
std::vector<Vertex> vertices;
std::vector<uint16> indices;
BatchKey key;
Shader* shader = nullptr;
Ptr<Texture> texture;
};
uint32_t vao_ = 0;
uint32_t vbo_ = 0;
uint32_t ibo_ = 0;
BatchData currentBatch_;
std::vector<BatchData> batches_;
bool begun_ = false;
};
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <frostbite2D/types/type_math.h>
namespace frostbite2D {
class Camera {
public:
Camera();
void setPosition(const Vec2& pos);
void setZoom(float zoom);
void setViewport(int width, int height);
void setFlipY(bool flip) { flipY_ = flip; } // 调试用Y轴翻转开关
const Vec2& getPosition() const { return position_; }
float getZoom() const { return zoom_; }
void lookAt(const Vec2& target);
void move(const Vec2& delta);
void zoomAt(float factor, const Vec2& screenPos);
glm::mat4 getViewMatrix() const;
glm::mat4 getProjectionMatrix() const;
private:
Vec2 position_;
float zoom_ = 1.0f;
int viewportWidth_ = 1280;
int viewportHeight_ = 720;
bool flipY_ = false; // 调试用Y轴翻转开关
};
}

View File

@ -0,0 +1,67 @@
#pragma once
#include <frostbite2D/graphics/types.h>
#include <frostbite2D/graphics/texture.h>
#include <frostbite2D/graphics/shader.h>
#include <frostbite2D/graphics/shader_manager.h>
#include <frostbite2D/graphics/batch.h>
#include <frostbite2D/graphics/camera.h>
#include <string>
namespace frostbite2D {
class Renderer {
public:
static Renderer& get();
bool init();
void shutdown();
void beginFrame();
void endFrame();
void flush();
void setViewport(int x, int y, int width, int height);
void setClearColor(float r, float g, float b, float a = 1.0f);
void setClearColor(const Color& color);
void clear(uint32_t flags);
void setCamera(Camera* camera);
Camera* getCamera() { return camera_; }
void drawQuad(const Vec2& pos, const Size& size, float cr = 1.0f, float cg = 1.0f,
float cb = 1.0f, float ca = 1.0f);
void drawQuad(const Vec2& pos, const Size& size, Ptr<Texture> texture);
void drawQuad(const Rect& rect, const Color& color);
void drawSprite(const Vec2& pos, const Size& size, Ptr<Texture> texture);
void drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
Ptr<Texture> texture, const Color& color = Color(1, 1, 1, 1));
ShaderManager& getShaderManager() { return shaderManager_; }
Batch& getBatch() { return batch_; }
private:
Renderer();
~Renderer();
void setupBlendMode(BlendMode mode);
void updateUniforms();
ShaderManager shaderManager_;
Batch batch_;
Camera* camera_ = nullptr;
uint32_t clearColor_[4] = {0, 0, 0, 255};
int viewportX_ = 0;
int viewportY_ = 0;
int viewportWidth_ = 1280;
int viewportHeight_ = 720;
bool initialized_ = false;
Renderer(const Renderer&) = delete;
Renderer& operator=(const Renderer&) = delete;
};
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <string>
#include <unordered_map>
namespace frostbite2D {
class Shader {
public:
Shader() = default;
~Shader();
void use();
void unuse();
bool isValid() const { return programID_ != 0; }
uint32_t getID() const { return programID_; }
const std::string& getName() const { return name_; }
void setInt(const std::string& name, int value);
void setFloat(const std::string& name, float value);
void setVec2(const std::string& name, const Vec2& value);
void setVec3(const std::string& name, const glm::vec3& value);
void setVec4(const std::string& name, const glm::vec4& value);
void setMat3(const std::string& name, const glm::mat3& value);
void setMat4(const std::string& name, const glm::mat4& value);
void setTexture(const std::string& name, int slot);
private:
Shader(const std::string& name, uint32_t programID);
friend class ShaderManager;
bool compile(uint32_t type, const std::string& source);
bool link(uint32_t vertexID, uint32_t fragmentID);
int getUniformLocation(const std::string& name);
uint32_t programID_ = 0;
std::string name_;
std::unordered_map<std::string, int> uniformLocations_;
Shader(const Shader&) = delete;
Shader& operator=(const Shader&) = delete;
};
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <frostbite2D/graphics/shader.h>
#include <frostbite2D/types/type_alias.h>
#include <string>
#include <unordered_map>
namespace frostbite2D {
class Renderer;
class ShaderManager {
public:
static ShaderManager& get();
bool init(const std::string& shadersDir);
void shutdown();
Shader* getShader(const std::string& name);
bool hasShader(const std::string& name) const;
~ShaderManager();
ShaderManager() = default;
private:
std::unordered_map<std::string, Ptr<Shader>> shaders_;
std::string shadersDir_;
bool loadShadersFromConfig(const std::string& configPath);
bool loadShader(const std::string& name, const std::string& vertPath,
const std::string& fragPath);
ShaderManager(const ShaderManager&) = delete;
ShaderManager& operator=(const ShaderManager&) = delete;
};
}

View File

@ -0,0 +1,46 @@
#pragma once
#include <frostbite2D/graphics/types.h>
#include <frostbite2D/types/type_alias.h>
#include <string>
#include <memory>
namespace frostbite2D {
class Texture {
public:
static Ptr<Texture> loadFromFile(const std::string& path);
static Ptr<Texture> createFromMemory(uint8* data, int width, int height, int channels);
static Ptr<Texture> createEmpty(int width, int height);
~Texture();
void bind(uint32_t slot = 0);
void unbind();
void setWrapMode(uint32_t wrapS, uint32_t wrapT);
void setFilterMode(uint32_t minFilter, uint32_t magFilter);
int getWidth() const { return width_; }
int getHeight() const { return height_; }
uint32_t getID() const { return textureID_; }
const std::string& getPath() const { return path_; }
private:
Texture(int width, int height, uint32_t id);
friend class std::shared_ptr<Texture>;
template<typename T, typename... Args> friend Ptr<T> makePtr(Args &&...args);
uint32_t textureID_ = 0;
int width_ = 0;
int height_ = 0;
int channels_ = 0;
std::string path_;
Texture() = default;
Texture(const Texture&) = delete;
Texture& operator=(const Texture&) = delete;
};
}

View File

@ -0,0 +1,80 @@
#pragma once
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <frostbite2D/types/type_color.h>
#include <cstdint>
namespace frostbite2D {
enum class BlendMode {
None,
Normal,
Additive,
Subtractive,
Multiply
};
enum class PrimitiveType {
Triangles,
TriangleStrip,
TriangleFan,
Quads
};
struct Vertex {
Vec2 position;
Vec2 texCoord;
float r, g, b, a;
Vertex() = default;
Vertex(const Vec2& pos, const Vec2& uv, float cr, float cg, float cb, float ca)
: position(pos), texCoord(uv), r(cr), g(cg), b(cb), a(ca) {}
};
struct Quad {
Vertex vertices[4];
Quad() = default;
static Quad create(const Rect& rect, float cr = 1.0f, float cg = 1.0f,
float cb = 1.0f, float ca = 1.0f) {
Quad q;
Vec2 bl(rect.left(), rect.bottom());
Vec2 br(rect.right(), rect.bottom());
Vec2 tl(rect.left(), rect.top());
Vec2 tr(rect.right(), rect.top());
q.vertices[0] = Vertex(bl, Vec2(0, 0), cr, cg, cb, ca);
q.vertices[1] = Vertex(br, Vec2(1, 0), cr, cg, cb, ca);
q.vertices[2] = Vertex(tl, Vec2(0, 1), cr, cg, cb, ca);
q.vertices[3] = Vertex(tr, Vec2(1, 1), cr, cg, cb, ca);
return q;
}
static Quad createTextured(const Rect& destRect, const Rect& srcRect,
const Vec2& texSize, float cr = 1.0f,
float cg = 1.0f, float cb = 1.0f, float ca = 1.0f) {
Quad q;
Vec2 bl(destRect.left(), destRect.bottom());
Vec2 br(destRect.right(), destRect.bottom());
Vec2 tl(destRect.left(), destRect.top());
Vec2 tr(destRect.right(), destRect.top());
Vec2 uvBL(srcRect.left() / texSize.x, srcRect.bottom() / texSize.y);
Vec2 uvBR(srcRect.right() / texSize.x, srcRect.bottom() / texSize.y);
Vec2 uvTL(srcRect.left() / texSize.x, srcRect.top() / texSize.y);
Vec2 uvTR(srcRect.right() / texSize.x, srcRect.top() / texSize.y);
q.vertices[0] = Vertex(bl, uvBL, cr, cg, cb, ca);
q.vertices[1] = Vertex(br, uvBR, cr, cg, cb, ca);
q.vertices[2] = Vertex(tl, uvTL, cr, cg, cb, ca);
q.vertices[3] = Vertex(tr, uvTR, cr, cg, cb, ca);
return q;
}
};
}

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <frostbite2D/core/types.h>
#include <algorithm> #include <algorithm>
#include <cstdint>
#include <glm/vec4.hpp> #include <glm/vec4.hpp>
namespace frostbite2D { namespace frostbite2D {

View File

@ -196,7 +196,7 @@ struct Rect {
constexpr Rect() = default; constexpr Rect() = default;
constexpr Rect(float x, float y, float w, float h) constexpr Rect(float x, float y, float w, float h)
: origin(x, y), size(w, h) {} : origin(x, y), size(w, h) {}
constexpr Rect(const Point &o, const Size &s) : origin(o), size(s) {} constexpr Rect(const Point& o, const Size& s) : origin(o), size(s) {}
float left() const { return origin.x; } float left() const { return origin.x; }
float top() const { return origin.y; } float top() const { return origin.y; }

View File

@ -1,7 +1,9 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <algorithm> #include <algorithm>
#include <frostbite2D/core/application.h> #include <frostbite2D/core/application.h>
#include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/platform/switch.h> #include <frostbite2D/platform/switch.h>
#include <frostbite2D/types/type_math.h>
namespace frostbite2D { namespace frostbite2D {
Application &Application::get() { Application &Application::get() {
@ -69,6 +71,12 @@ void Application::shutdown() {
destroyAllModules(); destroyAllModules();
// 关闭渲染器
if (renderer_) {
renderer_->shutdown();
renderer_ = nullptr;
}
if (window_) { if (window_) {
window_->destroy(); window_->destroy();
window_ = nullptr; window_ = nullptr;
@ -89,6 +97,14 @@ Application::~Application() {
bool Application::initCoreModules() { bool Application::initCoreModules() {
registerCoreServices(); registerCoreServices();
// 初始化渲染器
renderer_ = &Renderer::get();
if (!renderer_->init()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize renderer");
return false;
}
return true; return true;
} }
@ -234,6 +250,28 @@ void Application::render() {
} }
} }
// 简单的测试代码 - 绘制彩色方块
if (renderer_) {
static float angle = 0.0f;
angle += 0.02f;
renderer_->beginFrame();
// 绘制橙色方块
renderer_->drawQuad(Vec2(0, 0), Size(200, 150), 1.0f, 0.5f, 0.2f);
// 绘制蓝色移动方块
float x = 1920.0 / 2 + cosf(angle) * 100.0f;
float y = 1080.0 / 2 + sinf(angle) * 100.0f;
renderer_->drawQuad(Vec2(x, y), Size(100, 100), 0.3f, 0.5f, 1.0f);
renderer_->drawQuad(Vec2(1920 - 100, 1080 - 150), Size(100, 150), 1.0f,
0.5f, 0.2f);
renderer_->endFrame();
renderer_->flush();
}
if (window_) { if (window_) {
window_->swap(); window_->swap();
} }

View File

@ -81,7 +81,20 @@ bool Window::create(const WindowConfig &cfg) {
SDL_GL_SetSwapInterval(cfg.vsync ? 1 : 0); SDL_GL_SetSwapInterval(cfg.vsync ? 1 : 0);
SDL_GetWindowSize(sdlWindow_, &width_, &height_); // 获取实际的可绘制大小(考虑高 DPI 和 Switch 等平台)
int drawableWidth, drawableHeight;
SDL_GL_GetDrawableSize(sdlWindow_, &drawableWidth, &drawableHeight);
width_ = drawableWidth;
height_ = drawableHeight;
// 计算缩放比例
int windowWidth, windowHeight;
SDL_GetWindowSize(sdlWindow_, &windowWidth, &windowHeight);
if (windowWidth > 0 && windowHeight > 0) {
scaleX_ = static_cast<float>(drawableWidth) / static_cast<float>(windowWidth);
scaleY_ = static_cast<float>(drawableHeight) / static_cast<float>(windowHeight);
}
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
vsync_ = cfg.vsync; vsync_ = cfg.vsync;
@ -119,7 +132,11 @@ void Window::destroy() {
void Window::poll() {} void Window::poll() {}
void Window::swap() {} void Window::swap() {
if (sdlWindow_ && glContext_) {
SDL_GL_SwapWindow(sdlWindow_);
}
}
void Window::close() { shouldClose_ = true; } void Window::close() { shouldClose_ = true; }

View File

@ -0,0 +1,180 @@
#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/graphics/batch.h>
#include <glad/glad.h>
namespace frostbite2D {
Batch::Batch() {
currentBatch_.vertices.reserve(MAX_VERTICES);
currentBatch_.indices.reserve(MAX_INDICES);
}
Batch::~Batch() {
shutdown();
}
bool Batch::init() {
glGenVertexArrays(1, &vao_);
glGenBuffers(1, &vbo_);
glGenBuffers(1, &ibo_);
setupMesh();
SDL_Log("Batch system initialized");
return true;
}
void Batch::shutdown() {
if (vao_ != 0) {
glDeleteVertexArrays(1, &vao_);
vao_ = 0;
}
if (vbo_ != 0) {
glDeleteBuffers(1, &vbo_);
vbo_ = 0;
}
if (ibo_ != 0) {
glDeleteBuffers(1, &ibo_);
ibo_ = 0;
}
currentBatch_.vertices.clear();
currentBatch_.indices.clear();
batches_.clear();
}
void Batch::setupMesh() {
glBindVertexArray(vao_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_INDICES * sizeof(uint16), nullptr, GL_DYNAMIC_DRAW);
int stride = sizeof(Vertex);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(Vertex, texCoord));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(Vertex, r));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Batch::begin() {
begun_ = true;
currentBatch_.vertices.clear();
currentBatch_.indices.clear();
currentBatch_.key = {0, 0, 0};
currentBatch_.shader = nullptr;
currentBatch_.texture.reset();
}
void Batch::end() {
if (begun_) {
flush();
begun_ = false;
}
}
void Batch::submitQuad(const Quad& quad, const Transform2D& transform,
Ptr<Texture> texture, Shader* shader,
BlendMode blendMode) {
if (!begun_) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Batch not started, call begin() first");
return;
}
uint32_t textureID = texture ? texture->getID() : 0;
uint32_t shaderID = shader ? shader->getID() : 0;
BatchKey newKey = {shaderID, textureID, (uint32_t)blendMode};
flushIfNeeded(newKey, shader, texture);
glm::mat4 mat = transform.matrix;
uint16_t indexOffset = static_cast<uint16_t>(currentBatch_.vertices.size());
for (int i = 0; i < 4; i++) {
Vertex v = quad.vertices[i];
glm::vec4 pos(v.position.x, v.position.y, 0.0f, 1.0f);
glm::vec4 transformed = mat * pos;
v.position.x = transformed.x;
v.position.y = transformed.y;
currentBatch_.vertices.push_back(v);
}
currentBatch_.indices.push_back(indexOffset);
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 1));
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 2));
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 1));
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 3));
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 2));
}
void Batch::flush() {
if (!currentBatch_.vertices.empty()) {
flushCurrentBatch();
}
}
void Batch::flushIfNeeded(const BatchKey& newKey, Shader* shader, Ptr<Texture> texture) {
if (currentBatch_.indices.empty()) {
currentBatch_.key = newKey;
currentBatch_.shader = shader;
currentBatch_.texture = texture;
return;
}
if (currentBatch_.key.shaderID != newKey.shaderID ||
currentBatch_.key.textureID != newKey.textureID ||
currentBatch_.key.blendMode != newKey.blendMode) {
flushCurrentBatch();
currentBatch_.key = newKey;
currentBatch_.shader = shader;
currentBatch_.texture = texture;
}
}
void Batch::flushCurrentBatch() {
if (currentBatch_.vertices.empty()) {
return;
}
glBindVertexArray(vao_);
// 绑定着色器和纹理
if (currentBatch_.shader) {
currentBatch_.shader->use();
}
if (currentBatch_.texture) {
currentBatch_.texture->bind();
}
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferSubData(GL_ARRAY_BUFFER, 0,
currentBatch_.vertices.size() * sizeof(Vertex),
currentBatch_.vertices.data());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0,
currentBatch_.indices.size() * sizeof(uint16),
currentBatch_.indices.data());
glDrawElements(GL_TRIANGLES,
static_cast<GLsizei>(currentBatch_.indices.size()),
GL_UNSIGNED_SHORT, 0);
currentBatch_.vertices.clear();
currentBatch_.indices.clear();
currentBatch_.shader = nullptr;
currentBatch_.texture.reset();
}
}

View File

@ -0,0 +1,46 @@
#include <frostbite2D/graphics/camera.h>
#include <glm/gtc/matrix_transform.hpp>
namespace frostbite2D {
Camera::Camera() : position_(0, 0), zoom_(1.0f) {}
void Camera::setPosition(const Vec2 &pos) { position_ = pos; }
void Camera::setZoom(float zoom) { zoom_ = zoom; }
void Camera::setViewport(int width, int height) {
viewportWidth_ = width;
viewportHeight_ = height;
}
void Camera::lookAt(const Vec2 &target) { position_ = target; }
void Camera::move(const Vec2 &delta) { position_ = position_ + delta; }
void Camera::zoomAt(float factor, const Vec2 &screenPos) {
Vec2 worldBefore = screenPos - position_;
zoom_ *= factor;
Vec2 worldAfter = screenPos - position_;
position_ = position_ + (worldBefore - worldAfter);
}
glm::mat4 Camera::getViewMatrix() const {
glm::mat4 view = glm::mat4(1.0f);
view[3][0] = -position_.x;
view[3][1] = -position_.y;
view[0][0] = zoom_;
view[1][1] = zoom_;
return view;
}
glm::mat4 Camera::getProjectionMatrix() const {
float left = 0.0f;
float right = static_cast<float>(viewportWidth_);
float bottom = static_cast<float>(viewportHeight_);
float top = 0.0f;
return glm::ortho(left, right, bottom, top, -1.0f, 1.0f);
}
} // namespace frostbite2D

View File

@ -0,0 +1,200 @@
#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/graphics/renderer.h>
#include <glad/glad.h>
#include <glm/gtc/type_ptr.hpp>
namespace frostbite2D {
Renderer& Renderer::get() {
static Renderer instance;
return instance;
}
Renderer::Renderer() = default;
Renderer::~Renderer() {
shutdown();
}
bool Renderer::init() {
if (initialized_) {
return true;
}
if (!batch_.init()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize batch system");
return false;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
SDL_Log("Renderer initialized");
initialized_ = true;
return true;
}
void Renderer::shutdown() {
batch_.shutdown();
shaderManager_.shutdown();
initialized_ = false;
}
void Renderer::beginFrame() {
glViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
glClearColor(clearColor_[0] / 255.0f, clearColor_[1] / 255.0f,
clearColor_[2] / 255.0f, clearColor_[3] / 255.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
updateUniforms();
batch_.begin();
}
void Renderer::endFrame() {
batch_.end();
}
void Renderer::flush() {
batch_.flush();
}
void Renderer::setViewport(int x, int y, int width, int height) {
viewportX_ = x;
viewportY_ = y;
viewportWidth_ = width;
viewportHeight_ = height;
if (camera_) {
camera_->setViewport(width, height);
}
}
void Renderer::setClearColor(float r, float g, float b, float a) {
clearColor_[0] = static_cast<uint8>(r * 255);
clearColor_[1] = static_cast<uint8>(g * 255);
clearColor_[2] = static_cast<uint8>(b * 255);
clearColor_[3] = static_cast<uint8>(a * 255);
}
void Renderer::setClearColor(const Color& color) {
clearColor_[0] = color.r;
clearColor_[1] = color.g;
clearColor_[2] = color.b;
clearColor_[3] = color.a;
}
void Renderer::clear(uint32_t flags) {
glClear(flags);
}
void Renderer::setCamera(Camera* camera) {
camera_ = camera;
if (camera) {
camera_->setViewport(viewportWidth_, viewportHeight_);
}
}
void Renderer::setupBlendMode(BlendMode mode) {
switch (mode) {
case BlendMode::None:
glDisable(GL_BLEND);
break;
case BlendMode::Normal:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case BlendMode::Additive:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
break;
case BlendMode::Subtractive:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
break;
case BlendMode::Multiply:
glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR, GL_ZERO);
break;
}
}
void Renderer::updateUniforms() {
if (camera_) {
auto* spriteShader = shaderManager_.getShader("sprite");
if (spriteShader) {
spriteShader->use();
spriteShader->setMat4("u_view", camera_->getViewMatrix());
spriteShader->setMat4("u_projection", camera_->getProjectionMatrix());
}
auto* coloredShader = shaderManager_.getShader("colored_quad");
if (coloredShader) {
coloredShader->use();
coloredShader->setMat4("u_view", camera_->getViewMatrix());
coloredShader->setMat4("u_projection", camera_->getProjectionMatrix());
}
}
}
void Renderer::drawQuad(const Vec2& pos, const Size& size, float cr, float cg,
float cb, float ca) {
Rect rect(pos.x, pos.y, size.width, size.height);
Quad quad = Quad::create(rect, cr, cg, cb, ca);
Transform2D transform = Transform2D::identity();
auto* shader = shaderManager_.getShader("colored_quad");
if (shader) {
shader->use();
}
batch_.submitQuad(quad, transform, nullptr, shader, BlendMode::Normal);
}
void Renderer::drawQuad(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
Rect rect(pos.x, pos.y, size.width, size.height);
Quad quad = Quad::createTextured(rect, Rect(0, 0, size.width, size.height),
Vec2(size.width, size.height));
Transform2D transform = Transform2D::identity();
auto* shader = shaderManager_.getShader("sprite");
if (shader) {
shader->use();
}
batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
}
void Renderer::drawQuad(const Rect& rect, const Color& color) {
drawQuad(Vec2(rect.left(), rect.top()),
Size(rect.width(), rect.height()),
static_cast<float>(color.r) / 255.0f,
static_cast<float>(color.g) / 255.0f,
static_cast<float>(color.b) / 255.0f,
static_cast<float>(color.a) / 255.0f);
}
void Renderer::drawSprite(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
drawQuad(pos, size, texture);
}
void Renderer::drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
Ptr<Texture> texture, const Color& color) {
Rect destRect(pos.x, pos.y, srcRect.width(), srcRect.height());
Quad quad = Quad::createTextured(destRect, srcRect, texSize,
static_cast<float>(color.r) / 255.0f,
static_cast<float>(color.g) / 255.0f,
static_cast<float>(color.b) / 255.0f,
static_cast<float>(color.a) / 255.0f);
Transform2D transform = Transform2D::identity();
auto* shader = shaderManager_.getShader("sprite");
if (shader) {
shader->use();
}
batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
}
}

View File

@ -0,0 +1,144 @@
#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/graphics/shader.h>
#include <frostbite2D/utils/asset.h>
#include <glad/glad.h>
#include <sstream>
namespace frostbite2D {
Shader::Shader(const std::string& name, uint32_t programID)
: name_(name), programID_(programID) {}
Shader::~Shader() {
if (programID_ != 0) {
glDeleteProgram(programID_);
programID_ = 0;
}
uniformLocations_.clear();
}
void Shader::use() {
glUseProgram(programID_);
}
void Shader::unuse() {
glUseProgram(0);
}
bool Shader::compile(uint32_t type, const std::string& source) {
uint32_t shaderID = glCreateShader(type);
const char* src = source.c_str();
glShaderSource(shaderID, 1, &src, nullptr);
glCompileShader(shaderID);
int success;
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success);
if (!success) {
int length;
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &length);
std::vector<char> log(length);
glGetShaderInfoLog(shaderID, length, &length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader compilation failed: %s", log.data());
glDeleteShader(shaderID);
return false;
}
return true;
}
bool Shader::link(uint32_t vertexID, uint32_t fragmentID) {
programID_ = glCreateProgram();
glAttachShader(programID_, vertexID);
glAttachShader(programID_, fragmentID);
glLinkProgram(programID_);
int success;
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
if (!success) {
int length;
glGetProgramiv(programID_, GL_INFO_LOG_LENGTH, &length);
std::vector<char> log(length);
glGetProgramInfoLog(programID_, length, &length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader linking failed: %s", log.data());
glDeleteProgram(programID_);
programID_ = 0;
return false;
}
glDeleteShader(vertexID);
glDeleteShader(fragmentID);
return true;
}
int Shader::getUniformLocation(const std::string& name) {
auto it = uniformLocations_.find(name);
if (it != uniformLocations_.end()) {
return it->second;
}
int location = glGetUniformLocation(programID_, name.c_str());
uniformLocations_[name] = location;
return location;
}
void Shader::setInt(const std::string& name, int value) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniform1i(location, value);
}
}
void Shader::setFloat(const std::string& name, float value) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniform1f(location, value);
}
}
void Shader::setVec2(const std::string& name, const Vec2& value) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniform2f(location, value.x, value.y);
}
}
void Shader::setVec3(const std::string& name, const glm::vec3& value) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniform3f(location, value.x, value.y, value.z);
}
}
void Shader::setVec4(const std::string& name, const glm::vec4& value) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniform4f(location, value.x, value.y, value.z, value.w);
}
}
void Shader::setMat3(const std::string& name, const glm::mat3& value) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniformMatrix3fv(location, 1, GL_FALSE, &value[0][0]);
}
}
void Shader::setMat4(const std::string& name, const glm::mat4& value) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniformMatrix4fv(location, 1, GL_FALSE, &value[0][0]);
}
}
void Shader::setTexture(const std::string& name, int slot) {
int location = getUniformLocation(name);
if (location >= 0) {
glUniform1i(location, slot);
}
}
}

View File

@ -0,0 +1,186 @@
#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/graphics/shader_manager.h>
#include <frostbite2D/graphics/shader.h>
#include <frostbite2D/utils/asset.h>
#include <json/json.hpp>
#include <fstream>
#include <sstream>
#include <glad/glad.h>
using json = nlohmann::json;
using json = nlohmann::json;
namespace frostbite2D {
ShaderManager& ShaderManager::get() {
static ShaderManager instance;
return instance;
}
ShaderManager::~ShaderManager() {
shutdown();
}
bool ShaderManager::init(const std::string& shadersDir) {
shadersDir_ = shadersDir;
shaders_.clear();
std::string configPath = shadersDir_ + "/shaders.json";
return loadShadersFromConfig(configPath);
}
void ShaderManager::shutdown() {
shaders_.clear();
}
Shader* ShaderManager::getShader(const std::string& name) {
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.get();
}
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Shader not found: %s", name.c_str());
return nullptr;
}
bool ShaderManager::hasShader(const std::string& name) const {
return shaders_.find(name) != shaders_.end();
}
bool ShaderManager::loadShader(const std::string& name, const std::string& vertPath,
const std::string& fragPath) {
Asset& asset = Asset::get();
std::string vertSource, fragSource;
if (!asset.readTextFile(vertPath, vertSource)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load vertex shader: %s", vertPath.c_str());
return false;
}
if (!asset.readTextFile(fragPath, fragSource)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load fragment shader: %s", fragPath.c_str());
return false;
}
uint32_t vertexID = glCreateShader(GL_VERTEX_SHADER);
uint32_t fragmentID = glCreateShader(GL_FRAGMENT_SHADER);
const char* vertSrc = vertSource.c_str();
glShaderSource(vertexID, 1, &vertSrc, nullptr);
glCompileShader(vertexID);
const char* fragSrc = fragSource.c_str();
glShaderSource(fragmentID, 1, &fragSrc, nullptr);
glCompileShader(fragmentID);
int success;
glGetShaderiv(vertexID, GL_COMPILE_STATUS, &success);
if (!success) {
int length;
glGetShaderiv(vertexID, GL_INFO_LOG_LENGTH, &length);
std::vector<char> log(length);
glGetShaderInfoLog(vertexID, length, &length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Vertex shader compilation failed: %s", log.data());
glDeleteShader(vertexID);
glDeleteShader(fragmentID);
return false;
}
glGetShaderiv(fragmentID, GL_COMPILE_STATUS, &success);
if (!success) {
int length;
glGetShaderiv(fragmentID, GL_INFO_LOG_LENGTH, &length);
std::vector<char> log(length);
glGetShaderInfoLog(fragmentID, length, &length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Fragment shader compilation failed: %s", log.data());
glDeleteShader(vertexID);
glDeleteShader(fragmentID);
return false;
}
uint32_t programID = glCreateProgram();
glAttachShader(programID, vertexID);
glAttachShader(programID, fragmentID);
// 绑定顶点属性位置,确保和 Batch 类的设置一致
glBindAttribLocation(programID, 0, "a_position");
glBindAttribLocation(programID, 1, "a_texCoord");
glBindAttribLocation(programID, 2, "a_color");
glLinkProgram(programID);
glGetProgramiv(programID, GL_LINK_STATUS, &success);
if (!success) {
int length;
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &length);
std::vector<char> log(length);
glGetProgramInfoLog(programID, length, &length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader linking failed: %s", log.data());
glDeleteProgram(programID);
glDeleteShader(vertexID);
glDeleteShader(fragmentID);
return false;
}
glDeleteShader(vertexID);
glDeleteShader(fragmentID);
auto shader = Ptr<Shader>(new Shader(name, programID));
shaders_[name] = shader;
SDL_Log("Loaded shader: %s", name.c_str());
return true;
}
bool ShaderManager::loadShadersFromConfig(const std::string& configPath) {
Asset& asset = Asset::get();
std::string content;
if (!asset.readTextFile(configPath, content)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "No shaders.json found, will compile shaders on demand");
return true;
}
try {
json config = json::parse(content);
if (!config.contains("shaders")) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid shaders.json: missing 'shaders' key");
return false;
}
auto shaders = config["shaders"];
for (auto& [key, value] : shaders.items()) {
if (!value.contains("vertex") || !value.contains("fragment")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid shader entry: %s", key.c_str());
continue;
}
std::string vertFile = value["vertex"];
std::string fragFile = value["fragment"];
std::string vertPath = shadersDir_ + "/" + vertFile;
std::string fragPath = shadersDir_ + "/" + fragFile;
if (!loadShader(key, vertPath, fragPath)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to load shader: %s", key.c_str());
}
}
SDL_Log("Loaded %zu shaders from config", shaders_.size());
return true;
} catch (const json::parse_error& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to parse shaders.json: %s", e.what());
return false;
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error loading shaders: %s", e.what());
return false;
}
}
}

View File

@ -0,0 +1,125 @@
#define STB_IMAGE_IMPLEMENTATION
#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/graphics/texture.h>
#include <frostbite2D/utils/asset.h>
#include <stb/stb_image.h>
#include <glad/glad.h>
namespace frostbite2D {
Texture::Texture(int width, int height, uint32_t id)
: width_(width), height_(height), textureID_(id) {}
Texture::~Texture() {
if (textureID_ != 0) {
glDeleteTextures(1, &textureID_);
textureID_ = 0;
}
}
Ptr<Texture> Texture::loadFromFile(const std::string& path) {
Asset& asset = Asset::get();
std::string resolvedPath = asset.resolveAssetPath(path);
int width, height, channels;
uint8* data = stbi_load(resolvedPath.c_str(), &width, &height, &channels, 0);
if (!data) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s", resolvedPath.c_str());
return nullptr;
}
uint32_t format = channels == 4 ? GL_RGBA : GL_RGB;
uint32_t textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(data);
auto texture = Ptr<Texture>(new Texture(width, height, textureID));
texture->path_ = resolvedPath;
texture->channels_ = channels;
SDL_Log("Loaded texture: %s (%dx%d, %d channels)", resolvedPath.c_str(), width, height, channels);
return texture;
}
Ptr<Texture> Texture::createFromMemory(uint8* data, int width, int height, int channels) {
if (!data) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture from memory: null data");
return nullptr;
}
uint32_t format = channels == 4 ? GL_RGBA : GL_RGB;
uint32_t textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
auto texture = Ptr<Texture>(new Texture(width, height, textureID));
texture->channels_ = channels;
return texture;
}
Ptr<Texture> Texture::createEmpty(int width, int height) {
uint32_t textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
return Ptr<Texture>(new Texture(width, height, textureID));
}
void Texture::bind(uint32_t slot) {
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, textureID_);
}
void Texture::unbind() {
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::setWrapMode(uint32_t wrapS, uint32_t wrapT) {
bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
unbind();
}
void Texture::setFilterMode(uint32_t minFilter, uint32_t magFilter) {
bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
unbind();
}
}

View File

@ -2,8 +2,10 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <frostbite2D/core/application.h> #include <frostbite2D/core/application.h>
#include <frostbite2D/core/window.h> #include <frostbite2D/core/window.h>
#include <frostbite2D/graphics/camera.h>
#include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/graphics/texture.h>
#include <frostbite2D/utils/asset.h> #include <frostbite2D/utils/asset.h>
#include <glad/glad.h> #include <glad/glad.h>
using namespace frostbite2D; using namespace frostbite2D;
@ -12,9 +14,9 @@ int main(int argc, char **argv) {
AppConfig config = AppConfig::createDefault(); AppConfig config = AppConfig::createDefault();
config.appName = "Frostbite2D Test App"; config.appName = "Frostbite2D Test App";
config.appVersion = "1.0.0"; config.appVersion = "1.0.0";
config.windowConfig.width = 800; config.windowConfig.width = 1920;
config.windowConfig.height = 600; config.windowConfig.height = 1080;
config.windowConfig.title = "Frostbite2D - Application Test"; config.windowConfig.title = "Frostbite2D - Renderer Test";
Application &app = Application::get(); Application &app = Application::get();
@ -23,8 +25,38 @@ int main(int argc, char **argv) {
return -1; return -1;
} }
auto &renderer = Renderer::get();
renderer.setClearColor(0.1f, 0.1f, 0.15f);
// 获取屏幕实际大小
auto *window = app.getWindow();
int screenWidth = window ? window->width() : config.windowConfig.width;
int screenHeight = window ? window->height() : config.windowConfig.height;
// 设置 Renderer 的视口到屏幕大小
renderer.setViewport(0, 0, screenWidth, screenHeight);
// 相机使用游戏逻辑分辨率 (1280x720)
static Camera camera;
camera.setViewport(config.windowConfig.width, config.windowConfig.height);
renderer.setCamera(&camera);
auto &asset = Asset::get();
#ifdef __SWITCH__
asset.setWorkingDirectory("/switch/testgame");
#else
asset.setWorkingDirectory(".");
#endif
// 初始化着色器管理器
auto &shaderManager = renderer.getShaderManager();
shaderManager.init("shaders");
SDL_Log("Application initialized successfully"); SDL_Log("Application initialized successfully");
SDL_Log("Running main loop..."); SDL_Log("Screen size: %dx%d", screenWidth, screenHeight);
SDL_Log("Render size: %dx%d", config.windowConfig.width,
config.windowConfig.height);
SDL_Log("Starting main loop...");
app.run(); app.run();
@ -32,4 +64,4 @@ int main(int argc, char **argv) {
SDL_Log("Application exited normally"); SDL_Log("Application exited normally");
return 0; return 0;
} }

View File

@ -22,6 +22,7 @@ target("Frostbite2D")
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp")) add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp"))
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c")) add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include")) add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include/json"))
-- 添加资源文件(如果存在) -- 添加资源文件(如果存在)
local rc_file = path.join(os.projectdir(), "resources/app.rc") local rc_file = path.join(os.projectdir(), "resources/app.rc")

View File

@ -0,0 +1,9 @@
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}

19
shaders/colored_quad.vert Normal file
View File

@ -0,0 +1,19 @@
#ifdef GL_ES
precision mediump float;
#endif
attribute vec2 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
uniform mat4 u_view;
uniform mat4 u_projection;
varying vec2 v_texCoord;
varying vec4 v_color;
void main() {
gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
v_color = a_color;
}

13
shaders/sprite.frag Normal file
View File

@ -0,0 +1,13 @@
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord;
varying vec4 v_color;
uniform sampler2D u_texture;
void main() {
vec4 texColor = texture2D(u_texture, v_texCoord);
gl_FragColor = texColor * v_color;
}

20
shaders/sprite.vert Normal file
View File

@ -0,0 +1,20 @@
#ifdef GL_ES
precision mediump float;
#endif
attribute vec2 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
uniform mat4 u_view;
uniform mat4 u_projection;
uniform sampler2D u_texture;
varying vec2 v_texCoord;
varying vec4 v_color;
void main() {
gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
v_color = a_color;
}

47
src/main.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/core/application.h>
#include <frostbite2D/core/window.h>
#include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/utils/asset.h>
#include <glad/glad.h>
using namespace frostbite2D;
int main(int argc, char **argv) {
AppConfig AppConfig::createDefault();
config.appName = "Frostbite2D Test App";
config.appVersion = "1.0.0";
config.windowConfig.width = 800;
config.windowConfig.height = 600;
config.windowConfig.title = "Frostbite2D - Renderer Test";
Application& app = Application::get();
if (!app.init(config)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize application!");
return -1;
}
auto& renderer = Renderer::get();
if (!renderer.init()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize renderer!");
app.shutdown();
return -1;
}
renderer.setClearColor(0.2f, 0.3f, 1.0f);
auto& asset = Asset::get();
asset.setWorkingDirectory("assets");
SDL_Log("Application initialized successfully");
SDL_Log("Starting main loop...");
app.run();
app.shutdown();
SDL_Log("Application exited normally");
return 0;
}