From 1097aeae6c8bba9a893bf40656b5f0720018464d Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Mon, 16 Mar 2026 19:18:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(shader):=20=E6=94=AF=E6=8C=81=E5=8D=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=88=86=E6=AE=B5=E7=9D=80=E8=89=B2=E5=99=A8?= =?UTF-8?q?=E5=B9=B6=E6=9B=B4=E6=96=B0=E5=86=85=E7=BD=AE=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E7=9D=80=E8=89=B2=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 扩展 ShaderLoader 以支持 .glsl 单文件分段格式(#type vertex/fragment) - 将内置默认着色器从分离的 .vert/.frag 文件合并为单个 default.glsl 文件 - 更新 BuiltinAssetFactory 使用新的加载方式创建默认着色器 - 删除旧的 default.frag 文件,简化着色器资源管理 --- include/assets/loaders/shader_loader.h | 2 +- shader/default.frag | 40 ---------- shader/{default.vert => default.glsl} | 43 +++++++---- src/assets/builtin/builtin_asset_factory.cpp | 17 +---- src/assets/loaders/shader_loader.cpp | 77 +++++++++++++++++++- 5 files changed, 111 insertions(+), 68 deletions(-) delete mode 100644 shader/default.frag rename shader/{default.vert => default.glsl} (58%) diff --git a/include/assets/loaders/shader_loader.h b/include/assets/loaders/shader_loader.h index 37ab808..940a232 100644 --- a/include/assets/loaders/shader_loader.h +++ b/include/assets/loaders/shader_loader.h @@ -17,7 +17,7 @@ public: explicit ShaderLoader(AssetFileSystem fileSystem); /** * @brief 从文件加载着色器 - * @param path 单个文件路径(自动推断 .vert/.frag) + * @param path 单个文件路径(.glsl 单文件分段,或自动推断 .vert/.frag) * @return 着色器指针,失败返回 nullptr */ Ptr load(const std::string& path) override; diff --git a/shader/default.frag b/shader/default.frag deleted file mode 100644 index 4a235f0..0000000 --- a/shader/default.frag +++ /dev/null @@ -1,40 +0,0 @@ -#version 320 es -precision highp float; - -// 从顶点着色器输入 -in vec2 vTexCoord; -in vec4 vColor; -in vec4 vTintColor; -in float vOpacity; - -// 纹理采样器 -uniform sampler2D uTexture; - -// 输出颜色 -out vec4 fragColor; - -/** - * @brief 片段着色器入口 - * - * 采样纹理并与顶点颜色、色调和透明度混合 - */ -void main() { - // 采样纹理 - vec4 texColor = texture(uTexture, vTexCoord); - - // 如果纹理采样结果是黑色或透明,使用白色作为默认值 - if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) { - texColor = vec4(1.0, 1.0, 1.0, 1.0); - } - - // 混合:纹理 * 顶点颜色 * 色调 - fragColor = texColor * vColor * vTintColor; - - // 应用透明度 - fragColor.a *= vOpacity; - - // Alpha 测试:丢弃几乎透明的像素 - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/shader/default.vert b/shader/default.glsl similarity index 58% rename from shader/default.vert rename to shader/default.glsl index 95fca8b..8a2f9f5 100644 --- a/shader/default.vert +++ b/shader/default.glsl @@ -1,7 +1,7 @@ +#type vertex #version 320 es precision highp float; -// 全局 UBO (binding = 0) - 每帧更新一次 layout(std140, binding = 0) uniform GlobalUBO { mat4 uViewProjection; vec4 uCameraPosition; @@ -10,39 +10,56 @@ layout(std140, binding = 0) uniform GlobalUBO { vec2 uScreenSize; }; -// 材质 UBO (binding = 1) - 每物体更新 layout(std140, binding = 1) uniform MaterialUBO { vec4 uColor; vec4 uTintColor; float uOpacity; - float uPadding[3]; // std140 对齐填充 + float uPadding[3]; }; -// 模型矩阵作为单独的统一变量(每个物体设置) uniform mat4 uModelMatrix; -// 顶点属性 layout(location = 0) in vec2 aPosition; layout(location = 1) in vec2 aTexCoord; layout(location = 2) in vec4 aColor; -// 输出到片段着色器 out vec2 vTexCoord; out vec4 vColor; out vec4 vTintColor; out float vOpacity; -/** - * @brief 顶点着色器入口 - * - * 计算顶点在裁剪空间中的位置, - * 并传递纹理坐标和颜色到片段着色器 - */ void main() { gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0); vTexCoord = aTexCoord; - // 混合顶点颜色和材质 UBO 中的颜色 vColor = aColor * uColor; vTintColor = uTintColor; vOpacity = uOpacity; } + +#type fragment +#version 320 es +precision highp float; + +in vec2 vTexCoord; +in vec4 vColor; +in vec4 vTintColor; +in float vOpacity; + +uniform sampler2D uTexture; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(uTexture, vTexCoord); + + if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) { + texColor = vec4(1.0, 1.0, 1.0, 1.0); + } + + fragColor = texColor * vColor * vTintColor; + fragColor.a *= vOpacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/src/assets/builtin/builtin_asset_factory.cpp b/src/assets/builtin/builtin_asset_factory.cpp index 9aac6dc..ef80312 100644 --- a/src/assets/builtin/builtin_asset_factory.cpp +++ b/src/assets/builtin/builtin_asset_factory.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace extra2d { @@ -22,18 +23,9 @@ bool BuiltinAssetFactory::create() { } { - std::string vertPath = fileSystem_.assetPath("shader/default.vert"); - std::string fragPath = fileSystem_.assetPath("shader/default.frag"); - if (!fileSystem_.exists(vertPath) || !fileSystem_.exists(fragPath)) { - return false; - } - std::string vsSource = fileSystem_.readString(vertPath); - std::string fsSource = fileSystem_.readString(fragPath); - if (vsSource.empty() || fsSource.empty()) { - return false; - } - Ptr shader = makePtr(); - if (!shader->loadFromSource(vsSource, fsSource)) { + ShaderLoader shaderLoader(fileSystem_); + Ptr shader = shaderLoader.load("shader/default.glsl"); + if (!shader) { return false; } defaultShader_ = shaders_.insert(shader); @@ -92,4 +84,3 @@ Handle BuiltinAssetFactory::defaultMaterial() const { Handle BuiltinAssetFactory::defaultQuad() const { return defaultQuad_; } } // namespace extra2d - diff --git a/src/assets/loaders/shader_loader.cpp b/src/assets/loaders/shader_loader.cpp index fa1cb36..b6fab19 100644 --- a/src/assets/loaders/shader_loader.cpp +++ b/src/assets/loaders/shader_loader.cpp @@ -1,16 +1,91 @@ #include #include #include +#include namespace extra2d { +namespace { + +enum class ShaderStageType { + None, + Vertex, + Fragment +}; + +static bool parseCombinedShaderSource(const std::string& source, + std::string& vsSource, + std::string& fsSource) { + std::istringstream stream(source); + std::string line; + ShaderStageType stage = ShaderStageType::None; + + while (std::getline(stream, line)) { + const size_t first = line.find_first_not_of(" \t"); + std::string trimmed = first == std::string::npos ? "" : line.substr(first); + + if (!trimmed.empty() && (trimmed.rfind("#type", 0) == 0 || trimmed.rfind("#shader", 0) == 0)) { + std::istringstream directive(trimmed); + std::string key; + std::string value; + directive >> key >> value; + + if (value == "vertex" || value == "vert" || value == "vs") { + stage = ShaderStageType::Vertex; + } else if (value == "fragment" || value == "frag" || value == "fs") { + stage = ShaderStageType::Fragment; + } else { + stage = ShaderStageType::None; + } + continue; + } + + if (stage == ShaderStageType::Vertex) { + vsSource += line + "\n"; + } else if (stage == ShaderStageType::Fragment) { + fsSource += line + "\n"; + } + } + + return !vsSource.empty() && !fsSource.empty(); +} + +} ShaderLoader::ShaderLoader(AssetFileSystem fileSystem) : fileSystem_(std::move(fileSystem)) {} Ptr ShaderLoader::load(const std::string &path) { + std::string::size_type dotPos = path.rfind('.'); + if (dotPos != std::string::npos) { + std::string ext = path.substr(dotPos); + if (ext == ".glsl") { + std::string source = fileSystem_.readString(path); + if (source.empty()) { + E2D_ERROR("ShaderLoader: 读取着色器失败: {}", path); + return Ptr(); + } + + std::string vsSource; + std::string fsSource; + if (!parseCombinedShaderSource(source, vsSource, fsSource)) { + E2D_ERROR("ShaderLoader: 单文件着色器缺少分段标记(#type vertex/#type fragment): {}", path); + return Ptr(); + } + + Ptr shader = makePtr(); + if (!shader->loadFromSource(vsSource, fsSource)) { + E2D_ERROR("ShaderLoader: 编译单文件着色器失败 {}", path); + return Ptr(); + } + + E2D_DEBUG("ShaderLoader: 已加载单文件着色器 {}", path); + return shader; + } + } + std::string basePath = path; - std::string::size_type dotPos = basePath.rfind('.'); + dotPos = basePath.rfind('.'); if (dotPos != std::string::npos) { basePath = basePath.substr(0, dotPos); }