From 475ae50d2af9799d4abe9bb61b60b732ee38eaef Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Sun, 15 Feb 2026 11:12:27 +0800 Subject: [PATCH] =?UTF-8?q?refactor(shader):=20=E9=87=8D=E6=9E=84=E7=9D=80?= =?UTF-8?q?=E8=89=B2=E5=99=A8=E7=B3=BB=E7=BB=9F=E5=B9=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将shader_system.h重命名为shader_manager.h并重构接口 - 新增shader_interface.h作为跨平台着色器抽象 - 实现GLShaderNew作为OpenGL着色器新实现 - 添加shader_cache支持着色器二进制缓存 - 引入shader_hot_reloader实现热重载功能 - 新增shader_loader支持多种着色器文件格式加载 - 添加内置着色器文件到shaders目录 - 更新gl_renderer.cpp使用新的着色器系统 - 扩展platform_detector.h添加资源路径相关方法 - 添加shaders/common目录包含常用GLSL工具函数 重构后的着色器系统提供更完善的缓存、热重载和跨平台支持,同时优化了性能和维护性。 --- .../extra2d/config/platform_detector.h | 36 + Extra2D/include/extra2d/extra2d.h | 2 +- .../extra2d/graphics/opengl/gl_renderer.h | 4 +- .../extra2d/graphics/opengl/gl_shader_new.h | 202 ++++++ .../include/extra2d/graphics/shader_cache.h | 131 ++++ .../extra2d/graphics/shader_hot_reloader.h | 137 ++++ .../extra2d/graphics/shader_interface.h | 152 ++++ .../include/extra2d/graphics/shader_loader.h | 227 ++++++ .../include/extra2d/graphics/shader_manager.h | 251 +++++++ .../include/extra2d/graphics/shader_preset.h | 343 ++------- .../include/extra2d/graphics/shader_system.h | 179 ----- Extra2D/shaders/builtin/font.shader | 56 ++ Extra2D/shaders/builtin/particle.shader | 53 ++ Extra2D/shaders/builtin/postprocess.shader | 42 ++ Extra2D/shaders/builtin/shape.shader | 42 ++ Extra2D/shaders/builtin/sprite.shader | 61 ++ Extra2D/shaders/common/color.glsl | 137 ++++ Extra2D/shaders/common/math.glsl | 96 +++ Extra2D/shaders/effects/blur.shader | 71 ++ Extra2D/shaders/effects/distortion.shader | 64 ++ Extra2D/shaders/effects/grayscale.shader | 61 ++ .../shaders/effects/grayscale_outline.shader | 77 ++ Extra2D/shaders/effects/invert.shader | 60 ++ Extra2D/shaders/effects/outline.shader | 70 ++ Extra2D/shaders/effects/pixelate.shader | 61 ++ .../shaders/effects/pixelate_invert.shader | 66 ++ Extra2D/shaders/effects/water.shader | 63 ++ Extra2D/src/config/platform_detector.cpp | 71 ++ Extra2D/src/graphics/opengl/gl_renderer.cpp | 65 +- Extra2D/src/graphics/opengl/gl_shader_new.cpp | 325 +++++++++ Extra2D/src/graphics/shader_cache.cpp | 278 ++++++++ Extra2D/src/graphics/shader_hot_reloader.cpp | 164 +++++ Extra2D/src/graphics/shader_loader.cpp | 451 ++++++++++++ Extra2D/src/graphics/shader_manager.cpp | 525 ++++++++++++++ Extra2D/src/graphics/shader_preset.cpp | 251 ++----- Extra2D/src/graphics/shader_system.cpp | 667 ------------------ README.md | 221 ------ 37 files changed, 4190 insertions(+), 1572 deletions(-) create mode 100644 Extra2D/include/extra2d/graphics/opengl/gl_shader_new.h create mode 100644 Extra2D/include/extra2d/graphics/shader_cache.h create mode 100644 Extra2D/include/extra2d/graphics/shader_hot_reloader.h create mode 100644 Extra2D/include/extra2d/graphics/shader_interface.h create mode 100644 Extra2D/include/extra2d/graphics/shader_loader.h create mode 100644 Extra2D/include/extra2d/graphics/shader_manager.h delete mode 100644 Extra2D/include/extra2d/graphics/shader_system.h create mode 100644 Extra2D/shaders/builtin/font.shader create mode 100644 Extra2D/shaders/builtin/particle.shader create mode 100644 Extra2D/shaders/builtin/postprocess.shader create mode 100644 Extra2D/shaders/builtin/shape.shader create mode 100644 Extra2D/shaders/builtin/sprite.shader create mode 100644 Extra2D/shaders/common/color.glsl create mode 100644 Extra2D/shaders/common/math.glsl create mode 100644 Extra2D/shaders/effects/blur.shader create mode 100644 Extra2D/shaders/effects/distortion.shader create mode 100644 Extra2D/shaders/effects/grayscale.shader create mode 100644 Extra2D/shaders/effects/grayscale_outline.shader create mode 100644 Extra2D/shaders/effects/invert.shader create mode 100644 Extra2D/shaders/effects/outline.shader create mode 100644 Extra2D/shaders/effects/pixelate.shader create mode 100644 Extra2D/shaders/effects/pixelate_invert.shader create mode 100644 Extra2D/shaders/effects/water.shader create mode 100644 Extra2D/src/graphics/opengl/gl_shader_new.cpp create mode 100644 Extra2D/src/graphics/shader_cache.cpp create mode 100644 Extra2D/src/graphics/shader_hot_reloader.cpp create mode 100644 Extra2D/src/graphics/shader_loader.cpp create mode 100644 Extra2D/src/graphics/shader_manager.cpp delete mode 100644 Extra2D/src/graphics/shader_system.cpp delete mode 100644 README.md diff --git a/Extra2D/include/extra2d/config/platform_detector.h b/Extra2D/include/extra2d/config/platform_detector.h index c5d6a8e..9507c29 100644 --- a/Extra2D/include/extra2d/config/platform_detector.h +++ b/Extra2D/include/extra2d/config/platform_detector.h @@ -141,6 +141,42 @@ public: */ static std::string getLogPath(const std::string& appName); + /** + * @brief 获取平台特定的资源路径(Shader、纹理等) + * Switch平台使用romfs,其他平台使用相对路径 + * @param appName 应用名称 + * @return 资源目录路径 + */ + static std::string getResourcePath(const std::string& appName = ""); + + /** + * @brief 获取平台特定的Shader路径 + * @param appName 应用名称 + * @return Shader目录路径 + */ + static std::string getShaderPath(const std::string& appName = ""); + + /** + * @brief 获取平台特定的Shader缓存路径 + * Switch平台使用sdmc,其他平台使用系统缓存目录 + * @param appName 应用名称 + * @return Shader缓存目录路径 + */ + static std::string getShaderCachePath(const std::string& appName = ""); + + /** + * @brief 检查平台是否使用romfs(只读文件系统) + * @return 使用romfs返回true + */ + static bool usesRomfs(); + + /** + * @brief 检查平台是否支持热重载 + * Switch平台不支持热重载(romfs只读) + * @return 支持热重载返回true + */ + static bool supportsHotReload(); + /** * @brief 检查平台是否为小端字节序 * @return 如果是小端字节序返回 true diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index bffb894..c6b04ec 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h b/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h index f977e68..b2bd972 100644 --- a/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h +++ b/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h @@ -1,8 +1,8 @@ #pragma once -#include #include #include +#include #include #include @@ -90,7 +90,7 @@ private: IWindow* window_; GLSpriteBatch spriteBatch_; - GLShader shapeShader_; + Ptr shapeShader_; GLuint shapeVao_; GLuint shapeVbo_; diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_shader_new.h b/Extra2D/include/extra2d/graphics/opengl/gl_shader_new.h new file mode 100644 index 0000000..a832af8 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/opengl/gl_shader_new.h @@ -0,0 +1,202 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// OpenGL Shader实现 +// ============================================================================ +class GLShaderNew : public IShader { +public: + /** + * @brief 构造函数 + */ + GLShaderNew(); + + /** + * @brief 析构函数 + */ + ~GLShaderNew() override; + + /** + * @brief 绑定Shader程序 + */ + void bind() const override; + + /** + * @brief 解绑Shader程序 + */ + void unbind() const override; + + /** + * @brief 设置布尔类型uniform变量 + * @param name uniform变量名 + * @param value 布尔值 + */ + void setBool(const std::string& name, bool value) override; + + /** + * @brief 设置整数类型uniform变量 + * @param name uniform变量名 + * @param value 整数值 + */ + void setInt(const std::string& name, int value) override; + + /** + * @brief 设置浮点类型uniform变量 + * @param name uniform变量名 + * @param value 浮点值 + */ + void setFloat(const std::string& name, float value) override; + + /** + * @brief 设置二维向量类型uniform变量 + * @param name uniform变量名 + * @param value 二维向量值 + */ + void setVec2(const std::string& name, const glm::vec2& value) override; + + /** + * @brief 设置三维向量类型uniform变量 + * @param name uniform变量名 + * @param value 三维向量值 + */ + void setVec3(const std::string& name, const glm::vec3& value) override; + + /** + * @brief 设置四维向量类型uniform变量 + * @param name uniform变量名 + * @param value 四维向量值 + */ + void setVec4(const std::string& name, const glm::vec4& value) override; + + /** + * @brief 设置4x4矩阵类型uniform变量 + * @param name uniform变量名 + * @param value 4x4矩阵值 + */ + void setMat4(const std::string& name, const glm::mat4& value) override; + + /** + * @brief 设置颜色类型uniform变量 + * @param name uniform变量名 + * @param color 颜色值 + */ + void setColor(const std::string& name, const Color& color) override; + + /** + * @brief 检查Shader是否有效 + * @return 有效返回true,否则返回false + */ + bool isValid() const override { return programID_ != 0; } + + /** + * @brief 获取原生句柄(OpenGL程序ID) + * @return OpenGL程序ID + */ + uint32_t getNativeHandle() const override { return programID_; } + + /** + * @brief 获取Shader名称 + * @return Shader名称 + */ + const std::string& getName() const override { return name_; } + + /** + * @brief 设置Shader名称 + * @param name Shader名称 + */ + void setName(const std::string& name) override { name_ = name; } + + /** + * @brief 从源码编译Shader + * @param vertexSource 顶点着色器源码 + * @param fragmentSource 片段着色器源码 + * @return 编译成功返回true,失败返回false + */ + bool compileFromSource(const char* vertexSource, const char* fragmentSource); + + /** + * @brief 从二进制数据创建Shader + * @param binary 二进制数据 + * @return 创建成功返回true,失败返回false + */ + bool compileFromBinary(const std::vector& binary); + + /** + * @brief 获取Shader二进制数据 + * @param outBinary 输出的二进制数据 + * @return 成功返回true,失败返回false + */ + bool getBinary(std::vector& outBinary); + + /** + * @brief 获取OpenGL程序ID + * @return OpenGL程序ID + */ + GLuint getProgramID() const { return programID_; } + +private: + GLuint programID_ = 0; + std::string name_; + std::unordered_map uniformCache_; + + /** + * @brief 编译单个着色器 + * @param type 着色器类型 + * @param source 着色器源码 + * @return 着色器ID,失败返回0 + */ + GLuint compileShader(GLenum type, const char* source); + + /** + * @brief 获取uniform位置 + * @param name uniform变量名 + * @return uniform位置 + */ + GLint getUniformLocation(const std::string& name); +}; + +// ============================================================================ +// OpenGL Shader工厂 +// ============================================================================ +class GLShaderFactory : public IShaderFactory { +public: + /** + * @brief 从源码创建Shader + * @param name Shader名称 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 创建的Shader实例 + */ + Ptr createFromSource( + const std::string& name, + const std::string& vertSource, + const std::string& fragSource) override; + + /** + * @brief 从缓存二进制创建Shader + * @param name Shader名称 + * @param binary 编译后的二进制数据 + * @return 创建的Shader实例 + */ + Ptr createFromBinary( + const std::string& name, + const std::vector& binary) override; + + /** + * @brief 获取Shader的二进制数据 + * @param shader Shader实例 + * @param outBinary 输出的二进制数据 + * @return 成功返回true,失败返回false + */ + bool getShaderBinary(const IShader& shader, + std::vector& outBinary) override; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_cache.h b/Extra2D/include/extra2d/graphics/shader_cache.h new file mode 100644 index 0000000..1ab93f5 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/shader_cache.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// Shader缓存条目 +// ============================================================================ +struct ShaderCacheEntry { + std::string name; + std::string sourceHash; + uint64_t compileTime = 0; + std::vector binary; + std::vector dependencies; +}; + +// ============================================================================ +// Shader缓存管理器 +// ============================================================================ +class ShaderCache { +public: + /** + * @brief 获取单例实例 + * @return 缓存管理器实例引用 + */ + static ShaderCache& getInstance(); + + /** + * @brief 初始化缓存系统 + * @param cacheDir 缓存目录路径 + * @return 初始化成功返回true,失败返回false + */ + bool init(const std::string& cacheDir); + + /** + * @brief 关闭缓存系统 + */ + void shutdown(); + + /** + * @brief 检查缓存是否有效 + * @param name Shader名称 + * @param sourceHash 源码哈希值 + * @return 缓存有效返回true,否则返回false + */ + bool hasValidCache(const std::string& name, const std::string& sourceHash); + + /** + * @brief 加载缓存的二进制数据 + * @param name Shader名称 + * @return 缓存条目指针,不存在返回nullptr + */ + Ptr loadCache(const std::string& name); + + /** + * @brief 保存编译结果到缓存 + * @param entry 缓存条目 + * @return 保存成功返回true,失败返回false + */ + bool saveCache(const ShaderCacheEntry& entry); + + /** + * @brief 使缓存失效 + * @param name Shader名称 + */ + void invalidate(const std::string& name); + + /** + * @brief 清除所有缓存 + */ + void clearAll(); + + /** + * @brief 计算源码哈希值 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 哈希值字符串 + */ + static std::string computeHash(const std::string& vertSource, + const std::string& fragSource); + + /** + * @brief 检查是否已初始化 + * @return 已初始化返回true,否则返回false + */ + bool isInitialized() const { return initialized_; } + +private: + ShaderCache() = default; + ~ShaderCache() = default; + ShaderCache(const ShaderCache&) = delete; + ShaderCache& operator=(const ShaderCache&) = delete; + + std::string cacheDir_; + std::unordered_map cacheMap_; + bool initialized_ = false; + + /** + * @brief 加载缓存索引 + * @return 加载成功返回true,失败返回false + */ + bool loadCacheIndex(); + + /** + * @brief 保存缓存索引 + * @return 保存成功返回true,失败返回false + */ + bool saveCacheIndex(); + + /** + * @brief 获取缓存文件路径 + * @param name Shader名称 + * @return 缓存文件完整路径 + */ + std::string getCachePath(const std::string& name) const; + + /** + * @brief 确保缓存目录存在 + * @return 目录存在或创建成功返回true,否则返回false + */ + bool ensureCacheDirectory(); +}; + +// 便捷宏 +#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_hot_reloader.h b/Extra2D/include/extra2d/graphics/shader_hot_reloader.h new file mode 100644 index 0000000..d5ddebc --- /dev/null +++ b/Extra2D/include/extra2d/graphics/shader_hot_reloader.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +namespace extra2d { + +// ============================================================================ +// 文件变化事件 +// ============================================================================ +struct FileChangeEvent { + std::string filepath; + + enum class Type { + Created, + Modified, + Deleted, + Renamed + } type; + + uint64_t timestamp = 0; +}; + +// ============================================================================ +// 文件变化回调 +// ============================================================================ +using FileChangeCallback = std::function; + +// ============================================================================ +// Shader热重载管理器 +// ============================================================================ +class ShaderHotReloader { +public: + /** + * @brief 获取单例实例 + * @return 热重载管理器实例引用 + */ + static ShaderHotReloader& getInstance(); + + /** + * @brief 初始化热重载系统 + * @return 初始化成功返回true,失败返回false + */ + bool init(); + + /** + * @brief 关闭热重载系统 + */ + void shutdown(); + + /** + * @brief 注册Shader文件监视 + * @param shaderName Shader名称 + * @param filePaths 要监视的文件列表 + * @param callback 文件变化时的回调 + */ + void watch(const std::string& shaderName, + const std::vector& filePaths, + FileChangeCallback callback); + + /** + * @brief 取消监视 + * @param shaderName Shader名称 + */ + void unwatch(const std::string& shaderName); + + /** + * @brief 更新文件监视(在主循环中调用) + */ + void update(); + + /** + * @brief 启用/禁用热重载 + * @param enabled 是否启用 + */ + void setEnabled(bool enabled); + + /** + * @brief 检查是否启用 + * @return 启用返回true,否则返回false + */ + bool isEnabled() const { return enabled_; } + + /** + * @brief 检查是否已初始化 + * @return 已初始化返回true,否则返回false + */ + bool isInitialized() const { return initialized_; } + +private: + ShaderHotReloader() = default; + ~ShaderHotReloader() = default; + ShaderHotReloader(const ShaderHotReloader&) = delete; + ShaderHotReloader& operator=(const ShaderHotReloader&) = delete; + + bool enabled_ = false; + bool initialized_ = false; + + struct WatchInfo { + std::vector filePaths; + FileChangeCallback callback; + std::unordered_map modifiedTimes; + }; + std::unordered_map watchMap_; + +#ifdef _WIN32 + HANDLE watchHandle_ = nullptr; + std::vector buffer_; + std::string watchDir_; + bool watching_ = false; +#endif + + /** + * @brief 轮询检查文件变化 + */ + void pollChanges(); + + /** + * @brief 获取文件修改时间 + * @param filepath 文件路径 + * @return 修改时间戳 + */ + static uint64_t getFileModifiedTime(const std::string& filepath); +}; + +// 便捷宏 +#define E2D_SHADER_HOT_RELOADER() ::extra2d::ShaderHotReloader::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_interface.h b/Extra2D/include/extra2d/graphics/shader_interface.h new file mode 100644 index 0000000..e343d1d --- /dev/null +++ b/Extra2D/include/extra2d/graphics/shader_interface.h @@ -0,0 +1,152 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +class Color; + +// ============================================================================ +// Shader抽象接口 - 渲染后端无关 +// ============================================================================ +class IShader { +public: + virtual ~IShader() = default; + + /** + * @brief 绑定Shader程序 + */ + virtual void bind() const = 0; + + /** + * @brief 解绑Shader程序 + */ + virtual void unbind() const = 0; + + /** + * @brief 设置布尔类型uniform变量 + * @param name uniform变量名 + * @param value 布尔值 + */ + virtual void setBool(const std::string& name, bool value) = 0; + + /** + * @brief 设置整数类型uniform变量 + * @param name uniform变量名 + * @param value 整数值 + */ + virtual void setInt(const std::string& name, int value) = 0; + + /** + * @brief 设置浮点类型uniform变量 + * @param name uniform变量名 + * @param value 浮点值 + */ + virtual void setFloat(const std::string& name, float value) = 0; + + /** + * @brief 设置二维向量类型uniform变量 + * @param name uniform变量名 + * @param value 二维向量值 + */ + virtual void setVec2(const std::string& name, const glm::vec2& value) = 0; + + /** + * @brief 设置三维向量类型uniform变量 + * @param name uniform变量名 + * @param value 三维向量值 + */ + virtual void setVec3(const std::string& name, const glm::vec3& value) = 0; + + /** + * @brief 设置四维向量类型uniform变量 + * @param name uniform变量名 + * @param value 四维向量值 + */ + virtual void setVec4(const std::string& name, const glm::vec4& value) = 0; + + /** + * @brief 设置4x4矩阵类型uniform变量 + * @param name uniform变量名 + * @param value 4x4矩阵值 + */ + virtual void setMat4(const std::string& name, const glm::mat4& value) = 0; + + /** + * @brief 设置颜色类型uniform变量 + * @param name uniform变量名 + * @param color 颜色值 + */ + virtual void setColor(const std::string& name, const Color& color) = 0; + + /** + * @brief 检查Shader是否有效 + * @return 有效返回true,否则返回false + */ + virtual bool isValid() const = 0; + + /** + * @brief 获取原生句柄(如OpenGL程序ID) + * @return 原生句柄值 + */ + virtual uint32_t getNativeHandle() const = 0; + + /** + * @brief 获取Shader名称 + * @return Shader名称 + */ + virtual const std::string& getName() const = 0; + + /** + * @brief 设置Shader名称 + * @param name Shader名称 + */ + virtual void setName(const std::string& name) = 0; +}; + +// ============================================================================ +// Shader工厂接口 - 用于创建渲染后端特定的Shader实例 +// ============================================================================ +class IShaderFactory { +public: + virtual ~IShaderFactory() = default; + + /** + * @brief 从源码创建Shader + * @param name Shader名称 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 创建的Shader实例 + */ + virtual Ptr createFromSource( + const std::string& name, + const std::string& vertSource, + const std::string& fragSource) = 0; + + /** + * @brief 从缓存二进制创建Shader + * @param name Shader名称 + * @param binary 编译后的二进制数据 + * @return 创建的Shader实例 + */ + virtual Ptr createFromBinary( + const std::string& name, + const std::vector& binary) = 0; + + /** + * @brief 获取Shader的二进制数据(用于缓存) + * @param shader Shader实例 + * @param outBinary 输出的二进制数据 + * @return 成功返回true,失败返回false + */ + virtual bool getShaderBinary(const IShader& shader, + std::vector& outBinary) = 0; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_loader.h b/Extra2D/include/extra2d/graphics/shader_loader.h new file mode 100644 index 0000000..3f8bdc3 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/shader_loader.h @@ -0,0 +1,227 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// Shader加载结果 +// ============================================================================ +struct ShaderLoadResult { + bool success = false; + std::string errorMessage; + std::string vertSource; + std::string fragSource; + std::vector dependencies; +}; + +// ============================================================================ +// Shader元数据 +// ============================================================================ +struct ShaderMetadata { + std::string name; + std::string vertPath; + std::string fragPath; + std::string combinedPath; + uint64_t lastModified = 0; + std::vector defines; + std::unordered_map uniforms; +}; + +// ============================================================================ +// ShaderLoader接口 - 支持多种文件格式加载 +// ============================================================================ +class IShaderLoader { +public: + virtual ~IShaderLoader() = default; + + /** + * @brief 从分离文件加载Shader (.vert + .frag) + * @param name Shader名称 + * @param vertPath 顶点着色器文件路径 + * @param fragPath 片段着色器文件路径 + * @return 加载结果 + */ + virtual ShaderLoadResult loadFromSeparateFiles( + const std::string& name, + const std::string& vertPath, + const std::string& fragPath) = 0; + + /** + * @brief 从组合文件加载Shader (.shader) + * @param path 组合Shader文件路径 + * @return 加载结果 + */ + virtual ShaderLoadResult loadFromCombinedFile(const std::string& path) = 0; + + /** + * @brief 从源码字符串加载Shader + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 加载结果 + */ + virtual ShaderLoadResult loadFromSource( + const std::string& vertSource, + const std::string& fragSource) = 0; + + /** + * @brief 处理Shader源码中的#include指令 + * @param source 原始源码 + * @param baseDir 基础目录 + * @param outDependencies 输出依赖列表 + * @return 处理后的源码 + */ + virtual std::string processIncludes( + const std::string& source, + const std::string& baseDir, + std::vector& outDependencies) = 0; + + /** + * @brief 应用预处理器定义 + * @param source 原始源码 + * @param defines 预处理器定义列表 + * @return 处理后的源码 + */ + virtual std::string applyDefines( + const std::string& source, + const std::vector& defines) = 0; + + /** + * @brief 获取Shader元数据 + * @param path Shader文件路径 + * @return 元数据 + */ + virtual ShaderMetadata getMetadata(const std::string& path) = 0; +}; + +// ============================================================================ +// 默认ShaderLoader实现 +// ============================================================================ +class ShaderLoader : public IShaderLoader { +public: + ShaderLoader(); + ~ShaderLoader() override = default; + + /** + * @brief 从分离文件加载Shader (.vert + .frag) + * @param name Shader名称 + * @param vertPath 顶点着色器文件路径 + * @param fragPath 片段着色器文件路径 + * @return 加载结果 + */ + ShaderLoadResult loadFromSeparateFiles( + const std::string& name, + const std::string& vertPath, + const std::string& fragPath) override; + + /** + * @brief 从组合文件加载Shader (.shader) + * @param path 组合Shader文件路径 + * @return 加载结果 + */ + ShaderLoadResult loadFromCombinedFile(const std::string& path) override; + + /** + * @brief 从源码字符串加载Shader + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 加载结果 + */ + ShaderLoadResult loadFromSource( + const std::string& vertSource, + const std::string& fragSource) override; + + /** + * @brief 处理Shader源码中的#include指令 + * @param source 原始源码 + * @param baseDir 基础目录 + * @param outDependencies 输出依赖列表 + * @return 处理后的源码 + */ + std::string processIncludes( + const std::string& source, + const std::string& baseDir, + std::vector& outDependencies) override; + + /** + * @brief 应用预处理器定义 + * @param source 原始源码 + * @param defines 预处理器定义列表 + * @return 处理后的源码 + */ + std::string applyDefines( + const std::string& source, + const std::vector& defines) override; + + /** + * @brief 获取Shader元数据 + * @param path Shader文件路径 + * @return 元数据 + */ + ShaderMetadata getMetadata(const std::string& path) override; + + /** + * @brief 添加include搜索路径 + * @param path 搜索路径 + */ + void addIncludePath(const std::string& path); + + /** + * @brief 读取文件内容 + * @param filepath 文件路径 + * @return 文件内容字符串 + */ + static std::string readFile(const std::string& filepath); + + /** + * @brief 获取文件修改时间 + * @param filepath 文件路径 + * @return 修改时间戳 + */ + static uint64_t getFileModifiedTime(const std::string& filepath); + + /** + * @brief 检查文件是否存在 + * @param filepath 文件路径 + * @return 存在返回true,否则返回false + */ + static bool fileExists(const std::string& filepath); + +private: + std::vector includePaths_; + std::unordered_map includeCache_; + + /** + * @brief 解析组合Shader文件 + * @param content 文件内容 + * @param outVert 输出顶点着色器源码 + * @param outFrag 输出片段着色器源码 + * @param outMetadata 输出元数据 + * @return 解析成功返回true,失败返回false + */ + bool parseCombinedFile(const std::string& content, + std::string& outVert, + std::string& outFrag, + ShaderMetadata& outMetadata); + + /** + * @brief 解析元数据JSON块 + * @param jsonContent JSON内容 + * @param outMetadata 输出元数据 + * @return 解析成功返回true,失败返回false + */ + bool parseMetadata(const std::string& jsonContent, ShaderMetadata& outMetadata); + + /** + * @brief 查找include文件路径 + * @param includeName include文件名 + * @param baseDir 基础目录 + * @return 找到的完整路径,未找到返回空字符串 + */ + std::string findIncludeFile(const std::string& includeName, const std::string& baseDir); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_manager.h b/Extra2D/include/extra2d/graphics/shader_manager.h new file mode 100644 index 0000000..ce34937 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/shader_manager.h @@ -0,0 +1,251 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// Shader重载回调 +// ============================================================================ +using ShaderReloadCallback = std::function newShader)>; + +// ============================================================================ +// Shader管理器 - 统一入口 +// ============================================================================ +class ShaderManager { +public: + /** + * @brief 获取单例实例 + * @return Shader管理器实例引用 + */ + static ShaderManager& getInstance(); + + // ------------------------------------------------------------------------ + // 初始化和关闭 + // ------------------------------------------------------------------------ + + /** + * @brief 使用平台默认路径初始化Shader系统 + * 自动检测平台并使用正确的路径(romfs/sdmc/相对路径) + * @param factory 渲染后端Shader工厂 + * @param appName 应用名称(用于缓存目录) + * @return 初始化成功返回true,失败返回false + */ + bool init(Ptr factory, const std::string& appName = "extra2d"); + + /** + * @brief 初始化Shader系统 + * @param shaderDir Shader文件目录 + * @param cacheDir 缓存目录 + * @param factory 渲染后端Shader工厂 + * @return 初始化成功返回true,失败返回false + */ + bool init(const std::string& shaderDir, + const std::string& cacheDir, + Ptr factory); + + /** + * @brief 关闭Shader系统 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + * @return 已初始化返回true,否则返回false + */ + bool isInitialized() const { return initialized_; } + + /** + * @brief 检查当前平台是否支持热重载 + * Switch平台使用romfs,不支持热重载 + * @return 支持热重载返回true + */ + bool isHotReloadSupported() const { return hotReloadSupported_; } + + // ------------------------------------------------------------------------ + // Shader加载 + // ------------------------------------------------------------------------ + + /** + * @brief 从分离文件加载Shader + * @param name Shader名称 + * @param vertPath 顶点着色器文件路径 + * @param fragPath 片段着色器文件路径 + * @return 加载的Shader实例 + */ + Ptr loadFromFiles(const std::string& name, + const std::string& vertPath, + const std::string& fragPath); + + /** + * @brief 从组合文件加载Shader + * @param path 组合Shader文件路径 + * @return 加载的Shader实例 + */ + Ptr loadFromCombinedFile(const std::string& path); + + /** + * @brief 从源码加载Shader + * @param name Shader名称 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 加载的Shader实例 + */ + Ptr loadFromSource(const std::string& name, + const std::string& vertSource, + const std::string& fragSource); + + /** + * @brief 获取已加载的Shader + * @param name Shader名称 + * @return Shader实例,不存在返回nullptr + */ + Ptr get(const std::string& name) const; + + /** + * @brief 检查Shader是否存在 + * @param name Shader名称 + * @return 存在返回true,否则返回false + */ + bool has(const std::string& name) const; + + /** + * @brief 移除Shader + * @param name Shader名称 + */ + void remove(const std::string& name); + + /** + * @brief 清除所有Shader + */ + void clear(); + + // ------------------------------------------------------------------------ + // 热重载 + // ------------------------------------------------------------------------ + + /** + * @brief 注册重载回调 + * @param name Shader名称 + * @param callback 重载回调函数 + */ + void setReloadCallback(const std::string& name, ShaderReloadCallback callback); + + /** + * @brief 启用/禁用热重载 + * @param enabled 是否启用 + */ + void setHotReloadEnabled(bool enabled); + + /** + * @brief 检查热重载是否启用 + * @return 启用返回true,否则返回false + */ + bool isHotReloadEnabled() const; + + /** + * @brief 更新热重载系统(主循环调用) + */ + void update(); + + /** + * @brief 手动重载Shader + * @param name Shader名称 + * @return 重载成功返回true,失败返回false + */ + bool reload(const std::string& name); + + // ------------------------------------------------------------------------ + // 内置Shader + // ------------------------------------------------------------------------ + + /** + * @brief 获取内置Shader + * @param name 内置Shader名称 + * @return Shader实例 + */ + Ptr getBuiltin(const std::string& name); + + /** + * @brief 加载所有内置Shader + * @return 加载成功返回true,失败返回false + */ + bool loadBuiltinShaders(); + + // ------------------------------------------------------------------------ + // 工具方法 + // ------------------------------------------------------------------------ + + /** + * @brief 获取Shader目录 + * @return Shader目录路径 + */ + const std::string& getShaderDir() const { return shaderDir_; } + + /** + * @brief 获取ShaderLoader + * @return ShaderLoader引用 + */ + ShaderLoader& getLoader() { return loader_; } + +private: + ShaderManager() = default; + ~ShaderManager() = default; + ShaderManager(const ShaderManager&) = delete; + ShaderManager& operator=(const ShaderManager&) = delete; + + std::string shaderDir_; + std::string cacheDir_; + Ptr factory_; + ShaderLoader loader_; + + struct ShaderInfo { + Ptr shader; + ShaderMetadata metadata; + ShaderReloadCallback reloadCallback; + std::string vertSource; + std::string fragSource; + std::vector filePaths; + }; + std::unordered_map shaders_; + + bool initialized_ = false; + bool hotReloadEnabled_ = false; + bool hotReloadSupported_ = true; + + /** + * @brief 从缓存加载Shader + * @param name Shader名称 + * @param sourceHash 源码哈希值 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return Shader实例 + */ + Ptr loadFromCache(const std::string& name, + const std::string& sourceHash, + const std::string& vertSource, + const std::string& fragSource); + + /** + * @brief 创建内置Shader源码 + */ + void createBuiltinShaderSources(); + + /** + * @brief 处理文件变化事件 + * @param shaderName Shader名称 + * @param event 文件变化事件 + */ + void handleFileChange(const std::string& shaderName, const FileChangeEvent& event); +}; + +// 便捷宏 +#define E2D_SHADER_MANAGER() ::extra2d::ShaderManager::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_preset.h b/Extra2D/include/extra2d/graphics/shader_preset.h index 801f88b..18dd3fc 100644 --- a/Extra2D/include/extra2d/graphics/shader_preset.h +++ b/Extra2D/include/extra2d/graphics/shader_preset.h @@ -1,8 +1,8 @@ #pragma once -#include -#include #include +#include +#include #include namespace extra2d { @@ -39,281 +39,74 @@ struct BlurParams { float radius = 5.0f; }; -namespace ShaderSource { - -static const char* StandardVert = R"( -#version 300 es -precision highp float; -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} -)"; - -static const char* StandardFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord); - fragColor = texColor * v_color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -static const char* WaterFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_waveSpeed; -uniform float u_waveAmplitude; -uniform float u_waveFrequency; -uniform float u_time; - -out vec4 fragColor; - -void main() { - vec2 uv = v_texCoord; - - // 水波纹效果 - float wave = sin(uv.y * u_waveFrequency + u_time * u_waveSpeed) * u_waveAmplitude; - uv.x += wave; - - vec4 texColor = texture(u_texture, uv); - fragColor = texColor * v_color; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -static const char* OutlineFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform vec4 u_outlineColor; -uniform float u_thickness; -uniform vec2 u_textureSize; - -out vec4 fragColor; - -void main() { - vec4 color = texture(u_texture, v_texCoord); - - // 简单的描边检测 - float alpha = 0.0; - vec2 offset = u_thickness / u_textureSize; - - alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a; - - if (color.a < 0.1 && alpha > 0.0) { - fragColor = u_outlineColor; - } else { - fragColor = color; - } - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -static const char* DistortionFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_distortionAmount; -uniform float u_time; -uniform float u_timeScale; - -out vec4 fragColor; - -void main() { - vec2 uv = v_texCoord; - - // 扭曲效果 - float t = u_time * u_timeScale; - float dx = sin(uv.y * 10.0 + t) * u_distortionAmount; - float dy = cos(uv.x * 10.0 + t) * u_distortionAmount; - uv += vec2(dx, dy); - - vec4 texColor = texture(u_texture, uv); - fragColor = texColor * v_color; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -static const char* PixelateFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_pixelSize; -uniform vec2 u_textureSize; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec2 pixel = u_pixelSize / u_textureSize; - vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5; - - vec4 texColor = texture(u_texture, uv); - fragColor = texColor * v_color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -static const char* InvertFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_strength; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord) * v_color; - vec3 inverted = vec3(1.0) - texColor.rgb; - texColor.rgb = mix(texColor.rgb, inverted, u_strength); - - fragColor = texColor; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -static const char* GrayscaleFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_intensity; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord) * v_color; - - float gray = dot(texColor.rgb, vec3(0.299, 0.587, 0.114)); - texColor.rgb = mix(texColor.rgb, vec3(gray), u_intensity); - - fragColor = texColor; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -static const char* BlurFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_radius; -uniform vec2 u_textureSize; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec2 texel = u_radius / u_textureSize; - - vec4 sum = vec4(0.0); - sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, -1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, -1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, -1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 0.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 0.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 0.0)); - sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 1.0)); - sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 1.0)); - - vec4 texColor = sum / 9.0; - fragColor = texColor * v_color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -} // namespace ShaderSource - class ShaderPreset { public: - static Ptr Water(const WaterParams& params); - static Ptr Outline(const OutlineParams& params); - static Ptr Distortion(const DistortionParams& params); - static Ptr Pixelate(const PixelateParams& params); - static Ptr Invert(const InvertParams& params); - static Ptr Grayscale(const GrayscaleParams& params); - static Ptr Blur(const BlurParams& params); - - static Ptr GrayscaleOutline(const GrayscaleParams& grayParams, - const OutlineParams& outlineParams); - static Ptr PixelateInvert(const PixelateParams& pixParams, - const InvertParams& invParams); + /** + * @brief 创建水波纹效果着色器 + * @param params 水波纹效果参数 + * @return 配置好的着色器 + */ + static Ptr Water(const WaterParams& params = {}); + + /** + * @brief 创建描边效果着色器 + * @param params 描边效果参数 + * @return 配置好的着色器 + */ + static Ptr Outline(const OutlineParams& params = {}); + + /** + * @brief 创建扭曲效果着色器 + * @param params 扭曲效果参数 + * @return 配置好的着色器 + */ + static Ptr Distortion(const DistortionParams& params = {}); + + /** + * @brief 创建像素化效果着色器 + * @param params 像素化效果参数 + * @return 配置好的着色器 + */ + static Ptr Pixelate(const PixelateParams& params = {}); + + /** + * @brief 创建反相效果着色器 + * @param params 反相效果参数 + * @return 配置好的着色器 + */ + static Ptr Invert(const InvertParams& params = {}); + + /** + * @brief 创建灰度效果着色器 + * @param params 灰度效果参数 + * @return 配置好的着色器 + */ + static Ptr Grayscale(const GrayscaleParams& params = {}); + + /** + * @brief 创建模糊效果着色器 + * @param params 模糊效果参数 + * @return 配置好的着色器 + */ + static Ptr Blur(const BlurParams& params = {}); + + /** + * @brief 创建灰度+描边组合效果着色器 + * @param grayParams 灰度效果参数 + * @param outlineParams 描边效果参数 + * @return 配置好的着色器 + */ + static Ptr GrayscaleOutline(const GrayscaleParams& grayParams, + const OutlineParams& outlineParams); + + /** + * @brief 创建像素化+反相组合效果着色器 + * @param pixParams 像素化效果参数 + * @param invParams 反相效果参数 + * @return 配置好的着色器 + */ + static Ptr PixelateInvert(const PixelateParams& pixParams, + const InvertParams& invParams); }; } // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_system.h b/Extra2D/include/extra2d/graphics/shader_system.h deleted file mode 100644 index 11d281a..0000000 --- a/Extra2D/include/extra2d/graphics/shader_system.h +++ /dev/null @@ -1,179 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// Shader参数绑定回调 -// ============================================================================ -using ShaderBindCallback = std::function; - -// ============================================================================ -// Shader系统 - 管理所有Shader的加载、缓存和热重载 -// ============================================================================ -class ShaderSystem { -public: - // ------------------------------------------------------------------------ - // 单例访问 - // ------------------------------------------------------------------------ - static ShaderSystem &get(); - - // ------------------------------------------------------------------------ - // 初始化和关闭 - // ------------------------------------------------------------------------ - bool init(); - void shutdown(); - - // ------------------------------------------------------------------------ - // Shader加载 - // ------------------------------------------------------------------------ - - /** - * @brief 从文件加载Shader - * @param name Shader名称(用于缓存) - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载的Shader,失败返回nullptr - */ - Ptr loadFromFile(const std::string &name, - const std::string &vertPath, - const std::string &fragPath); - - /** - * @brief 从源码字符串加载Shader - * @param name Shader名称(用于缓存) - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载的Shader,失败返回nullptr - */ - Ptr loadFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource); - - /** - * @brief 从已编译的程序获取Shader - * @param name Shader名称 - * @return 缓存的Shader,不存在返回nullptr - */ - Ptr get(const std::string &name); - - /** - * @brief 检查Shader是否存在 - */ - bool has(const std::string &name) const; - - // ------------------------------------------------------------------------ - // Shader移除 - // ------------------------------------------------------------------------ - void remove(const std::string &name); - void clear(); - - // ------------------------------------------------------------------------ - // 热重载支持 - // ------------------------------------------------------------------------ - - /** - * @brief 启用/禁用文件监视 - */ - void setFileWatching(bool enable); - bool isFileWatching() const { return fileWatching_; } - - /** - * @brief 更新文件监视(在主循环中调用) - */ - void updateFileWatching(); - - /** - * @brief 重载指定Shader - */ - bool reload(const std::string &name); - - /** - * @brief 重载所有Shader - */ - void reloadAll(); - - // ------------------------------------------------------------------------ - // 内置Shader获取 - // ------------------------------------------------------------------------ - Ptr getBuiltinSpriteShader(); - Ptr getBuiltinParticleShader(); - Ptr getBuiltinPostProcessShader(); - Ptr getBuiltinShapeShader(); - - // ------------------------------------------------------------------------ - // 工具方法 - // ------------------------------------------------------------------------ - - /** - * @brief 从文件读取文本内容 - */ - static std::string readFile(const std::string &filepath); - - /** - * @brief 获取Shader文件的最后修改时间 - */ - static uint64_t getFileModifiedTime(const std::string &filepath); - -private: - ShaderSystem() = default; - ~ShaderSystem() = default; - ShaderSystem(const ShaderSystem &) = delete; - ShaderSystem &operator=(const ShaderSystem &) = delete; - - struct ShaderInfo { - Ptr shader; - std::string vertPath; - std::string fragPath; - uint64_t vertModifiedTime; - uint64_t fragModifiedTime; - bool isBuiltin; - }; - - std::unordered_map shaders_; - bool fileWatching_ = false; - float watchTimer_ = 0.0f; - static constexpr float WATCH_INTERVAL = 1.0f; // 检查间隔(秒) - - // 内置Shader缓存 - Ptr builtinSpriteShader_; - Ptr builtinParticleShader_; - Ptr builtinPostProcessShader_; - Ptr builtinShapeShader_; - - bool loadBuiltinShaders(); - void checkAndReload(); -}; - -// ============================================================================ -// Shader参数包装器 - 简化Uniform设置 -// ============================================================================ -class ShaderParams { -public: - explicit ShaderParams(GLShader &shader); - - ShaderParams &setBool(const std::string &name, bool value); - ShaderParams &setInt(const std::string &name, int value); - ShaderParams &setFloat(const std::string &name, float value); - ShaderParams &setVec2(const std::string &name, const glm::vec2 &value); - ShaderParams &setVec3(const std::string &name, const glm::vec3 &value); - ShaderParams &setVec4(const std::string &name, const glm::vec4 &value); - ShaderParams &setMat4(const std::string &name, const glm::mat4 &value); - ShaderParams &setColor(const std::string &name, const Color &color); - -private: - GLShader &shader_; -}; - -// ============================================================================ -// 便捷宏 -// ============================================================================ -#define E2D_SHADER_SYSTEM() ::extra2d::ShaderSystem::get() - -} // namespace extra2d diff --git a/Extra2D/shaders/builtin/font.shader b/Extra2D/shaders/builtin/font.shader new file mode 100644 index 0000000..52e343b --- /dev/null +++ b/Extra2D/shaders/builtin/font.shader @@ -0,0 +1,56 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: font +// Category: builtin +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "font", + "category": "builtin", + "author": "Extra2D Team", + "description": "字体渲染Shader,支持SDF" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_smoothing = 0.1; + +out vec4 fragColor; + +void main() { + float dist = texture(u_texture, v_texCoord).r; + float alpha = smoothstep(0.5 - u_smoothing, 0.5 + u_smoothing, dist); + fragColor = vec4(v_color.rgb, v_color.a * alpha); + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/builtin/particle.shader b/Extra2D/shaders/builtin/particle.shader new file mode 100644 index 0000000..4b1c1d4 --- /dev/null +++ b/Extra2D/shaders/builtin/particle.shader @@ -0,0 +1,53 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: particle +// Category: builtin +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "particle", + "category": "builtin", + "author": "Extra2D Team", + "description": "粒子渲染Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord); + fragColor = texColor * v_color; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/builtin/postprocess.shader b/Extra2D/shaders/builtin/postprocess.shader new file mode 100644 index 0000000..619f997 --- /dev/null +++ b/Extra2D/shaders/builtin/postprocess.shader @@ -0,0 +1,42 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: postprocess +// Category: builtin +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "postprocess", + "category": "builtin", + "author": "Extra2D Team", + "description": "后处理基础Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; + +out vec2 v_texCoord; + +void main() { + gl_Position = vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; + +uniform sampler2D u_texture; + +out vec4 fragColor; + +void main() { + fragColor = texture(u_texture, v_texCoord); +} diff --git a/Extra2D/shaders/builtin/shape.shader b/Extra2D/shaders/builtin/shape.shader new file mode 100644 index 0000000..48809f2 --- /dev/null +++ b/Extra2D/shaders/builtin/shape.shader @@ -0,0 +1,42 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: shape +// Category: builtin +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "shape", + "category": "builtin", + "author": "Extra2D Team", + "description": "形状渲染Shader,支持顶点颜色批处理" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec4 a_color; + +uniform mat4 u_viewProjection; + +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec4 v_color; + +out vec4 fragColor; + +void main() { + fragColor = v_color; +} diff --git a/Extra2D/shaders/builtin/sprite.shader b/Extra2D/shaders/builtin/sprite.shader new file mode 100644 index 0000000..ec30971 --- /dev/null +++ b/Extra2D/shaders/builtin/sprite.shader @@ -0,0 +1,61 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: sprite +// Category: builtin +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "sprite", + "category": "builtin", + "author": "Extra2D Team", + "description": "标准2D精灵渲染Shader", + "uniforms": { + "u_viewProjection": { "type": "mat4", "description": "视图投影矩阵" }, + "u_model": { "type": "mat4", "description": "模型矩阵" }, + "u_opacity": { "type": "float", "default": 1.0, "description": "透明度" } + } +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord); + fragColor = texColor * v_color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/common/color.glsl b/Extra2D/shaders/common/color.glsl new file mode 100644 index 0000000..32a77f3 --- /dev/null +++ b/Extra2D/shaders/common/color.glsl @@ -0,0 +1,137 @@ +// ============================================ +// Common Color Functions +// ============================================ + +#ifndef E2D_COLOR_GLSL +#define E2D_COLOR_GLSL + +/** + * @brief RGB转灰度 + * @param color RGB颜色 + * @return 灰度值 + */ +float rgbToGrayscale(vec3 color) { + return dot(color, vec3(0.299, 0.587, 0.114)); +} + +/** + * @brief RGB转HSV + * @param c RGB颜色 + * @return HSV颜色 + */ +vec3 rgbToHsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +/** + * @brief HSV转RGB + * @param c HSV颜色 + * @return RGB颜色 + */ +vec3 hsvToRgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +/** + * @brief 调整亮度 + * @param color 原始颜色 + * @param amount 亮度调整量 + * @return 调整后的颜色 + */ +vec3 adjustBrightness(vec3 color, float amount) { + return color + amount; +} + +/** + * @brief 调整对比度 + * @param color 原始颜色 + * @param amount 对比度调整量 + * @return 调整后的颜色 + */ +vec3 adjustContrast(vec3 color, float amount) { + return (color - 0.5) * amount + 0.5; +} + +/** + * @brief 调整饱和度 + * @param color 原始颜色 + * @param amount 饱和度调整量 + * @return 调整后的颜色 + */ +vec3 adjustSaturation(vec3 color, float amount) { + float gray = rgbToGrayscale(color); + return mix(vec3(gray), color, amount); +} + +/** + * @brief 颜色混合(正片叠底) + * @param a 底色 + * @param b 混合色 + * @return 混合结果 + */ +vec3 blendMultiply(vec3 a, vec3 b) { + return a * b; +} + +/** + * @brief 颜色混合(滤色) + * @param a 底色 + * @param b 混合色 + * @return 混合结果 + */ +vec3 blendScreen(vec3 a, vec3 b) { + return 1.0 - (1.0 - a) * (1.0 - b); +} + +/** + * @brief 颜色混合(叠加) + * @param a 底色 + * @param b 混合色 + * @return 混合结果 + */ +vec3 blendOverlay(vec3 a, vec3 b) { + return mix( + 2.0 * a * b, + 1.0 - 2.0 * (1.0 - a) * (1.0 - b), + step(0.5, a) + ); +} + +/** + * @brief 颜色调色 + * @param color 原始颜色 + * @param tintColor 色调颜色 + * @param amount 色调强度 + * @return 调色结果 + */ +vec3 tint(vec3 color, vec3 tintColor, float amount) { + return mix(color, tintColor, amount); +} + +/** + * @brief 预乘Alpha + * @param color RGBA颜色 + * @return 预乘后的RGB颜色 + */ +vec3 premultiplyAlpha(vec4 color) { + return color.rgb * color.a; +} + +/** + * @brief 取消预乘Alpha + * @param color RGB颜色 + * @param alpha Alpha值 + * @return 未预乘的RGB颜色 + */ +vec3 unpremultiplyAlpha(vec3 color, float alpha) { + return alpha > 0.0 ? color / alpha : color; +} + +#endif // E2D_COLOR_GLSL diff --git a/Extra2D/shaders/common/math.glsl b/Extra2D/shaders/common/math.glsl new file mode 100644 index 0000000..6d4e067 --- /dev/null +++ b/Extra2D/shaders/common/math.glsl @@ -0,0 +1,96 @@ +// ============================================ +// Common Math Functions +// ============================================ + +#ifndef E2D_MATH_GLSL +#define E2D_MATH_GLSL + +const float PI = 3.14159265359; +const float E = 2.71828182846; + +/** + * @brief 角度转弧度 + * @param deg 角度值 + * @return 弧度值 + */ +float degToRad(float deg) { + return deg * PI / 180.0; +} + +/** + * @brief 弧度转角度 + * @param rad 弧度值 + * @return 角度值 + */ +float radToDeg(float rad) { + return rad * 180.0 / PI; +} + +/** + * @brief 线性插值 + * @param a 起始值 + * @param b 结束值 + * @param t 插值因子 [0, 1] + * @return 插值结果 + */ +float lerp(float a, float b, float t) { + return a + (b - a) * t; +} + +/** + * @brief 平滑插值 + * @param edge0 下边界 + * @param edge1 上边界 + * @param x 输入值 + * @return 平滑插值结果 + */ +float smoothStep(float edge0, float edge1, float x) { + float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +/** + * @brief 2D向量线性插值 + */ +vec2 lerpVec2(vec2 a, vec2 b, float t) { + return a + (b - a) * t; +} + +/** + * @brief 计算两点之间的距离 + */ +float distance2D(vec2 a, vec2 b) { + return length(b - a); +} + +/** + * @brief 计算两点之间的距离平方 + */ +float distance2DSquared(vec2 a, vec2 b) { + vec2 diff = b - a; + return dot(diff, diff); +} + +/** + * @brief 将值限制在范围内 + */ +float clamp01(float x) { + return clamp(x, 0.0, 1.0); +} + +/** + * @brief 重复平铺 + */ +float repeat(float x, float period) { + return mod(x, period); +} + +/** + * @brief 镜像重复 + */ +float mirrorRepeat(float x, float period) { + float m = mod(x, period * 2.0); + return m > period ? period * 2.0 - m : m; +} + +#endif // E2D_MATH_GLSL diff --git a/Extra2D/shaders/effects/blur.shader b/Extra2D/shaders/effects/blur.shader new file mode 100644 index 0000000..7f6eec5 --- /dev/null +++ b/Extra2D/shaders/effects/blur.shader @@ -0,0 +1,71 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: blur +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "blur", + "category": "effects", + "author": "Extra2D Team", + "description": "模糊特效Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_radius; +uniform vec2 u_textureSize; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec2 texel = u_radius / u_textureSize; + + vec4 sum = vec4(0.0); + sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, -1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, -1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, -1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 0.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 0.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 0.0)); + sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 1.0)); + + vec4 texColor = sum / 9.0; + fragColor = texColor * v_color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/distortion.shader b/Extra2D/shaders/effects/distortion.shader new file mode 100644 index 0000000..1d583d4 --- /dev/null +++ b/Extra2D/shaders/effects/distortion.shader @@ -0,0 +1,64 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: distortion +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "distortion", + "category": "effects", + "author": "Extra2D Team", + "description": "扭曲特效Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_distortionAmount; +uniform float u_time; +uniform float u_timeScale; + +out vec4 fragColor; + +void main() { + vec2 uv = v_texCoord; + + float t = u_time * u_timeScale; + float dx = sin(uv.y * 10.0 + t) * u_distortionAmount; + float dy = cos(uv.x * 10.0 + t) * u_distortionAmount; + uv += vec2(dx, dy); + + vec4 texColor = texture(u_texture, uv); + fragColor = texColor * v_color; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/grayscale.shader b/Extra2D/shaders/effects/grayscale.shader new file mode 100644 index 0000000..70748fb --- /dev/null +++ b/Extra2D/shaders/effects/grayscale.shader @@ -0,0 +1,61 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: grayscale +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "grayscale", + "category": "effects", + "author": "Extra2D Team", + "description": "灰度特效Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_intensity; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord) * v_color; + + float gray = dot(texColor.rgb, vec3(0.299, 0.587, 0.114)); + texColor.rgb = mix(texColor.rgb, vec3(gray), u_intensity); + + fragColor = texColor; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/grayscale_outline.shader b/Extra2D/shaders/effects/grayscale_outline.shader new file mode 100644 index 0000000..a5b0f91 --- /dev/null +++ b/Extra2D/shaders/effects/grayscale_outline.shader @@ -0,0 +1,77 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: grayscale_outline +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "grayscale_outline", + "category": "effects", + "author": "Extra2D Team", + "description": "灰度+描边组合效果Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_grayIntensity; +uniform vec4 u_outlineColor; +uniform float u_thickness; +uniform vec2 u_textureSize; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 color = texture(u_texture, v_texCoord) * v_color; + + float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); + color.rgb = mix(color.rgb, vec3(gray), u_grayIntensity); + + float alpha = 0.0; + vec2 offset = u_thickness / u_textureSize; + + alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a; + alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a; + alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a; + alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a; + + if (color.a < 0.1 && alpha > 0.0) { + fragColor = u_outlineColor; + } else { + fragColor = color; + } + + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/invert.shader b/Extra2D/shaders/effects/invert.shader new file mode 100644 index 0000000..9e8cdfc --- /dev/null +++ b/Extra2D/shaders/effects/invert.shader @@ -0,0 +1,60 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: invert +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "invert", + "category": "effects", + "author": "Extra2D Team", + "description": "反相特效Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_strength; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord) * v_color; + vec3 inverted = vec3(1.0) - texColor.rgb; + texColor.rgb = mix(texColor.rgb, inverted, u_strength); + + fragColor = texColor; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/outline.shader b/Extra2D/shaders/effects/outline.shader new file mode 100644 index 0000000..6969a8b --- /dev/null +++ b/Extra2D/shaders/effects/outline.shader @@ -0,0 +1,70 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: outline +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "outline", + "category": "effects", + "author": "Extra2D Team", + "description": "描边特效Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform vec4 u_outlineColor; +uniform float u_thickness; +uniform vec2 u_textureSize; + +out vec4 fragColor; + +void main() { + vec4 color = texture(u_texture, v_texCoord); + + float alpha = 0.0; + vec2 offset = u_thickness / u_textureSize; + + alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a; + alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a; + alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a; + alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a; + + if (color.a < 0.1 && alpha > 0.0) { + fragColor = u_outlineColor; + } else { + fragColor = color; + } + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/pixelate.shader b/Extra2D/shaders/effects/pixelate.shader new file mode 100644 index 0000000..17eb372 --- /dev/null +++ b/Extra2D/shaders/effects/pixelate.shader @@ -0,0 +1,61 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: pixelate +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "pixelate", + "category": "effects", + "author": "Extra2D Team", + "description": "像素化特效Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_pixelSize; +uniform vec2 u_textureSize; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec2 pixel = u_pixelSize / u_textureSize; + vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5; + + vec4 texColor = texture(u_texture, uv); + fragColor = texColor * v_color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/pixelate_invert.shader b/Extra2D/shaders/effects/pixelate_invert.shader new file mode 100644 index 0000000..f19e31c --- /dev/null +++ b/Extra2D/shaders/effects/pixelate_invert.shader @@ -0,0 +1,66 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: pixelate_invert +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "pixelate_invert", + "category": "effects", + "author": "Extra2D Team", + "description": "像素化+反相组合效果Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_pixelSize; +uniform vec2 u_textureSize; +uniform float u_invertStrength; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec2 pixel = u_pixelSize / u_textureSize; + vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5; + + vec4 color = texture(u_texture, uv) * v_color; + + vec3 inverted = 1.0 - color.rgb; + color.rgb = mix(color.rgb, inverted, u_invertStrength); + + fragColor = color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/shaders/effects/water.shader b/Extra2D/shaders/effects/water.shader new file mode 100644 index 0000000..e729afc --- /dev/null +++ b/Extra2D/shaders/effects/water.shader @@ -0,0 +1,63 @@ +// ============================================ +// Extra2D Combined Shader File +// Name: water +// Category: effects +// Version: 1.0 +// ============================================ + +#meta +{ + "name": "water", + "category": "effects", + "author": "Extra2D Team", + "description": "水波纹特效Shader" +} + +#vertex +#version 300 es +precision highp float; + +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} + +#fragment +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_waveSpeed; +uniform float u_waveAmplitude; +uniform float u_waveFrequency; +uniform float u_time; + +out vec4 fragColor; + +void main() { + vec2 uv = v_texCoord; + + float wave = sin(uv.y * u_waveFrequency + u_time * u_waveSpeed) * u_waveAmplitude; + uv.x += wave; + + vec4 texColor = texture(u_texture, uv); + fragColor = texColor * v_color; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/Extra2D/src/config/platform_detector.cpp b/Extra2D/src/config/platform_detector.cpp index a39fecf..dc6b7c3 100644 --- a/Extra2D/src/config/platform_detector.cpp +++ b/Extra2D/src/config/platform_detector.cpp @@ -432,6 +432,77 @@ std::string PlatformDetector::getLogPath(const std::string& appName) { #endif } +/** + * @brief 获取平台特定的资源路径(Shader、纹理等) + * Switch平台使用romfs,其他平台使用相对路径 + * @param appName 应用名称 + * @return 资源目录路径 + */ +std::string PlatformDetector::getResourcePath(const std::string& appName) { +#ifdef __SWITCH__ + (void)appName; + return "romfs:/"; +#else + (void)appName; + return "./resources/"; +#endif +} + +/** + * @brief 获取平台特定的Shader路径 + * @param appName 应用名称 + * @return Shader目录路径 + */ +std::string PlatformDetector::getShaderPath(const std::string& appName) { +#ifdef __SWITCH__ + (void)appName; + return "romfs:/shaders/"; +#else + (void)appName; + return "./shaders/"; +#endif +} + +/** + * @brief 获取平台特定的Shader缓存路径 + * Switch平台使用sdmc,其他平台使用系统缓存目录 + * @param appName 应用名称 + * @return Shader缓存目录路径 + */ +std::string PlatformDetector::getShaderCachePath(const std::string& appName) { +#ifdef __SWITCH__ + std::string name = appName.empty() ? "extra2d" : appName; + return "sdmc:/cache/" + name + "/shaders/"; +#else + return getCachePath(appName.empty() ? "extra2d" : appName) + "/shaders/"; +#endif +} + +/** + * @brief 检查平台是否使用romfs(只读文件系统) + * @return 使用romfs返回true + */ +bool PlatformDetector::usesRomfs() { +#ifdef __SWITCH__ + return true; +#else + return false; +#endif +} + +/** + * @brief 检查平台是否支持热重载 + * Switch平台不支持热重载(romfs只读) + * @return 支持热重载返回true + */ +bool PlatformDetector::supportsHotReload() { +#ifdef __SWITCH__ + return false; +#else + return true; +#endif +} + /** * @brief 检查平台是否为小端字节序 * @return 如果是小端字节序返回 true diff --git a/Extra2D/src/graphics/opengl/gl_renderer.cpp b/Extra2D/src/graphics/opengl/gl_renderer.cpp index f108e51..e7daa8c 100644 --- a/Extra2D/src/graphics/opengl/gl_renderer.cpp +++ b/Extra2D/src/graphics/opengl/gl_renderer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -13,30 +14,6 @@ namespace extra2d { -// 形状渲染着色器 - 支持顶点颜色批处理 (GLES 3.2) -static const char *SHAPE_VERTEX_SHADER = R"( -#version 300 es -precision highp float; -layout(location = 0) in vec2 aPosition; -layout(location = 1) in vec4 aColor; -uniform mat4 uViewProjection; -out vec4 vColor; -void main() { - gl_Position = uViewProjection * vec4(aPosition, 0.0, 1.0); - vColor = aColor; -} -)"; - -static const char *SHAPE_FRAGMENT_SHADER = R"( -#version 300 es -precision highp float; -in vec4 vColor; -out vec4 fragColor; -void main() { - fragColor = vColor; -} -)"; - // VBO 初始大小(用于 VRAM 跟踪) static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float); @@ -85,7 +62,7 @@ GLRenderer::~GLRenderer() { shutdown(); } * @param window 窗口指针 * @return 初始化成功返回true,失败返回false */ -bool GLRenderer::init(IWindow* window) { +bool GLRenderer::init(IWindow *window) { window_ = window; // Switch: GL 上下文已通过 SDL2 + EGL 初始化,无需 glewInit() @@ -125,8 +102,7 @@ void GLRenderer::shutdown() { if (lineVbo_ != 0) { glDeleteBuffers(1, &lineVbo_); - VRAMMgr::get().freeBuffer(MAX_LINE_VERTICES * - sizeof(ShapeVertex)); + VRAMMgr::get().freeBuffer(MAX_LINE_VERTICES * sizeof(ShapeVertex)); lineVbo_ = 0; } if (lineVao_ != 0) { @@ -135,8 +111,7 @@ void GLRenderer::shutdown() { } if (shapeVbo_ != 0) { glDeleteBuffers(1, &shapeVbo_); - VRAMMgr::get().freeBuffer(MAX_SHAPE_VERTICES * - sizeof(ShapeVertex)); + VRAMMgr::get().freeBuffer(MAX_SHAPE_VERTICES * sizeof(ShapeVertex)); shapeVbo_ = 0; } if (shapeVao_ != 0) { @@ -605,7 +580,7 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, // 收集所有字符数据用于批处理 std::vector sprites; - sprites.reserve(text.size()); // 预分配空间 + sprites.reserve(text.size()); // 预分配空间 for (char c : text) { char32_t codepoint = static_cast(static_cast(c)); @@ -637,7 +612,7 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, data.rotation = 0.0f; data.anchor = glm::vec2(0.0f, 0.0f); data.isSDF = font.isSDF(); - + sprites.push_back(data); } } @@ -657,8 +632,14 @@ void GLRenderer::resetStats() { stats_ = Stats{}; } * @brief 初始化形状渲染所需的OpenGL资源(VAO、VBO、着色器) */ void GLRenderer::initShapeRendering() { - // 编译形状着色器 - shapeShader_.compileFromSource(SHAPE_VERTEX_SHADER, SHAPE_FRAGMENT_SHADER); + // 从ShaderManager获取形状着色器 + shapeShader_ = ShaderManager::getInstance().getBuiltin("builtin_shape"); + if (!shapeShader_) { + E2D_LOG_WARN("Failed to get builtin shape shader, loading from manager"); + if (!ShaderManager::getInstance().isInitialized()) { + E2D_LOG_ERROR("ShaderManager not initialized, shape rendering may fail"); + } + } // 创建形状 VAO 和 VBO glGenVertexArrays(1, &shapeVao_); @@ -703,10 +684,8 @@ void GLRenderer::initShapeRendering() { glBindVertexArray(0); // VRAM 跟踪 - VRAMMgr::get().allocBuffer(MAX_SHAPE_VERTICES * - sizeof(ShapeVertex)); - VRAMMgr::get().allocBuffer(MAX_LINE_VERTICES * - sizeof(ShapeVertex)); + VRAMMgr::get().allocBuffer(MAX_SHAPE_VERTICES * sizeof(ShapeVertex)); + VRAMMgr::get().allocBuffer(MAX_LINE_VERTICES * sizeof(ShapeVertex)); } /** @@ -769,8 +748,10 @@ void GLRenderer::flushShapeBatch() { if (shapeVertexCount_ == 0) return; - shapeShader_.bind(); - shapeShader_.setMat4("uViewProjection", viewProjection_); + if (shapeShader_) { + shapeShader_->bind(); + shapeShader_->setMat4("u_viewProjection", viewProjection_); + } glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); glBufferSubData(GL_ARRAY_BUFFER, 0, shapeVertexCount_ * sizeof(ShapeVertex), @@ -796,8 +777,10 @@ void GLRenderer::flushLineBatch() { flushShapeBatch(); glLineWidth(currentLineWidth_); - shapeShader_.bind(); - shapeShader_.setMat4("uViewProjection", viewProjection_); + if (shapeShader_) { + shapeShader_->bind(); + shapeShader_->setMat4("u_viewProjection", viewProjection_); + } glBindBuffer(GL_ARRAY_BUFFER, lineVbo_); glBufferSubData(GL_ARRAY_BUFFER, 0, lineVertexCount_ * sizeof(ShapeVertex), diff --git a/Extra2D/src/graphics/opengl/gl_shader_new.cpp b/Extra2D/src/graphics/opengl/gl_shader_new.cpp new file mode 100644 index 0000000..2ca141c --- /dev/null +++ b/Extra2D/src/graphics/opengl/gl_shader_new.cpp @@ -0,0 +1,325 @@ +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数,初始化着色器程序ID为0 + */ +GLShaderNew::GLShaderNew() : programID_(0) { +} + +/** + * @brief 析构函数,删除OpenGL着色器程序 + */ +GLShaderNew::~GLShaderNew() { + if (programID_ != 0) { + glDeleteProgram(programID_); + programID_ = 0; + } +} + +/** + * @brief 绑定Shader程序 + */ +void GLShaderNew::bind() const { + glUseProgram(programID_); +} + +/** + * @brief 解绑Shader程序 + */ +void GLShaderNew::unbind() const { + glUseProgram(0); +} + +/** + * @brief 设置布尔类型uniform变量 + * @param name uniform变量名 + * @param value 布尔值 + */ +void GLShaderNew::setBool(const std::string& name, bool value) { + glUniform1i(getUniformLocation(name), value ? 1 : 0); +} + +/** + * @brief 设置整数类型uniform变量 + * @param name uniform变量名 + * @param value 整数值 + */ +void GLShaderNew::setInt(const std::string& name, int value) { + glUniform1i(getUniformLocation(name), value); +} + +/** + * @brief 设置浮点类型uniform变量 + * @param name uniform变量名 + * @param value 浮点值 + */ +void GLShaderNew::setFloat(const std::string& name, float value) { + glUniform1f(getUniformLocation(name), value); +} + +/** + * @brief 设置二维向量类型uniform变量 + * @param name uniform变量名 + * @param value 二维向量值 + */ +void GLShaderNew::setVec2(const std::string& name, const glm::vec2& value) { + glUniform2fv(getUniformLocation(name), 1, &value[0]); +} + +/** + * @brief 设置三维向量类型uniform变量 + * @param name uniform变量名 + * @param value 三维向量值 + */ +void GLShaderNew::setVec3(const std::string& name, const glm::vec3& value) { + glUniform3fv(getUniformLocation(name), 1, &value[0]); +} + +/** + * @brief 设置四维向量类型uniform变量 + * @param name uniform变量名 + * @param value 四维向量值 + */ +void GLShaderNew::setVec4(const std::string& name, const glm::vec4& value) { + glUniform4fv(getUniformLocation(name), 1, &value[0]); +} + +/** + * @brief 设置4x4矩阵类型uniform变量 + * @param name uniform变量名 + * @param value 4x4矩阵值 + */ +void GLShaderNew::setMat4(const std::string& name, const glm::mat4& value) { + glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]); +} + +/** + * @brief 设置颜色类型uniform变量 + * @param name uniform变量名 + * @param color 颜色值 + */ +void GLShaderNew::setColor(const std::string& name, const Color& color) { + glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a); +} + +/** + * @brief 从源码编译Shader + * @param vertexSource 顶点着色器源码 + * @param fragmentSource 片段着色器源码 + * @return 编译成功返回true,失败返回false + */ +bool GLShaderNew::compileFromSource(const char* vertexSource, const char* fragmentSource) { + GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource); + if (vertexShader == 0) { + return false; + } + + GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource); + if (fragmentShader == 0) { + glDeleteShader(vertexShader); + return false; + } + + if (programID_ != 0) { + glDeleteProgram(programID_); + uniformCache_.clear(); + } + + programID_ = glCreateProgram(); + glAttachShader(programID_, vertexShader); + glAttachShader(programID_, fragmentShader); + glLinkProgram(programID_); + + GLint success; + glGetProgramiv(programID_, GL_LINK_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetProgramInfoLog(programID_, 512, nullptr, infoLog); + E2D_LOG_ERROR("Shader program linking failed: {}", infoLog); + glDeleteProgram(programID_); + programID_ = 0; + } + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + return success == GL_TRUE; +} + +/** + * @brief 从二进制数据创建Shader + * @param binary 二进制数据 + * @return 创建成功返回true,失败返回false + */ +bool GLShaderNew::compileFromBinary(const std::vector& binary) { + if (binary.empty()) { + E2D_LOG_ERROR("Binary data is empty"); + return false; + } + + GLint numFormats = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats); + if (numFormats == 0) { + E2D_LOG_ERROR("Program binary formats not supported"); + return false; + } + + if (programID_ != 0) { + glDeleteProgram(programID_); + uniformCache_.clear(); + } + + programID_ = glCreateProgram(); + + GLenum binaryFormat = 0; + glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, reinterpret_cast(&binaryFormat)); + + glProgramBinary(programID_, binaryFormat, binary.data(), static_cast(binary.size())); + + GLint success = 0; + glGetProgramiv(programID_, GL_LINK_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetProgramInfoLog(programID_, 512, nullptr, infoLog); + E2D_LOG_ERROR("Failed to load shader from binary: {}", infoLog); + glDeleteProgram(programID_); + programID_ = 0; + return false; + } + + return true; +} + +/** + * @brief 获取Shader二进制数据 + * @param outBinary 输出的二进制数据 + * @return 成功返回true,失败返回false + */ +bool GLShaderNew::getBinary(std::vector& outBinary) { + if (programID_ == 0) { + return false; + } + + GLint binaryLength = 0; + glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength); + + if (binaryLength <= 0) { + return false; + } + + outBinary.resize(binaryLength); + + GLenum binaryFormat = 0; + glGetProgramBinary(programID_, binaryLength, nullptr, &binaryFormat, outBinary.data()); + + return true; +} + +/** + * @brief 编译单个着色器 + * @param type 着色器类型 + * @param source 着色器源码 + * @return 着色器ID,失败返回0 + */ +GLuint GLShaderNew::compileShader(GLenum type, const char* source) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetShaderInfoLog(shader, 512, nullptr, infoLog); + E2D_LOG_ERROR("Shader compilation failed: {}", infoLog); + glDeleteShader(shader); + return 0; + } + + return shader; +} + +/** + * @brief 获取uniform位置 + * @param name uniform变量名 + * @return uniform位置 + */ +GLint GLShaderNew::getUniformLocation(const std::string& name) { + auto it = uniformCache_.find(name); + if (it != uniformCache_.end()) { + return it->second; + } + + GLint location = glGetUniformLocation(programID_, name.c_str()); + uniformCache_[name] = location; + return location; +} + +// ============================================================================ +// GLShaderFactory 实现 +// ============================================================================ + +/** + * @brief 从源码创建Shader + * @param name Shader名称 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 创建的Shader实例 + */ +Ptr GLShaderFactory::createFromSource( + const std::string& name, + const std::string& vertSource, + const std::string& fragSource) { + + auto shader = std::make_shared(); + shader->setName(name); + + if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) { + E2D_LOG_ERROR("Failed to compile shader from source: {}", name); + return nullptr; + } + + return shader; +} + +/** + * @brief 从缓存二进制创建Shader + * @param name Shader名称 + * @param binary 编译后的二进制数据 + * @return 创建的Shader实例 + */ +Ptr GLShaderFactory::createFromBinary( + const std::string& name, + const std::vector& binary) { + + auto shader = std::make_shared(); + shader->setName(name); + + if (!shader->compileFromBinary(binary)) { + E2D_LOG_ERROR("Failed to create shader from binary: {}", name); + return nullptr; + } + + return shader; +} + +/** + * @brief 获取Shader的二进制数据 + * @param shader Shader实例 + * @param outBinary 输出的二进制数据 + * @return 成功返回true,失败返回false + */ +bool GLShaderFactory::getShaderBinary(const IShader& shader, std::vector& outBinary) { + const GLShaderNew* glShader = dynamic_cast(&shader); + if (!glShader) { + E2D_LOG_ERROR("Shader is not a GLShaderNew instance"); + return false; + } + + return const_cast(glShader)->getBinary(outBinary); +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/shader_cache.cpp b/Extra2D/src/graphics/shader_cache.cpp new file mode 100644 index 0000000..1ead6a9 --- /dev/null +++ b/Extra2D/src/graphics/shader_cache.cpp @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +namespace fs = std::filesystem; + +/** + * @brief 获取单例实例 + * @return 缓存管理器实例引用 + */ +ShaderCache& ShaderCache::getInstance() { + static ShaderCache instance; + return instance; +} + +/** + * @brief 初始化缓存系统 + * @param cacheDir 缓存目录路径 + * @return 初始化成功返回true,失败返回false + */ +bool ShaderCache::init(const std::string& cacheDir) { + cacheDir_ = cacheDir; + + if (!ensureCacheDirectory()) { + E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir); + return false; + } + + if (!loadCacheIndex()) { + E2D_LOG_WARN("Failed to load cache index, starting fresh"); + } + + initialized_ = true; + E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir); + return true; +} + +/** + * @brief 关闭缓存系统 + */ +void ShaderCache::shutdown() { + if (!initialized_) { + return; + } + + saveCacheIndex(); + cacheMap_.clear(); + initialized_ = false; + E2D_LOG_INFO("Shader cache shutdown"); +} + +/** + * @brief 检查缓存是否有效 + * @param name Shader名称 + * @param sourceHash 源码哈希值 + * @return 缓存有效返回true,否则返回false + */ +bool ShaderCache::hasValidCache(const std::string& name, const std::string& sourceHash) { + auto it = cacheMap_.find(name); + if (it == cacheMap_.end()) { + return false; + } + + return it->second.sourceHash == sourceHash; +} + +/** + * @brief 加载缓存的二进制数据 + * @param name Shader名称 + * @return 缓存条目指针,不存在返回nullptr + */ +Ptr ShaderCache::loadCache(const std::string& name) { + auto it = cacheMap_.find(name); + if (it == cacheMap_.end()) { + return nullptr; + } + + std::string cachePath = getCachePath(name); + std::ifstream file(cachePath, std::ios::binary); + if (!file.is_open()) { + E2D_LOG_WARN("Failed to open cache file: {}", cachePath); + return nullptr; + } + + auto entry = std::make_shared(it->second); + entry->binary.clear(); + + file.seekg(0, std::ios::end); + size_t fileSize = static_cast(file.tellg()); + file.seekg(0, std::ios::beg); + + entry->binary.resize(fileSize); + file.read(reinterpret_cast(entry->binary.data()), fileSize); + + return entry; +} + +/** + * @brief 保存编译结果到缓存 + * @param entry 缓存条目 + * @return 保存成功返回true,失败返回false + */ +bool ShaderCache::saveCache(const ShaderCacheEntry& entry) { + if (!initialized_) { + return false; + } + + std::string cachePath = getCachePath(entry.name); + std::ofstream file(cachePath, std::ios::binary); + if (!file.is_open()) { + E2D_LOG_ERROR("Failed to create cache file: {}", cachePath); + return false; + } + + file.write(reinterpret_cast(entry.binary.data()), entry.binary.size()); + file.close(); + + cacheMap_[entry.name] = entry; + saveCacheIndex(); + + E2D_LOG_DEBUG("Shader cache saved: {}", entry.name); + return true; +} + +/** + * @brief 使缓存失效 + * @param name Shader名称 + */ +void ShaderCache::invalidate(const std::string& name) { + auto it = cacheMap_.find(name); + if (it == cacheMap_.end()) { + return; + } + + std::string cachePath = getCachePath(name); + fs::remove(cachePath); + + cacheMap_.erase(it); + saveCacheIndex(); + + E2D_LOG_DEBUG("Shader cache invalidated: {}", name); +} + +/** + * @brief 清除所有缓存 + */ +void ShaderCache::clearAll() { + for (const auto& pair : cacheMap_) { + std::string cachePath = getCachePath(pair.first); + fs::remove(cachePath); + } + + cacheMap_.clear(); + saveCacheIndex(); + + E2D_LOG_INFO("All shader caches cleared"); +} + +/** + * @brief 计算源码哈希值 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 哈希值字符串 + */ +std::string ShaderCache::computeHash(const std::string& vertSource, + const std::string& fragSource) { + std::string combined = vertSource + fragSource; + + uint32_t hash = 5381; + for (char c : combined) { + hash = ((hash << 5) + hash) + static_cast(c); + } + + std::stringstream ss; + ss << std::hex << hash; + return ss.str(); +} + +/** + * @brief 加载缓存索引 + * @return 加载成功返回true,失败返回false + */ +bool ShaderCache::loadCacheIndex() { + std::string indexPath = cacheDir_ + "/.cache_index"; + + if (!fs::exists(indexPath)) { + return true; + } + + std::ifstream file(indexPath); + if (!file.is_open()) { + return false; + } + + std::string line; + while (std::getline(file, line)) { + if (line.empty() || line[0] == '#') { + continue; + } + + size_t pos = line.find('='); + if (pos == std::string::npos) { + continue; + } + + std::string name = line.substr(0, pos); + std::string hash = line.substr(pos + 1); + + std::string cachePath = getCachePath(name); + if (fs::exists(cachePath)) { + ShaderCacheEntry entry; + entry.name = name; + entry.sourceHash = hash; + entry.compileTime = static_cast( + std::chrono::system_clock::now().time_since_epoch().count()); + cacheMap_[name] = entry; + } + } + + return true; +} + +/** + * @brief 保存缓存索引 + * @return 保存成功返回true,失败返回false + */ +bool ShaderCache::saveCacheIndex() { + std::string indexPath = cacheDir_ + "/.cache_index"; + + std::ofstream file(indexPath); + if (!file.is_open()) { + return false; + } + + file << "# Extra2D Shader Cache Index\n"; + file << "# Format: name=hash\n"; + + for (const auto& pair : cacheMap_) { + file << pair.first << "=" << pair.second.sourceHash << "\n"; + } + + return true; +} + +/** + * @brief 获取缓存文件路径 + * @param name Shader名称 + * @return 缓存文件完整路径 + */ +std::string ShaderCache::getCachePath(const std::string& name) const { + return cacheDir_ + "/" + name + ".cache"; +} + +/** + * @brief 确保缓存目录存在 + * @return 目录存在或创建成功返回true,否则返回false + */ +bool ShaderCache::ensureCacheDirectory() { + if (cacheDir_.empty()) { + return false; + } + + std::error_code ec; + if (!fs::exists(cacheDir_)) { + if (!fs::create_directories(cacheDir_, ec)) { + return false; + } + } + + return true; +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/shader_hot_reloader.cpp b/Extra2D/src/graphics/shader_hot_reloader.cpp new file mode 100644 index 0000000..867ad84 --- /dev/null +++ b/Extra2D/src/graphics/shader_hot_reloader.cpp @@ -0,0 +1,164 @@ +#include +#include +#include +#include + +namespace extra2d { + +namespace fs = std::filesystem; + +/** + * @brief 获取单例实例 + * @return 热重载管理器实例引用 + */ +ShaderHotReloader& ShaderHotReloader::getInstance() { + static ShaderHotReloader instance; + return instance; +} + +/** + * @brief 初始化热重载系统 + * @return 初始化成功返回true,失败返回false + */ +bool ShaderHotReloader::init() { + if (initialized_) { + return true; + } + +#ifdef _WIN32 + buffer_.resize(4096); +#endif + + initialized_ = true; + E2D_LOG_INFO("Shader hot reloader initialized"); + return true; +} + +/** + * @brief 关闭热重载系统 + */ +void ShaderHotReloader::shutdown() { + if (!initialized_) { + return; + } + +#ifdef _WIN32 + if (watchHandle_ != nullptr) { + FindCloseChangeNotification(watchHandle_); + watchHandle_ = nullptr; + } +#endif + + watchMap_.clear(); + initialized_ = false; + enabled_ = false; + E2D_LOG_INFO("Shader hot reloader shutdown"); +} + +/** + * @brief 注册Shader文件监视 + * @param shaderName Shader名称 + * @param filePaths 要监视的文件列表 + * @param callback 文件变化时的回调 + */ +void ShaderHotReloader::watch(const std::string& shaderName, + const std::vector& filePaths, + FileChangeCallback callback) { + if (!initialized_) { + E2D_LOG_WARN("Hot reloader not initialized"); + return; + } + + WatchInfo info; + info.filePaths = filePaths; + info.callback = callback; + + for (const auto& path : filePaths) { + info.modifiedTimes[path] = getFileModifiedTime(path); + } + + watchMap_[shaderName] = std::move(info); + E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size()); +} + +/** + * @brief 取消监视 + * @param shaderName Shader名称 + */ +void ShaderHotReloader::unwatch(const std::string& shaderName) { + auto it = watchMap_.find(shaderName); + if (it != watchMap_.end()) { + watchMap_.erase(it); + E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName); + } +} + +/** + * @brief 更新文件监视(在主循环中调用) + */ +void ShaderHotReloader::update() { + if (!initialized_ || !enabled_) { + return; + } + + pollChanges(); +} + +/** + * @brief 启用/禁用热重载 + * @param enabled 是否启用 + */ +void ShaderHotReloader::setEnabled(bool enabled) { + enabled_ = enabled; + E2D_LOG_DEBUG("Hot reload {}", enabled ? "enabled" : "disabled"); +} + +/** + * @brief 轮询检查文件变化 + */ +void ShaderHotReloader::pollChanges() { + auto now = static_cast( + std::chrono::system_clock::now().time_since_epoch().count()); + + for (auto& pair : watchMap_) { + WatchInfo& info = pair.second; + + for (const auto& filePath : info.filePaths) { + uint64_t currentModTime = getFileModifiedTime(filePath); + uint64_t lastModTime = info.modifiedTimes[filePath]; + + if (currentModTime != 0 && lastModTime != 0 && currentModTime != lastModTime) { + info.modifiedTimes[filePath] = currentModTime; + + FileChangeEvent event; + event.filepath = filePath; + event.type = FileChangeEvent::Type::Modified; + event.timestamp = now; + + E2D_LOG_DEBUG("Shader file changed: {}", filePath); + + if (info.callback) { + info.callback(event); + } + } + } + } +} + +/** + * @brief 获取文件修改时间 + * @param filepath 文件路径 + * @return 修改时间戳 + */ +uint64_t ShaderHotReloader::getFileModifiedTime(const std::string& filepath) { + try { + auto ftime = fs::last_write_time(filepath); + auto sctp = std::chrono::time_point_cast( + ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()); + return static_cast(sctp.time_since_epoch().count()); + } catch (...) { + return 0; + } +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/shader_loader.cpp b/Extra2D/src/graphics/shader_loader.cpp new file mode 100644 index 0000000..6444b59 --- /dev/null +++ b/Extra2D/src/graphics/shader_loader.cpp @@ -0,0 +1,451 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +namespace fs = std::filesystem; + +/** + * @brief 构造函数,初始化Shader加载器 + */ +ShaderLoader::ShaderLoader() { +} + +/** + * @brief 从分离文件加载Shader (.vert + .frag) + * @param name Shader名称 + * @param vertPath 顶点着色器文件路径 + * @param fragPath 片段着色器文件路径 + * @return 加载结果 + */ +ShaderLoadResult ShaderLoader::loadFromSeparateFiles( + const std::string& name, + const std::string& vertPath, + const std::string& fragPath) { + + ShaderLoadResult result; + + if (!fileExists(vertPath)) { + result.errorMessage = "Vertex shader file not found: " + vertPath; + E2D_LOG_ERROR("{}", result.errorMessage); + return result; + } + + if (!fileExists(fragPath)) { + result.errorMessage = "Fragment shader file not found: " + fragPath; + E2D_LOG_ERROR("{}", result.errorMessage); + return result; + } + + std::string vertSource = readFile(vertPath); + std::string fragSource = readFile(fragPath); + + if (vertSource.empty()) { + result.errorMessage = "Failed to read vertex shader file: " + vertPath; + E2D_LOG_ERROR("{}", result.errorMessage); + return result; + } + + if (fragSource.empty()) { + result.errorMessage = "Failed to read fragment shader file: " + fragPath; + E2D_LOG_ERROR("{}", result.errorMessage); + return result; + } + + fs::path vertDir = fs::path(vertPath).parent_path(); + fs::path fragDir = fs::path(fragPath).parent_path(); + + vertSource = processIncludes(vertSource, vertDir.string(), result.dependencies); + fragSource = processIncludes(fragSource, fragDir.string(), result.dependencies); + + result.vertSource = vertSource; + result.fragSource = fragSource; + result.success = true; + + return result; +} + +/** + * @brief 从组合文件加载Shader (.shader) + * @param path 组合Shader文件路径 + * @return 加载结果 + */ +ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string& path) { + ShaderLoadResult result; + + if (!fileExists(path)) { + result.errorMessage = "Shader file not found: " + path; + E2D_LOG_ERROR("{}", result.errorMessage); + return result; + } + + std::string content = readFile(path); + if (content.empty()) { + result.errorMessage = "Failed to read shader file: " + path; + E2D_LOG_ERROR("{}", result.errorMessage); + return result; + } + + ShaderMetadata metadata; + std::string vertSource, fragSource; + + if (!parseCombinedFile(content, vertSource, fragSource, metadata)) { + result.errorMessage = "Failed to parse combined shader file: " + path; + E2D_LOG_ERROR("{}", result.errorMessage); + return result; + } + + fs::path baseDir = fs::path(path).parent_path(); + vertSource = processIncludes(vertSource, baseDir.string(), result.dependencies); + fragSource = processIncludes(fragSource, baseDir.string(), result.dependencies); + + result.vertSource = vertSource; + result.fragSource = fragSource; + result.success = true; + + return result; +} + +/** + * @brief 从源码字符串加载Shader + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 加载结果 + */ +ShaderLoadResult ShaderLoader::loadFromSource( + const std::string& vertSource, + const std::string& fragSource) { + + ShaderLoadResult result; + result.vertSource = vertSource; + result.fragSource = fragSource; + result.success = true; + return result; +} + +/** + * @brief 处理Shader源码中的#include指令 + * @param source 原始源码 + * @param baseDir 基础目录 + * @param outDependencies 输出依赖列表 + * @return 处理后的源码 + */ +std::string ShaderLoader::processIncludes( + const std::string& source, + const std::string& baseDir, + std::vector& outDependencies) { + + std::string result; + std::istringstream stream(source); + std::string line; + + while (std::getline(stream, line)) { + size_t includePos = line.find("#include"); + if (includePos != std::string::npos) { + size_t startQuote = line.find('"', includePos); + size_t endQuote = line.find('"', startQuote + 1); + + if (startQuote != std::string::npos && endQuote != std::string::npos) { + std::string includeName = line.substr(startQuote + 1, endQuote - startQuote - 1); + std::string includePath = findIncludeFile(includeName, baseDir); + + if (!includePath.empty()) { + auto cacheIt = includeCache_.find(includePath); + std::string includeContent; + + if (cacheIt != includeCache_.end()) { + includeContent = cacheIt->second; + } else { + includeContent = readFile(includePath); + includeCache_[includePath] = includeContent; + } + + outDependencies.push_back(includePath); + result += includeContent; + result += "\n"; + continue; + } else { + E2D_LOG_WARN("Include file not found: {}", includeName); + } + } + } + + result += line; + result += "\n"; + } + + return result; +} + +/** + * @brief 应用预处理器定义 + * @param source 原始源码 + * @param defines 预处理器定义列表 + * @return 处理后的源码 + */ +std::string ShaderLoader::applyDefines( + const std::string& source, + const std::vector& defines) { + + if (defines.empty()) { + return source; + } + + std::string defineBlock; + for (const auto& def : defines) { + defineBlock += "#define " + def + "\n"; + } + + std::string result; + std::istringstream stream(source); + std::string line; + bool inserted = false; + + while (std::getline(stream, line)) { + if (!inserted && (line.find("#version") != std::string::npos || + line.find("precision") != std::string::npos)) { + result += line + "\n"; + continue; + } + + if (!inserted) { + result += defineBlock; + inserted = true; + } + + result += line + "\n"; + } + + return result; +} + +/** + * @brief 获取Shader元数据 + * @param path Shader文件路径 + * @return 元数据 + */ +ShaderMetadata ShaderLoader::getMetadata(const std::string& path) { + ShaderMetadata metadata; + + if (!fileExists(path)) { + return metadata; + } + + metadata.combinedPath = path; + metadata.lastModified = getFileModifiedTime(path); + + fs::path p(path); + metadata.name = p.stem().string(); + + return metadata; +} + +/** + * @brief 添加include搜索路径 + * @param path 搜索路径 + */ +void ShaderLoader::addIncludePath(const std::string& path) { + if (std::find(includePaths_.begin(), includePaths_.end(), path) == includePaths_.end()) { + includePaths_.push_back(path); + } +} + +/** + * @brief 读取文件内容 + * @param filepath 文件路径 + * @return 文件内容字符串 + */ +std::string ShaderLoader::readFile(const std::string& filepath) { + std::ifstream file(filepath, std::ios::binary); + if (!file.is_open()) { + return ""; + } + + std::ostringstream content; + content << file.rdbuf(); + return content.str(); +} + +/** + * @brief 获取文件修改时间 + * @param filepath 文件路径 + * @return 修改时间戳 + */ +uint64_t ShaderLoader::getFileModifiedTime(const std::string& filepath) { +#ifdef __SWITCH__ + (void)filepath; + return 1; +#else + try { + auto ftime = fs::last_write_time(filepath); + auto sctp = std::chrono::time_point_cast( + ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()); + return static_cast(sctp.time_since_epoch().count()); + } catch (...) { + return 0; + } +#endif +} + +/** + * @brief 检查文件是否存在 + * @param filepath 文件路径 + * @return 存在返回true,否则返回false + */ +bool ShaderLoader::fileExists(const std::string& filepath) { + return fs::exists(filepath); +} + +/** + * @brief 解析组合Shader文件 + * @param content 文件内容 + * @param outVert 输出顶点着色器源码 + * @param outFrag 输出片段着色器源码 + * @param outMetadata 输出元数据 + * @return 解析成功返回true,失败返回false + */ +bool ShaderLoader::parseCombinedFile(const std::string& content, + std::string& outVert, + std::string& outFrag, + ShaderMetadata& outMetadata) { + enum class Section { + None, + Meta, + Vertex, + Fragment + }; + + Section currentSection = Section::None; + std::string metaContent; + std::string vertContent; + std::string fragContent; + + std::istringstream stream(content); + std::string line; + + while (std::getline(stream, line)) { + std::string trimmedLine = line; + size_t start = trimmedLine.find_first_not_of(" \t\r\n"); + if (start != std::string::npos) { + trimmedLine = trimmedLine.substr(start); + } + size_t end = trimmedLine.find_last_not_of(" \t\r\n"); + if (end != std::string::npos) { + trimmedLine = trimmedLine.substr(0, end + 1); + } + + if (trimmedLine == "#meta") { + currentSection = Section::Meta; + continue; + } else if (trimmedLine == "#vertex") { + currentSection = Section::Vertex; + continue; + } else if (trimmedLine == "#fragment") { + currentSection = Section::Fragment; + continue; + } + + switch (currentSection) { + case Section::Meta: + metaContent += line + "\n"; + break; + case Section::Vertex: + vertContent += line + "\n"; + break; + case Section::Fragment: + fragContent += line + "\n"; + break; + default: + break; + } + } + + if (vertContent.empty() || fragContent.empty()) { + return false; + } + + if (!metaContent.empty()) { + parseMetadata(metaContent, outMetadata); + } + + outVert = vertContent; + outFrag = fragContent; + return true; +} + +/** + * @brief 解析元数据JSON块 + * @param jsonContent JSON内容 + * @param outMetadata 输出元数据 + * @return 解析成功返回true,失败返回false + */ +bool ShaderLoader::parseMetadata(const std::string& jsonContent, ShaderMetadata& outMetadata) { + std::string content = jsonContent; + + size_t start = content.find('{'); + size_t end = content.rfind('}'); + if (start == std::string::npos || end == std::string::npos || end <= start) { + return false; + } + + content = content.substr(start, end - start + 1); + + auto extractString = [&content](const std::string& key) -> std::string { + std::string searchKey = "\"" + key + "\""; + size_t keyPos = content.find(searchKey); + if (keyPos == std::string::npos) { + return ""; + } + + size_t colonPos = content.find(':', keyPos); + if (colonPos == std::string::npos) { + return ""; + } + + size_t quoteStart = content.find('"', colonPos); + if (quoteStart == std::string::npos) { + return ""; + } + + size_t quoteEnd = content.find('"', quoteStart + 1); + if (quoteEnd == std::string::npos) { + return ""; + } + + return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1); + }; + + outMetadata.name = extractString("name"); + return true; +} + +/** + * @brief 查找include文件路径 + * @param includeName include文件名 + * @param baseDir 基础目录 + * @return 找到的完整路径,未找到返回空字符串 + */ +std::string ShaderLoader::findIncludeFile(const std::string& includeName, const std::string& baseDir) { + fs::path basePath(baseDir); + fs::path includePath = basePath / includeName; + + if (fs::exists(includePath)) { + return includePath.string(); + } + + for (const auto& searchPath : includePaths_) { + includePath = fs::path(searchPath) / includeName; + if (fs::exists(includePath)) { + return includePath.string(); + } + } + + return ""; +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/shader_manager.cpp b/Extra2D/src/graphics/shader_manager.cpp new file mode 100644 index 0000000..7d95d70 --- /dev/null +++ b/Extra2D/src/graphics/shader_manager.cpp @@ -0,0 +1,525 @@ +#include +#include + +namespace extra2d { + +/** + * @brief 获取单例实例 + * @return Shader管理器实例引用 + */ +ShaderManager& ShaderManager::getInstance() { + static ShaderManager instance; + return instance; +} + +/** + * @brief 使用平台默认路径初始化Shader系统 + * 自动检测平台并使用正确的路径(romfs/sdmc/相对路径) + * @param factory 渲染后端Shader工厂 + * @param appName 应用名称(用于缓存目录) + * @return 初始化成功返回true,失败返回false + */ +bool ShaderManager::init(Ptr factory, const std::string& appName) { + std::string shaderDir = PlatformDetector::getShaderPath(appName); + std::string cacheDir = PlatformDetector::getShaderCachePath(appName); + + hotReloadSupported_ = PlatformDetector::supportsHotReload(); + + E2D_LOG_INFO("Platform: {} (HotReload: {})", + PlatformDetector::platformName(), + hotReloadSupported_ ? "supported" : "not supported"); + + return init(shaderDir, cacheDir, factory); +} + +/** + * @brief 初始化Shader系统 + * @param shaderDir Shader文件目录 + * @param cacheDir 缓存目录 + * @param factory 渲染后端Shader工厂 + * @return 初始化成功返回true,失败返回false + */ +bool ShaderManager::init(const std::string& shaderDir, + const std::string& cacheDir, + Ptr factory) { + if (initialized_) { + E2D_LOG_WARN("ShaderManager already initialized"); + return true; + } + + if (!factory) { + E2D_LOG_ERROR("Shader factory is null"); + return false; + } + + shaderDir_ = shaderDir; + cacheDir_ = cacheDir; + factory_ = factory; + + hotReloadSupported_ = PlatformDetector::supportsHotReload(); + +#ifdef __SWITCH__ + if (!ShaderCache::getInstance().init(cacheDir_)) { + E2D_LOG_WARN("Failed to initialize shader cache on Switch"); + } +#else + if (!ShaderCache::getInstance().init(cacheDir_)) { + E2D_LOG_WARN("Failed to initialize shader cache, caching disabled"); + } +#endif + + if (hotReloadSupported_) { + if (!ShaderHotReloader::getInstance().init()) { + E2D_LOG_WARN("Failed to initialize hot reloader"); + } + } + + loader_.addIncludePath(shaderDir_ + "common"); + + initialized_ = true; + E2D_LOG_INFO("ShaderManager initialized"); + E2D_LOG_INFO(" Shader directory: {}", shaderDir_); + E2D_LOG_INFO(" Cache directory: {}", cacheDir_); + E2D_LOG_INFO(" Hot reload: {}", hotReloadSupported_ ? "supported" : "not supported"); + + return true; +} + +/** + * @brief 关闭Shader系统 + */ +void ShaderManager::shutdown() { + if (!initialized_) { + return; + } + + if (hotReloadSupported_) { + ShaderHotReloader::getInstance().shutdown(); + } + ShaderCache::getInstance().shutdown(); + + shaders_.clear(); + factory_.reset(); + initialized_ = false; + + E2D_LOG_INFO("ShaderManager shutdown"); +} + +/** + * @brief 从分离文件加载Shader + * @param name Shader名称 + * @param vertPath 顶点着色器文件路径 + * @param fragPath 片段着色器文件路径 + * @return 加载的Shader实例 + */ +Ptr ShaderManager::loadFromFiles(const std::string& name, + const std::string& vertPath, + const std::string& fragPath) { + if (!initialized_) { + E2D_LOG_ERROR("ShaderManager not initialized"); + return nullptr; + } + + auto it = shaders_.find(name); + if (it != shaders_.end()) { + return it->second.shader; + } + + ShaderLoadResult result = loader_.loadFromSeparateFiles(name, vertPath, fragPath); + if (!result.success) { + E2D_LOG_ERROR("Failed to load shader files: {} - {}", vertPath, fragPath); + return nullptr; + } + + std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource); + Ptr shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource); + + if (!shader) { + shader = factory_->createFromSource(name, result.vertSource, result.fragSource); + if (!shader) { + E2D_LOG_ERROR("Failed to create shader from source: {}", name); + return nullptr; + } + + std::vector binary; + if (factory_->getShaderBinary(*shader, binary)) { + ShaderCacheEntry entry; + entry.name = name; + entry.sourceHash = sourceHash; + entry.binary = binary; + entry.dependencies = result.dependencies; + ShaderCache::getInstance().saveCache(entry); + } + } + + ShaderInfo info; + info.shader = shader; + info.vertSource = result.vertSource; + info.fragSource = result.fragSource; + info.filePaths = {vertPath, fragPath}; + info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end()); + + info.metadata.name = name; + info.metadata.vertPath = vertPath; + info.metadata.fragPath = fragPath; + + shaders_[name] = std::move(info); + + if (hotReloadEnabled_ && hotReloadSupported_) { + auto callback = [this, name](const FileChangeEvent& event) { + this->handleFileChange(name, event); + }; + ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback); + } + + E2D_LOG_DEBUG("Shader loaded: {}", name); + return shader; +} + +/** + * @brief 从组合文件加载Shader + * @param path 组合Shader文件路径 + * @return 加载的Shader实例 + */ +Ptr ShaderManager::loadFromCombinedFile(const std::string& path) { + if (!initialized_) { + E2D_LOG_ERROR("ShaderManager not initialized"); + return nullptr; + } + + ShaderMetadata metadata = loader_.getMetadata(path); + std::string name = metadata.name.empty() ? path : metadata.name; + + auto it = shaders_.find(name); + if (it != shaders_.end()) { + return it->second.shader; + } + + ShaderLoadResult result = loader_.loadFromCombinedFile(path); + if (!result.success) { + E2D_LOG_ERROR("Failed to load combined shader file: {}", path); + return nullptr; + } + + std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource); + Ptr shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource); + + if (!shader) { + shader = factory_->createFromSource(name, result.vertSource, result.fragSource); + if (!shader) { + E2D_LOG_ERROR("Failed to create shader from source: {}", name); + return nullptr; + } + + std::vector binary; + if (factory_->getShaderBinary(*shader, binary)) { + ShaderCacheEntry entry; + entry.name = name; + entry.sourceHash = sourceHash; + entry.binary = binary; + entry.dependencies = result.dependencies; + ShaderCache::getInstance().saveCache(entry); + } + } + + ShaderInfo info; + info.shader = shader; + info.vertSource = result.vertSource; + info.fragSource = result.fragSource; + info.filePaths = {path}; + info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end()); + info.metadata = metadata; + + shaders_[name] = std::move(info); + + if (hotReloadEnabled_ && hotReloadSupported_) { + auto callback = [this, name](const FileChangeEvent& event) { + this->handleFileChange(name, event); + }; + ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback); + } + + E2D_LOG_DEBUG("Shader loaded from combined file: {}", name); + return shader; +} + +/** + * @brief 从源码加载Shader + * @param name Shader名称 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 加载的Shader实例 + */ +Ptr ShaderManager::loadFromSource(const std::string& name, + const std::string& vertSource, + const std::string& fragSource) { + if (!initialized_) { + E2D_LOG_ERROR("ShaderManager not initialized"); + return nullptr; + } + + auto it = shaders_.find(name); + if (it != shaders_.end()) { + return it->second.shader; + } + + Ptr shader = factory_->createFromSource(name, vertSource, fragSource); + if (!shader) { + E2D_LOG_ERROR("Failed to create shader from source: {}", name); + return nullptr; + } + + ShaderInfo info; + info.shader = shader; + info.vertSource = vertSource; + info.fragSource = fragSource; + info.metadata.name = name; + + shaders_[name] = std::move(info); + + E2D_LOG_DEBUG("Shader loaded from source: {}", name); + return shader; +} + +/** + * @brief 获取已加载的Shader + * @param name Shader名称 + * @return Shader实例,不存在返回nullptr + */ +Ptr ShaderManager::get(const std::string& name) const { + auto it = shaders_.find(name); + if (it != shaders_.end()) { + return it->second.shader; + } + return nullptr; +} + +/** + * @brief 检查Shader是否存在 + * @param name Shader名称 + * @return 存在返回true,否则返回false + */ +bool ShaderManager::has(const std::string& name) const { + return shaders_.find(name) != shaders_.end(); +} + +/** + * @brief 移除Shader + * @param name Shader名称 + */ +void ShaderManager::remove(const std::string& name) { + auto it = shaders_.find(name); + if (it != shaders_.end()) { + ShaderHotReloader::getInstance().unwatch(name); + shaders_.erase(it); + E2D_LOG_DEBUG("Shader removed: {}", name); + } +} + +/** + * @brief 清除所有Shader + */ +void ShaderManager::clear() { + if (hotReloadSupported_) { + for (const auto& pair : shaders_) { + ShaderHotReloader::getInstance().unwatch(pair.first); + } + } + shaders_.clear(); + E2D_LOG_DEBUG("All shaders cleared"); +} + +/** + * @brief 注册重载回调 + * @param name Shader名称 + * @param callback 重载回调函数 + */ +void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) { + auto it = shaders_.find(name); + if (it != shaders_.end()) { + it->second.reloadCallback = callback; + } +} + +/** + * @brief 启用/禁用热重载 + * @param enabled 是否启用 + */ +void ShaderManager::setHotReloadEnabled(bool enabled) { + if (!hotReloadSupported_) { + E2D_LOG_WARN("Hot reload not supported on this platform"); + return; + } + hotReloadEnabled_ = enabled; + ShaderHotReloader::getInstance().setEnabled(enabled); + E2D_LOG_INFO("Hot reload {}", enabled ? "enabled" : "disabled"); +} + +/** + * @brief 检查热重载是否启用 + * @return 启用返回true,否则返回false + */ +bool ShaderManager::isHotReloadEnabled() const { + return hotReloadEnabled_ && hotReloadSupported_; +} + +/** + * @brief 更新热重载系统(主循环调用) + */ +void ShaderManager::update() { + if (hotReloadEnabled_ && hotReloadSupported_) { + ShaderHotReloader::getInstance().update(); + } +} + +/** + * @brief 手动重载Shader + * @param name Shader名称 + * @return 重载成功返回true,失败返回false + */ +bool ShaderManager::reload(const std::string& name) { + auto it = shaders_.find(name); + if (it == shaders_.end()) { + E2D_LOG_WARN("Shader not found for reload: {}", name); + return false; + } + + ShaderInfo& info = it->second; + + std::string vertSource = info.vertSource; + std::string fragSource = info.fragSource; + + if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) { + ShaderLoadResult result = loader_.loadFromSeparateFiles(name, info.metadata.vertPath, info.metadata.fragPath); + if (result.success) { + vertSource = result.vertSource; + fragSource = result.fragSource; + } + } else if (!info.metadata.combinedPath.empty()) { + ShaderLoadResult result = loader_.loadFromCombinedFile(info.metadata.combinedPath); + if (result.success) { + vertSource = result.vertSource; + fragSource = result.fragSource; + } + } + + Ptr newShader = factory_->createFromSource(name, vertSource, fragSource); + if (!newShader) { + E2D_LOG_ERROR("Failed to reload shader: {}", name); + return false; + } + + info.shader = newShader; + info.vertSource = vertSource; + info.fragSource = fragSource; + + if (info.reloadCallback) { + info.reloadCallback(newShader); + } + + E2D_LOG_INFO("Shader reloaded: {}", name); + return true; +} + +/** + * @brief 获取内置Shader + * @param name 内置Shader名称 + * @return Shader实例 + */ +Ptr ShaderManager::getBuiltin(const std::string& name) { + Ptr shader = get(name); + if (shader) { + return shader; + } + + std::string path = shaderDir_ + "builtin/" + name + ".shader"; + return loadFromCombinedFile(path); +} + +/** + * @brief 加载所有内置Shader + * @return 加载成功返回true,失败返回false + */ +bool ShaderManager::loadBuiltinShaders() { + if (!initialized_) { + E2D_LOG_ERROR("ShaderManager not initialized"); + return false; + } + + bool allSuccess = true; + + const char* builtinNames[] = { + "sprite", + "particle", + "shape", + "postprocess", + "font" + }; + + for (const char* name : builtinNames) { + std::string path = shaderDir_ + "builtin/" + name + ".shader"; + std::string shaderName = std::string("builtin_") + name; + + if (!loadFromCombinedFile(path)) { + E2D_LOG_ERROR("Failed to load builtin {} shader from: {}", name, path); + allSuccess = false; + } else { + auto it = shaders_.find(name); + if (it != shaders_.end()) { + shaders_[shaderName] = it->second; + shaders_.erase(it); + } + } + } + + if (allSuccess) { + E2D_LOG_INFO("All builtin shaders loaded"); + } + + return allSuccess; +} + +/** + * @brief 从缓存加载Shader + * @param name Shader名称 + * @param sourceHash 源码哈希值 + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return Shader实例 + */ +Ptr ShaderManager::loadFromCache(const std::string& name, + const std::string& sourceHash, + const std::string& vertSource, + const std::string& fragSource) { + if (!ShaderCache::getInstance().isInitialized()) { + return nullptr; + } + + if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) { + return nullptr; + } + + Ptr entry = ShaderCache::getInstance().loadCache(name); + if (!entry || entry->binary.empty()) { + return nullptr; + } + + Ptr shader = factory_->createFromBinary(name, entry->binary); + if (shader) { + E2D_LOG_DEBUG("Shader loaded from cache: {}", name); + } + + return shader; +} + +/** + * @brief 处理文件变化事件 + * @param shaderName Shader名称 + * @param event 文件变化事件 + */ +void ShaderManager::handleFileChange(const std::string& shaderName, const FileChangeEvent& event) { + E2D_LOG_DEBUG("Shader file changed: {} -> {}", shaderName, event.filepath); + reload(shaderName); +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/shader_preset.cpp b/Extra2D/src/graphics/shader_preset.cpp index fe05212..5c590ab 100644 --- a/Extra2D/src/graphics/shader_preset.cpp +++ b/Extra2D/src/graphics/shader_preset.cpp @@ -1,304 +1,183 @@ +#include #include #include namespace extra2d { -// ============================================================================ -// ShaderPreset实现 -// ============================================================================ - /** * @brief 创建水波纹效果着色器 - * - * 创建并配置一个水波纹效果的GLShader对象,包含波动速度、振幅和频率参数 - * - * @param params 水波纹效果参数,包含waveSpeed、waveAmplitude和waveFrequency - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param params 水波纹效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::Water(const WaterParams ¶ms) { - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, - ShaderSource::WaterFrag)) { - E2D_ERROR("编译水波纹Shader失败"); +Ptr ShaderPreset::Water(const WaterParams ¶ms) { + Ptr shader = ShaderManager::getInstance().get("water"); + if (!shader) { + E2D_LOG_ERROR("Failed to get water shader"); return nullptr; } - // 设置默认参数 shader->setFloat("u_waveSpeed", params.waveSpeed); shader->setFloat("u_waveAmplitude", params.waveAmplitude); shader->setFloat("u_waveFrequency", params.waveFrequency); - E2D_INFO("创建水波纹Shader预设"); return shader; } /** * @brief 创建描边效果着色器 - * - * 创建并配置一个描边效果的GLShader对象,包含描边颜色和厚度参数 - * - * @param params 描边效果参数,包含color和thickness - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param params 描边效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::Outline(const OutlineParams ¶ms) { - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, - ShaderSource::OutlineFrag)) { - E2D_ERROR("编译描边Shader失败"); +Ptr ShaderPreset::Outline(const OutlineParams ¶ms) { + Ptr shader = ShaderManager::getInstance().get("outline"); + if (!shader) { + E2D_LOG_ERROR("Failed to get outline shader"); return nullptr; } - // 设置默认参数 shader->setVec4("u_outlineColor", glm::vec4(params.color.r, params.color.g, params.color.b, params.color.a)); shader->setFloat("u_thickness", params.thickness); - E2D_INFO("创建描边Shader预设"); return shader; } /** * @brief 创建扭曲效果着色器 - * - * 创建并配置一个扭曲效果的GLShader对象,包含扭曲强度和时间缩放参数 - * - * @param params 扭曲效果参数,包含distortionAmount和timeScale - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param params 扭曲效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::Distortion(const DistortionParams ¶ms) { - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, - ShaderSource::DistortionFrag)) { - E2D_ERROR("编译扭曲Shader失败"); +Ptr ShaderPreset::Distortion(const DistortionParams ¶ms) { + Ptr shader = ShaderManager::getInstance().get("distortion"); + if (!shader) { + E2D_LOG_ERROR("Failed to get distortion shader"); return nullptr; } - // 设置默认参数 shader->setFloat("u_distortionAmount", params.distortionAmount); shader->setFloat("u_timeScale", params.timeScale); - E2D_INFO("创建扭曲Shader预设"); return shader; } /** * @brief 创建像素化效果着色器 - * - * 创建并配置一个像素化效果的GLShader对象,包含像素大小参数 - * - * @param params 像素化效果参数,包含pixelSize - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param params 像素化效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::Pixelate(const PixelateParams ¶ms) { - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, - ShaderSource::PixelateFrag)) { - E2D_ERROR("编译像素化Shader失败"); +Ptr ShaderPreset::Pixelate(const PixelateParams ¶ms) { + Ptr shader = ShaderManager::getInstance().get("pixelate"); + if (!shader) { + E2D_LOG_ERROR("Failed to get pixelate shader"); return nullptr; } - // 设置默认参数 shader->setFloat("u_pixelSize", params.pixelSize); - E2D_INFO("创建像素化Shader预设"); return shader; } /** * @brief 创建反相效果着色器 - * - * 创建并配置一个颜色反相效果的GLShader对象,包含反相强度参数 - * - * @param params 反相效果参数,包含strength - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param params 反相效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::Invert(const InvertParams ¶ms) { - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, - ShaderSource::InvertFrag)) { - E2D_ERROR("编译反相Shader失败"); +Ptr ShaderPreset::Invert(const InvertParams ¶ms) { + Ptr shader = ShaderManager::getInstance().get("invert"); + if (!shader) { + E2D_LOG_ERROR("Failed to get invert shader"); return nullptr; } - // 设置默认参数 shader->setFloat("u_strength", params.strength); - E2D_INFO("创建反相Shader预设"); return shader; } /** * @brief 创建灰度效果着色器 - * - * 创建并配置一个灰度效果的GLShader对象,包含灰度强度参数 - * - * @param params 灰度效果参数,包含intensity - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param params 灰度效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::Grayscale(const GrayscaleParams ¶ms) { - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, - ShaderSource::GrayscaleFrag)) { - E2D_ERROR("编译灰度Shader失败"); +Ptr ShaderPreset::Grayscale(const GrayscaleParams ¶ms) { + Ptr shader = ShaderManager::getInstance().get("grayscale"); + if (!shader) { + E2D_LOG_ERROR("Failed to get grayscale shader"); return nullptr; } - // 设置默认参数 shader->setFloat("u_intensity", params.intensity); - E2D_INFO("创建灰度Shader预设"); return shader; } /** * @brief 创建模糊效果着色器 - * - * 创建并配置一个模糊效果的GLShader对象,包含模糊半径参数 - * - * @param params 模糊效果参数,包含radius - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param params 模糊效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::Blur(const BlurParams ¶ms) { - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, - ShaderSource::BlurFrag)) { - E2D_ERROR("编译模糊Shader失败"); +Ptr ShaderPreset::Blur(const BlurParams ¶ms) { + Ptr shader = ShaderManager::getInstance().get("blur"); + if (!shader) { + E2D_LOG_ERROR("Failed to get blur shader"); return nullptr; } - // 设置默认参数 shader->setFloat("u_radius", params.radius); - E2D_INFO("创建模糊Shader预设"); return shader; } /** * @brief 创建灰度+描边组合效果着色器 - * - * 创建并配置一个同时应用灰度和描边效果的GLShader对象 - * - * @param grayParams 灰度效果参数,包含intensity - * @param outlineParams 描边效果参数,包含color和thickness - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param grayParams 灰度效果参数 + * @param outlineParams 描边效果参数 + * @return 配置好的着色器 */ -Ptr +Ptr ShaderPreset::GrayscaleOutline(const GrayscaleParams &grayParams, const OutlineParams &outlineParams) { - // 创建组合效果的片段着色器 (GLES 3.2) - const char *combinedFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; + std::string shaderDir = ShaderManager::getInstance().getShaderDir(); + std::string shaderPath = shaderDir + "effects/grayscale_outline.shader"; -uniform sampler2D u_texture; -uniform float u_grayIntensity; -uniform vec4 u_outlineColor; -uniform float u_thickness; -uniform vec2 u_textureSize; - -out vec4 fragColor; - -void main() { - vec4 color = texture(u_texture, v_texCoord); - - // 灰度效果 - float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); - color.rgb = mix(color.rgb, vec3(gray), u_grayIntensity); - - // 描边效果 - float alpha = 0.0; - vec2 offset = u_thickness / u_textureSize; - - alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a; - alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a; - - if (color.a < 0.1 && alpha > 0.0) { - fragColor = u_outlineColor; - } else { - fragColor = color; - } -} -)"; - - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, combinedFrag)) { - E2D_ERROR("编译灰度+描边Shader失败"); + Ptr shader = + ShaderManager::getInstance().loadFromCombinedFile(shaderPath); + if (!shader) { + E2D_LOG_ERROR("Failed to load grayscale_outline shader from: {}", + shaderPath); return nullptr; } - // 设置默认参数 shader->setFloat("u_grayIntensity", grayParams.intensity); shader->setVec4("u_outlineColor", glm::vec4(outlineParams.color.r, outlineParams.color.g, outlineParams.color.b, outlineParams.color.a)); shader->setFloat("u_thickness", outlineParams.thickness); - E2D_INFO("创建灰度+描边组合Shader预设"); return shader; } /** * @brief 创建像素化+反相组合效果着色器 - * - * 创建并配置一个同时应用像素化和反相效果的GLShader对象 - * - * @param pixParams 像素化效果参数,包含pixelSize - * @param invParams 反相效果参数,包含strength - * @return 成功返回配置好的着色器智能指针,失败返回nullptr + * @param pixParams 像素化效果参数 + * @param invParams 反相效果参数 + * @return 配置好的着色器 */ -Ptr ShaderPreset::PixelateInvert(const PixelateParams &pixParams, - const InvertParams &invParams) { - // 创建组合效果的片段着色器 (GLES 3.2) - const char *combinedFrag = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; +Ptr ShaderPreset::PixelateInvert(const PixelateParams &pixParams, + const InvertParams &invParams) { + std::string shaderDir = ShaderManager::getInstance().getShaderDir(); + std::string shaderPath = shaderDir + "effects/pixelate_invert.shader"; -uniform sampler2D u_texture; -uniform float u_pixelSize; -uniform vec2 u_textureSize; -uniform float u_invertStrength; - -out vec4 fragColor; - -void main() { - // 像素化 - vec2 pixel = u_pixelSize / u_textureSize; - vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5; - - vec4 color = texture(u_texture, uv); - - // 反相 - vec3 inverted = 1.0 - color.rgb; - color.rgb = mix(color.rgb, inverted, u_invertStrength); - - fragColor = color; -} -)"; - - auto shader = std::make_shared(); - - if (!shader->compileFromSource(ShaderSource::StandardVert, combinedFrag)) { - E2D_ERROR("编译像素化+反相Shader失败"); + Ptr shader = + ShaderManager::getInstance().loadFromCombinedFile(shaderPath); + if (!shader) { + E2D_LOG_ERROR("Failed to load pixelate_invert shader from: {}", shaderPath); return nullptr; } - // 设置默认参数 shader->setFloat("u_pixelSize", pixParams.pixelSize); shader->setFloat("u_invertStrength", invParams.strength); - E2D_INFO("创建像素化+反相组合Shader预设"); return shader; } diff --git a/Extra2D/src/graphics/shader_system.cpp b/Extra2D/src/graphics/shader_system.cpp deleted file mode 100644 index 3c189db..0000000 --- a/Extra2D/src/graphics/shader_system.cpp +++ /dev/null @@ -1,667 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -namespace extra2d { - -// ============================================================================ -// 内置Shader源码 -// ============================================================================ - -// 标准精灵着色器 (GLES 3.2) -static const char *BUILTIN_SPRITE_VERT = R"( -#version 300 es -precision highp float; -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; -uniform mat4 u_model; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} -)"; - -static const char *BUILTIN_SPRITE_FRAG = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform float u_opacity; - -out vec4 fragColor; - -void main() { - vec4 texColor = texture(u_texture, v_texCoord); - fragColor = texColor * v_color; - fragColor.a *= u_opacity; - - if (fragColor.a < 0.01) { - discard; - } -} -)"; - -// 粒子着色器 (GLES 3.2) -static const char *BUILTIN_PARTICLE_VERT = R"( -#version 300 es -precision highp float; -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; -layout(location = 3) in float a_size; -layout(location = 4) in float a_rotation; - -uniform mat4 u_viewProjection; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - float c = cos(a_rotation); - float s = sin(a_rotation); - mat2 rot = mat2(c, -s, s, c); - - vec2 pos = rot * a_position * a_size; - gl_Position = u_viewProjection * vec4(pos, 0.0, 1.0); - - v_texCoord = a_texCoord; - v_color = a_color; -} -)"; - -static const char *BUILTIN_PARTICLE_FRAG = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; -uniform int u_textureEnabled; - -out vec4 fragColor; - -void main() { - vec4 color = v_color; - - if (u_textureEnabled > 0) { - color *= texture(u_texture, v_texCoord); - } - - // 圆形粒子 - vec2 center = v_texCoord - vec2(0.5); - float dist = length(center); - float alpha = 1.0 - smoothstep(0.4, 0.5, dist); - color.a *= alpha; - - if (color.a < 0.01) { - discard; - } - - fragColor = color; -} -)"; - -// 后处理着色器 (GLES 3.2) -static const char *BUILTIN_POSTPROCESS_VERT = R"( -#version 300 es -precision highp float; -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; - -out vec2 v_texCoord; - -void main() { - gl_Position = vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; -} -)"; - -static const char *BUILTIN_POSTPROCESS_FRAG = R"( -#version 300 es -precision highp float; -in vec2 v_texCoord; - -uniform sampler2D u_texture; -uniform vec2 u_resolution; -uniform float u_time; - -out vec4 fragColor; - -void main() { - fragColor = texture(u_texture, v_texCoord); -} -)"; - -// 形状渲染着色器 (GLES 3.2) -static const char *BUILTIN_SHAPE_VERT = R"( -#version 300 es -precision highp float; -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec4 a_color; - -uniform mat4 u_viewProjection; - -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); - v_color = a_color; -} -)"; - -static const char *BUILTIN_SHAPE_FRAG = R"( -#version 300 es -precision highp float; -in vec4 v_color; - -out vec4 fragColor; - -void main() { - fragColor = v_color; -} -)"; - -// ============================================================================ -// ShaderSystem实现 -// ============================================================================ - -/** - * @brief 获取ShaderSystem单例实例 - * @return ShaderSystem单例的引用 - * - * 使用静态局部变量实现线程安全的单例模式 - */ -ShaderSystem &ShaderSystem::get() { - static ShaderSystem instance; - return instance; -} - -/** - * @brief 初始化Shader系统 - * @return 初始化成功返回true,失败返回false - * - * 加载所有内置着色器,包括精灵、粒子、后处理和形状着色器 - */ -bool ShaderSystem::init() { - E2D_INFO("初始化Shader系统..."); - - if (!loadBuiltinShaders()) { - E2D_ERROR("加载内置Shader失败!"); - return false; - } - - E2D_INFO("Shader系统初始化完成"); - return true; -} - -/** - * @brief 关闭Shader系统 - * - * 清理所有着色器资源,释放内置着色器 - */ -void ShaderSystem::shutdown() { - E2D_INFO("关闭Shader系统..."); - clear(); - - builtinSpriteShader_.reset(); - builtinParticleShader_.reset(); - builtinPostProcessShader_.reset(); - builtinShapeShader_.reset(); -} - -/** - * @brief 加载内置着色器 - * @return 加载成功返回true,失败返回false - * - * 编译并加载所有内置着色器:精灵、粒子、后处理和形状着色器 - */ -bool ShaderSystem::loadBuiltinShaders() { - // 加载精灵Shader - builtinSpriteShader_ = std::make_shared(); - if (!builtinSpriteShader_->compileFromSource(BUILTIN_SPRITE_VERT, - BUILTIN_SPRITE_FRAG)) { - E2D_ERROR("编译内置精灵Shader失败!"); - return false; - } - - // 加载粒子Shader - builtinParticleShader_ = std::make_shared(); - if (!builtinParticleShader_->compileFromSource(BUILTIN_PARTICLE_VERT, - BUILTIN_PARTICLE_FRAG)) { - E2D_ERROR("编译内置粒子Shader失败!"); - return false; - } - - // 加载后处理Shader - builtinPostProcessShader_ = std::make_shared(); - if (!builtinPostProcessShader_->compileFromSource(BUILTIN_POSTPROCESS_VERT, - BUILTIN_POSTPROCESS_FRAG)) { - E2D_ERROR("编译内置后处理Shader失败!"); - return false; - } - - // 加载形状Shader - builtinShapeShader_ = std::make_shared(); - if (!builtinShapeShader_->compileFromSource(BUILTIN_SHAPE_VERT, - BUILTIN_SHAPE_FRAG)) { - E2D_ERROR("编译内置形状Shader失败!"); - return false; - } - - E2D_INFO("内置Shader加载成功"); - return true; -} - -/** - * @brief 从文件加载着色器 - * @param name 着色器名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载成功返回着色器指针,失败返回nullptr - * - * 从指定路径读取顶点和片段着色器源码并编译 - */ -Ptr ShaderSystem::loadFromFile(const std::string &name, - const std::string &vertPath, - const std::string &fragPath) { - // 读取文件内容 - std::string vertSource = readFile(vertPath); - std::string fragSource = readFile(fragPath); - - if (vertSource.empty()) { - E2D_ERROR("无法读取顶点着色器文件: {}", vertPath); - return nullptr; - } - - if (fragSource.empty()) { - E2D_ERROR("无法读取片段着色器文件: {}", fragPath); - return nullptr; - } - - // 编译Shader - auto shader = std::make_shared(); - if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) { - E2D_ERROR("编译Shader '{}' 失败", name); - return nullptr; - } - - // 存储Shader信息 - ShaderInfo info; - info.shader = shader; - info.vertPath = vertPath; - info.fragPath = fragPath; - info.vertModifiedTime = getFileModifiedTime(vertPath); - info.fragModifiedTime = getFileModifiedTime(fragPath); - info.isBuiltin = false; - - shaders_[name] = std::move(info); - - E2D_INFO("加载Shader '{}' 成功", name); - return shader; -} - -/** - * @brief 从源码加载着色器 - * @param name 着色器名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载成功返回着色器指针,失败返回nullptr - * - * 直接从源码字符串编译着色器 - */ -Ptr ShaderSystem::loadFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource) { - auto shader = std::make_shared(); - if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) { - E2D_ERROR("编译Shader '{}' 失败", name); - return nullptr; - } - - ShaderInfo info; - info.shader = shader; - info.vertModifiedTime = 0; - info.fragModifiedTime = 0; - info.isBuiltin = false; - - shaders_[name] = std::move(info); - - E2D_INFO("加载Shader '{}' 成功", name); - return shader; -} - -/** - * @brief 获取指定名称的着色器 - * @param name 着色器名称 - * @return 找到返回着色器指针,未找到返回nullptr - */ -Ptr ShaderSystem::get(const std::string &name) { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - return nullptr; -} - -/** - * @brief 检查指定名称的着色器是否存在 - * @param name 着色器名称 - * @return 存在返回true,不存在返回false - */ -bool ShaderSystem::has(const std::string &name) const { - return shaders_.find(name) != shaders_.end(); -} - -/** - * @brief 移除指定名称的着色器 - * @param name 着色器名称 - * - * 从着色器管理器中移除指定着色器 - */ -void ShaderSystem::remove(const std::string &name) { shaders_.erase(name); } - -/** - * @brief 清空所有着色器 - * - * 移除所有已加载的着色器 - */ -void ShaderSystem::clear() { shaders_.clear(); } - -/** - * @brief 设置文件监视功能 - * @param enable 是否启用 - * - * 启用或禁用着色器文件变化监视功能 - */ -void ShaderSystem::setFileWatching(bool enable) { - fileWatching_ = enable; - if (enable) { - E2D_INFO("启用Shader文件监视"); - } else { - E2D_INFO("禁用Shader文件监视"); - } -} - -/** - * @brief 更新文件监视 - * - * 定期检查着色器文件是否发生变化,如有变化则自动重载 - */ -void ShaderSystem::updateFileWatching() { - if (!fileWatching_) - return; - - watchTimer_ += 0.016f; // 假设60fps - if (watchTimer_ >= WATCH_INTERVAL) { - watchTimer_ = 0.0f; - checkAndReload(); - } -} - -/** - * @brief 检查并重载变化的着色器 - * - * 检查所有着色器文件的修改时间,如有变化则自动重载 - */ -void ShaderSystem::checkAndReload() { - for (auto &[name, info] : shaders_) { - if (info.isBuiltin) - continue; - if (info.vertPath.empty() || info.fragPath.empty()) - continue; - - uint64_t vertTime = getFileModifiedTime(info.vertPath); - uint64_t fragTime = getFileModifiedTime(info.fragPath); - - if (vertTime > info.vertModifiedTime || fragTime > info.fragModifiedTime) { - E2D_INFO("检测到Shader '{}' 文件变化,正在重载...", name); - reload(name); - } - } -} - -/** - * @brief 重载指定着色器 - * @param name 着色器名称 - * @return 重载成功返回true,失败返回false - * - * 重新从文件加载并编译指定着色器 - */ -bool ShaderSystem::reload(const std::string &name) { - auto it = shaders_.find(name); - if (it == shaders_.end()) { - E2D_ERROR("无法重载不存在的Shader '{}'", name); - return false; - } - - auto &info = it->second; - if (info.isBuiltin) { - E2D_WARN("无法重载内置Shader '{}'", name); - return false; - } - - if (info.vertPath.empty() || info.fragPath.empty()) { - E2D_ERROR("Shader '{}' 没有关联的文件路径", name); - return false; - } - - // 重新加载 - auto newShader = loadFromFile(name, info.vertPath, info.fragPath); - if (newShader) { - E2D_INFO("重载Shader '{}' 成功", name); - return true; - } - - return false; -} - -/** - * @brief 重载所有着色器 - * - * 重载所有非内置着色器 - */ -void ShaderSystem::reloadAll() { - for (const auto &[name, info] : shaders_) { - if (!info.isBuiltin) { - reload(name); - } - } -} - -/** - * @brief 获取内置精灵着色器 - * @return 精灵着色器指针 - */ -Ptr ShaderSystem::getBuiltinSpriteShader() { - return builtinSpriteShader_; -} - -/** - * @brief 获取内置粒子着色器 - * @return 粒子着色器指针 - */ -Ptr ShaderSystem::getBuiltinParticleShader() { - return builtinParticleShader_; -} - -/** - * @brief 获取内置后处理着色器 - * @return 后处理着色器指针 - */ -Ptr ShaderSystem::getBuiltinPostProcessShader() { - return builtinPostProcessShader_; -} - -/** - * @brief 获取内置形状着色器 - * @return 形状着色器指针 - */ -Ptr ShaderSystem::getBuiltinShapeShader() { - return builtinShapeShader_; -} - -/** - * @brief 读取文件内容 - * @param filepath 文件路径 - * @return 文件内容字符串,读取失败返回空字符串 - * - * 以二进制模式读取文件全部内容 - */ -std::string ShaderSystem::readFile(const std::string &filepath) { - std::ifstream file(filepath, std::ios::in | std::ios::binary); - if (!file.is_open()) { - return ""; - } - - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); -} - -/** - * @brief 获取文件修改时间 - * @param filepath 文件路径 - * @return 文件最后修改时间戳,失败返回0 - * - * 获取文件的最后修改时间,用于文件监视功能 - */ -uint64_t ShaderSystem::getFileModifiedTime(const std::string &filepath) { -#ifdef _WIN32 - struct _stat64 statBuf; - if (_stat64(filepath.c_str(), &statBuf) == 0) { - return static_cast(statBuf.st_mtime); - } -#else - struct stat statBuf; - if (stat(filepath.c_str(), &statBuf) == 0) { - return static_cast(statBuf.st_mtime); - } -#endif - return 0; -} - -// ============================================================================ -// ShaderParams实现 -// ============================================================================ - -/** - * @brief 构造函数 - * @param shader 关联的着色器对象 - * - * 创建一个着色器参数设置器 - */ -ShaderParams::ShaderParams(GLShader &shader) : shader_(shader) {} - -/** - * @brief 设置布尔类型uniform变量 - * @param name 变量名 - * @param value 布尔值 - * @return 当前对象引用,支持链式调用 - */ -ShaderParams &ShaderParams::setBool(const std::string &name, bool value) { - shader_.setBool(name, value); - return *this; -} - -/** - * @brief 设置整数类型uniform变量 - * @param name 变量名 - * @param value 整数值 - * @return 当前对象引用,支持链式调用 - */ -ShaderParams &ShaderParams::setInt(const std::string &name, int value) { - shader_.setInt(name, value); - return *this; -} - -/** - * @brief 设置浮点类型uniform变量 - * @param name 变量名 - * @param value 浮点值 - * @return 当前对象引用,支持链式调用 - */ -ShaderParams &ShaderParams::setFloat(const std::string &name, float value) { - shader_.setFloat(name, value); - return *this; -} - -/** - * @brief 设置二维向量类型uniform变量 - * @param name 变量名 - * @param value 二维向量值 - * @return 当前对象引用,支持链式调用 - */ -ShaderParams &ShaderParams::setVec2(const std::string &name, - const glm::vec2 &value) { - shader_.setVec2(name, value); - return *this; -} - -/** - * @brief 设置三维向量类型uniform变量 - * @param name 变量名 - * @param value 三维向量值 - * @return 当前对象引用,支持链式调用 - */ -ShaderParams &ShaderParams::setVec3(const std::string &name, - const glm::vec3 &value) { - shader_.setVec3(name, value); - return *this; -} - -/** - * @brief 设置四维向量类型uniform变量 - * @param name 变量名 - * @param value 四维向量值 - * @return 当前对象引用,支持链式调用 - */ -ShaderParams &ShaderParams::setVec4(const std::string &name, - const glm::vec4 &value) { - shader_.setVec4(name, value); - return *this; -} - -/** - * @brief 设置4x4矩阵类型uniform变量 - * @param name 变量名 - * @param value 4x4矩阵值 - * @return 当前对象引用,支持链式调用 - */ -ShaderParams &ShaderParams::setMat4(const std::string &name, - const glm::mat4 &value) { - shader_.setMat4(name, value); - return *this; -} - -/** - * @brief 设置颜色类型uniform变量 - * @param name 变量名 - * @param color 颜色值 - * @return 当前对象引用,支持链式调用 - * - * 将颜色对象转换为四维向量并设置 - */ -ShaderParams &ShaderParams::setColor(const std::string &name, - const Color &color) { - shader_.setVec4(name, glm::vec4(color.r, color.g, color.b, color.a)); - return *this; -} - -} // namespace extra2d diff --git a/README.md b/README.md deleted file mode 100644 index 2deb948..0000000 --- a/README.md +++ /dev/null @@ -1,221 +0,0 @@ -
- -![Extra2D Logo](./logo/logo_text_dark.svg) - -

- - Release - - - License - - - Build Status - - - C++17 - - - Nintendo Switch - -

- -

- 🎮 专为 Nintendo Switch 打造的轻量级 2D 游戏引擎
- 高性能、易用、原生支持 Switch 平台 -

- -[📖 构建指南](./docs/Extra2D%20构建系统文档.md) | [🚀 快速开始](#快速开始) | [📦 示例程序](#示例程序) | [📚 API 教程](./docs/API_Tutorial/01_Quick_Start.md) - -
- ---- - -## 🌟 简介 - -**Extra2D** 是一个专为 **Nintendo Switch** 平台设计的轻量级 2D 游戏引擎,采用现代 C++17 架构,充分利用 Switch 硬件特性,为开发者提供流畅的游戏开发体验。 - -> 💡 Extra2D 的诞生是为了让 Switch 独立游戏开发变得更加简单高效。无论是复古风格的像素游戏,还是现代化的 2D 作品,Extra2D 都能提供强大的支持。 - -### ✨ 核心特性 - -- **🎯 Switch 原生支持**:专为 Nintendo Switch 硬件优化,支持掌机/主机双模式 -- **🎬 高级动画系统**:支持骨骼动画、精灵动画、补间动画 -- **🎵 音频系统**:基于 SDL2_mixer 的高质量音频播放,支持 BGM 和音效 -- **🎨 渲染系统**:基于 OpenGL ES 的 2D 渲染,支持自定义着色器 -- **💾 数据持久化**:游戏存档、配置文件的便捷读写 -- **🔧 空间索引**:内置四叉树和空间哈希碰撞检测系统 -- **🖱️ UI 系统**:完整的 UI 控件支持(按钮、文本、滑块等) - ---- - -## 🚀 快速开始 - -### 环境要求 - -| 组件 | 要求 | -|:----:|:-----| -| 开发环境 | devkitPro + devkitA64 (Switch) / MinGW-w64 (Windows) | -| C++ 标准 | C++17 | -| 构建工具 | xmake | -| 目标平台 | Nintendo Switch / Windows (MinGW) | - -### 安装 xmake - -```bash -# Windows (PowerShell) -Invoke-Expression (Invoke-WebRequest 'https://xmake.io/psget.text' -UseBasicParsing).Content - -# macOS -brew install xmake - -# Linux -sudo add-apt-repository ppa:xmake-io/xmake -sudo apt update -sudo apt install xmake -``` - -## 📚 文档 - -- [📖 API 教程](./docs/API_Tutorial/01_Quick_Start.md) - 完整的 API 使用教程 - - [01. 快速开始](./docs/API_Tutorial/01_Quick_Start.md) - - [02. 场景系统](./docs/API_Tutorial/02_Scene_System.md) - - [03. 节点系统](./docs/API_Tutorial/03_Node_System.md) - - [04. 资源管理](./docs/API_Tutorial/04_Resource_Management.md) - - [05. 输入处理](./docs/API_Tutorial/05_Input_Handling.md) - - [06. 碰撞检测](./docs/API_Tutorial/06_Collision_Detection.md) - - [07. UI 系统](./docs/API_Tutorial/07_UI_System.md) - - [08. 音频系统](./docs/API_Tutorial/08_Audio_System.md) -- [🔧 构建系统文档](./docs/Extra2D%20构建系统文档.md) - 详细的构建系统说明 - ---- - -## 🏗️ 架构概览 - -```mermaid -mindmap - root((Extra2D
引擎架构)) - 核心基础层 - 数学库 - 向量 Vec2/Vec3 - 矩形 Rect - 矩阵 glm::mat4 - 颜色 Color - 大小 Size - 类型系统 - 智能指针 Ptr/UniquePtr - 字符串 String - 平台抽象层 - 窗口管理 Window - 输入处理 Input - 键盘 Keyboard - 鼠标 Mouse - 手柄 Gamepad - 触摸 Touch - 输入码 InputCodes - 渲染系统 - 渲染后端 RenderBackend - OpenGL 实现 - GLRenderer - GLShader - GLTexture - GLSpriteBatch - 相机 Camera - 纹理 Texture - 字体 Font - 着色器系统 ShaderSystem - 渲染目标 RenderTarget - 场景系统 - 场景管理器 SceneManager - 场景 Scene - 过渡动画 Transition - 节点系统 Node - 精灵 Sprite - 动画精灵 AnimatedSprite - 形状 ShapeNode - 粒子系统 ParticleSystem - 空间索引 - 空间管理器 SpatialManager - 四叉树 QuadTree - 空间哈希 SpatialHash - 动画系统 - 动画控制器 AnimationController - 动画剪辑 AnimationClip - 关键帧 AnimationFrame - 插值引擎 InterpolationEngine - 精灵帧 SpriteFrame - 动作系统 - 动作基类 Action - 位移动作 MoveBy/MoveTo - 缩放动作 ScaleBy/ScaleTo - 旋转动作 RotateBy/RotateTo - 跳跃动作 JumpBy/JumpTo - 淡入淡出 FadeIn/FadeOut - 组合动作 Sequence/Spawn/Repeat - 缓动函数 Ease - 事件系统 - 事件队列 EventQueue - 事件分发 EventDispatcher - 事件类型 - 窗口事件 - 键盘事件 - 鼠标事件 - UI事件 - 音频系统 - 音频引擎 AudioEngine - 音效 Sound - 资源管理 - 资源管理器 ResourceManager - 纹理池 TexturePool - 显存管理 VRAMManager - UI系统 - 基础控件 Widget - 按钮 Button - 标签 Label - 文本 Text - 进度条 ProgressBar - 滑块 Slider - 复选框 CheckBox - 单选框 RadioButton - 特效系统 - 粒子系统 ParticleSystem - 后处理 PostProcess - 自定义效果 CustomEffectManager - 工具库 - 日志 Logger - 计时器 Timer - 随机数 Random - 数据持久化 Data -``` - -## 🛠️ 技术栈 - -| 技术 | 用途 | 版本 | -|:----:|:-----|:----:| -| OpenGL ES | 2D 图形渲染 | 3.0+ | -| GLFW | 窗口和输入管理 | 3.3+ | -| GLM | 数学库 | 0.9.9+ | -| SDL2_mixer | 音频播放 | 2.0+ | -| spdlog | 日志系统 | 最新版 | -| stb_image | 图像加载 | 最新版 | -| freetype | 字体渲染 | 最新版 | -| xmake | 构建系统 | 2.5+ | - ---- - -## 🤝 贡献 - -欢迎提交 Issue 和 Pull Request! - ---- - -## 📄 许可证 - -Extra2D 使用 [MIT](LICENSE) 许可证。 - ---- - -## 联系方式 - -- GitHub Issues: https://github.com/ChestnutYueyue/extra2d/issues -- 作者: [ChestnutYueyue](https://github.com/ChestnutYueyue)