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);
|
explicit ShaderLoader(AssetFileSystem fileSystem);
|
||||||
/**
|
/**
|
||||||
* @brief 从文件加载着色器
|
* @brief 从文件加载着色器
|
||||||
* @param path 单个文件路径(自动推断 .vert/.frag)
|
* @param path 单个文件路径(.glsl 单文件分段,或自动推断 .vert/.frag)
|
||||||
* @return 着色器指针,失败返回 nullptr
|
* @return 着色器指针,失败返回 nullptr
|
||||||
*/
|
*/
|
||||||
Ptr<Shader> load(const std::string& path) override;
|
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
|
#version 320 es
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
// 全局 UBO (binding = 0) - 每帧更新一次
|
|
||||||
layout(std140, binding = 0) uniform GlobalUBO {
|
layout(std140, binding = 0) uniform GlobalUBO {
|
||||||
mat4 uViewProjection;
|
mat4 uViewProjection;
|
||||||
vec4 uCameraPosition;
|
vec4 uCameraPosition;
|
||||||
|
|
@ -10,39 +10,56 @@ layout(std140, binding = 0) uniform GlobalUBO {
|
||||||
vec2 uScreenSize;
|
vec2 uScreenSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 材质 UBO (binding = 1) - 每物体更新
|
|
||||||
layout(std140, binding = 1) uniform MaterialUBO {
|
layout(std140, binding = 1) uniform MaterialUBO {
|
||||||
vec4 uColor;
|
vec4 uColor;
|
||||||
vec4 uTintColor;
|
vec4 uTintColor;
|
||||||
float uOpacity;
|
float uOpacity;
|
||||||
float uPadding[3]; // std140 对齐填充
|
float uPadding[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 模型矩阵作为单独的统一变量(每个物体设置)
|
|
||||||
uniform mat4 uModelMatrix;
|
uniform mat4 uModelMatrix;
|
||||||
|
|
||||||
// 顶点属性
|
|
||||||
layout(location = 0) in vec2 aPosition;
|
layout(location = 0) in vec2 aPosition;
|
||||||
layout(location = 1) in vec2 aTexCoord;
|
layout(location = 1) in vec2 aTexCoord;
|
||||||
layout(location = 2) in vec4 aColor;
|
layout(location = 2) in vec4 aColor;
|
||||||
|
|
||||||
// 输出到片段着色器
|
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
out vec4 vColor;
|
out vec4 vColor;
|
||||||
out vec4 vTintColor;
|
out vec4 vTintColor;
|
||||||
out float vOpacity;
|
out float vOpacity;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 顶点着色器入口
|
|
||||||
*
|
|
||||||
* 计算顶点在裁剪空间中的位置,
|
|
||||||
* 并传递纹理坐标和颜色到片段着色器
|
|
||||||
*/
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0);
|
gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0);
|
||||||
vTexCoord = aTexCoord;
|
vTexCoord = aTexCoord;
|
||||||
// 混合顶点颜色和材质 UBO 中的颜色
|
|
||||||
vColor = aColor * uColor;
|
vColor = aColor * uColor;
|
||||||
vTintColor = uTintColor;
|
vTintColor = uTintColor;
|
||||||
vOpacity = uOpacity;
|
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/builtin/builtin_asset_factory.h>
|
||||||
|
#include <assets/loaders/shader_loader.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -22,18 +23,9 @@ bool BuiltinAssetFactory::create() {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string vertPath = fileSystem_.assetPath("shader/default.vert");
|
ShaderLoader shaderLoader(fileSystem_);
|
||||||
std::string fragPath = fileSystem_.assetPath("shader/default.frag");
|
Ptr<Shader> shader = shaderLoader.load("shader/default.glsl");
|
||||||
if (!fileSystem_.exists(vertPath) || !fileSystem_.exists(fragPath)) {
|
if (!shader) {
|
||||||
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)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
defaultShader_ = shaders_.insert(shader);
|
defaultShader_ = shaders_.insert(shader);
|
||||||
|
|
@ -92,4 +84,3 @@ Handle<Material> BuiltinAssetFactory::defaultMaterial() const {
|
||||||
Handle<Mesh> BuiltinAssetFactory::defaultQuad() const { return defaultQuad_; }
|
Handle<Mesh> BuiltinAssetFactory::defaultQuad() const { return defaultQuad_; }
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,91 @@
|
||||||
#include <assets/loaders/shader_loader.h>
|
#include <assets/loaders/shader_loader.h>
|
||||||
#include <renderer/shader.h>
|
#include <renderer/shader.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace extra2d {
|
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)
|
ShaderLoader::ShaderLoader(AssetFileSystem fileSystem)
|
||||||
: fileSystem_(std::move(fileSystem)) {}
|
: fileSystem_(std::move(fileSystem)) {}
|
||||||
|
|
||||||
Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
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 basePath = path;
|
||||||
|
|
||||||
std::string::size_type dotPos = basePath.rfind('.');
|
dotPos = basePath.rfind('.');
|
||||||
if (dotPos != std::string::npos) {
|
if (dotPos != std::string::npos) {
|
||||||
basePath = basePath.substr(0, dotPos);
|
basePath = basePath.substr(0, dotPos);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue