feat(shader): 支持单文件分段着色器并更新内置默认着色器
- 扩展 ShaderLoader 以支持 .glsl 单文件分段格式(#type vertex/fragment) - 将内置默认着色器从分离的 .vert/.frag 文件合并为单个 default.glsl 文件 - 更新 BuiltinAssetFactory 使用新的加载方式创建默认着色器 - 删除旧的 default.frag 文件,简化着色器资源管理
This commit is contained in:
parent
3df1703839
commit
1097aeae6c
|
|
@ -17,7 +17,7 @@ public:
|
|||
explicit ShaderLoader(AssetFileSystem fileSystem);
|
||||
/**
|
||||
* @brief 从文件加载着色器
|
||||
* @param path 单个文件路径(自动推断 .vert/.frag)
|
||||
* @param path 单个文件路径(.glsl 单文件分段,或自动推断 .vert/.frag)
|
||||
* @return 着色器指针,失败返回 nullptr
|
||||
*/
|
||||
Ptr<Shader> load(const std::string& path) override;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include <assets/builtin/builtin_asset_factory.h>
|
||||
#include <assets/loaders/shader_loader.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
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> shader = makePtr<Shader>();
|
||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
||||
ShaderLoader shaderLoader(fileSystem_);
|
||||
Ptr<Shader> shader = shaderLoader.load("shader/default.glsl");
|
||||
if (!shader) {
|
||||
return false;
|
||||
}
|
||||
defaultShader_ = shaders_.insert(shader);
|
||||
|
|
@ -92,4 +84,3 @@ Handle<Material> BuiltinAssetFactory::defaultMaterial() const {
|
|||
Handle<Mesh> BuiltinAssetFactory::defaultQuad() const { return defaultQuad_; }
|
||||
|
||||
} // namespace extra2d
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,91 @@
|
|||
#include <assets/loaders/shader_loader.h>
|
||||
#include <renderer/shader.h>
|
||||
#include <utils/logger.h>
|
||||
#include <sstream>
|
||||
|
||||
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<Shader> 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<Shader>();
|
||||
}
|
||||
|
||||
std::string vsSource;
|
||||
std::string fsSource;
|
||||
if (!parseCombinedShaderSource(source, vsSource, fsSource)) {
|
||||
E2D_ERROR("ShaderLoader: 单文件着色器缺少分段标记(#type vertex/#type fragment): {}", path);
|
||||
return Ptr<Shader>();
|
||||
}
|
||||
|
||||
Ptr<Shader> shader = makePtr<Shader>();
|
||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
||||
E2D_ERROR("ShaderLoader: 编译单文件着色器失败 {}", path);
|
||||
return Ptr<Shader>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue