mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat: Refactor shader handling by implementing inline shader sources and enhancing error handling
This commit is contained in:
@@ -83,7 +83,7 @@ The file exists but cannot be opened. Check file permissions.
|
||||
|
||||
**Example error message**:
|
||||
```
|
||||
Vertex shader not found: shaders/gui_2d.vert
|
||||
Vertex shader not found: shaders/missing.vert
|
||||
|
||||
Shader key: default
|
||||
|
||||
@@ -160,19 +160,20 @@ Plus a message box with the same error.
|
||||
### Test Case 2: Missing Shader File
|
||||
```bash
|
||||
cd build/Release
|
||||
mv shaders/gui_2d.vert shaders/gui_2d.vert.backup
|
||||
sed -i 's/return {default = variant}/return {default = {vertex = "shaders\/missing.vert", fragment = "shaders\/missing.frag"}}/' ../scripts/gui_demo.lua
|
||||
./sdl3_app --json-file-in ./config/gui_runtime.json
|
||||
```
|
||||
|
||||
**Expected Result**:
|
||||
Message box showing:
|
||||
```
|
||||
Vertex shader not found: shaders/gui_2d.vert
|
||||
Vertex shader not found: shaders/missing.vert
|
||||
|
||||
Shader key: default
|
||||
|
||||
Please ensure shader files are compiled and present in the shaders directory.
|
||||
```
|
||||
Restore `scripts/gui_demo.lua` after the test run.
|
||||
|
||||
### Test Case 3: Normal Operation
|
||||
```bash
|
||||
|
||||
@@ -66,12 +66,26 @@ local function updateFpsModeToggle()
|
||||
end
|
||||
end
|
||||
|
||||
local shader_variants = {
|
||||
default = {
|
||||
vertex = "shaders/gui_2d.vert",
|
||||
fragment = "shaders/gui_2d.frag",
|
||||
},
|
||||
}
|
||||
local function build_shader_variants()
|
||||
local ok, toolkit = pcall(require, "shader_toolkit")
|
||||
if not ok then
|
||||
error("Shader toolkit unavailable: " .. tostring(toolkit))
|
||||
end
|
||||
|
||||
local ok_generate, variant = pcall(toolkit.generate_variant, {
|
||||
key = "default",
|
||||
template = "gui_2d",
|
||||
output_mode = "source",
|
||||
compile = false,
|
||||
})
|
||||
if not ok_generate then
|
||||
error("Shader generation failed: " .. tostring(variant))
|
||||
end
|
||||
|
||||
return {default = variant}
|
||||
end
|
||||
|
||||
local shader_variants = build_shader_variants()
|
||||
|
||||
local function drawTestButtons()
|
||||
-- Background panel
|
||||
@@ -177,9 +191,12 @@ function get_scene_objects()
|
||||
end
|
||||
|
||||
function get_shader_paths()
|
||||
local default_variant = shader_variants.default or {}
|
||||
local vertex_label = default_variant.vertex or "inline"
|
||||
local fragment_label = default_variant.fragment or "inline"
|
||||
log_trace("GUI demo shader variants: default vertex=%s fragment=%s",
|
||||
shader_variants.default.vertex,
|
||||
shader_variants.default.fragment)
|
||||
vertex_label,
|
||||
fragment_label)
|
||||
return shader_variants
|
||||
end
|
||||
|
||||
|
||||
@@ -338,6 +338,258 @@ void main() {
|
||||
}
|
||||
]]
|
||||
|
||||
local gui_2d_vertex_source = [[
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPos;
|
||||
layout(location = 1) in vec4 inColor;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics (ignored by basic shaders)
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pushConstants;
|
||||
|
||||
void main() {
|
||||
fragColor = inColor;
|
||||
vec4 worldPos = pushConstants.model * vec4(inPos, 1.0);
|
||||
gl_Position = pushConstants.viewProj * worldPos;
|
||||
}
|
||||
]]
|
||||
|
||||
local gui_2d_fragment_source = [[
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec4 fragColor;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = fragColor;
|
||||
}
|
||||
]]
|
||||
|
||||
local shadow_vertex_source = [[
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
// layout(location = 2) in vec2 inTexCoord; // Not used
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
gl_Position = pc.lightViewProj * pc.model * vec4(inPosition, 1.0);
|
||||
}
|
||||
]]
|
||||
|
||||
local shadow_fragment_source = [[
|
||||
#version 450
|
||||
|
||||
void main() {
|
||||
// Empty fragment shader for shadow mapping
|
||||
// Depth is automatically written
|
||||
}
|
||||
]]
|
||||
|
||||
local fullscreen_vertex_source = [[
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec2 fragPosition;
|
||||
layout(location = 1) out vec2 fragTexCoord;
|
||||
|
||||
void main() {
|
||||
fragPosition = inPosition;
|
||||
fragTexCoord = inTexCoord;
|
||||
gl_Position = vec4(inPosition, 0.0, 1.0);
|
||||
}
|
||||
]]
|
||||
|
||||
local ssgi_fragment_source = [[
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D sceneColor;
|
||||
layout(set = 0, binding = 1) uniform sampler2D normalBuffer;
|
||||
layout(set = 0, binding = 2) uniform sampler2D depthBuffer;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pc;
|
||||
|
||||
const int NUM_SAMPLES = 16;
|
||||
const float SAMPLE_RADIUS = 0.5;
|
||||
|
||||
// Reconstruct world position from depth
|
||||
vec3 worldPosFromDepth(float depth, vec2 texCoord) {
|
||||
vec4 clipSpace = vec4(texCoord * 2.0 - 1.0, depth, 1.0);
|
||||
vec4 viewSpace = pc.proj * clipSpace; // Use proj as invProj for now (dummy)
|
||||
viewSpace /= viewSpace.w;
|
||||
vec4 worldSpace = pc.view * viewSpace; // Use view as invView for now (dummy)
|
||||
return worldSpace.xyz;
|
||||
}
|
||||
|
||||
vec3 ssao(vec2 texCoord) {
|
||||
float depth = texture(depthBuffer, texCoord).r;
|
||||
if (depth >= 1.0) return vec3(1.0); // Skybox
|
||||
|
||||
vec3 worldPos = worldPosFromDepth(depth, texCoord);
|
||||
vec3 normal = normalize(texture(normalBuffer, texCoord).rgb * 2.0 - 1.0);
|
||||
|
||||
float occlusion = 0.0;
|
||||
|
||||
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
// Generate sample position in hemisphere around normal
|
||||
float angle = (float(i) / float(NUM_SAMPLES)) * 6.283185;
|
||||
vec2 offset = vec2(cos(angle), sin(angle)) * SAMPLE_RADIUS;
|
||||
|
||||
vec2 sampleTexCoord = texCoord + offset / textureSize(depthBuffer, 0);
|
||||
float sampleDepth = texture(depthBuffer, sampleTexCoord).r;
|
||||
|
||||
if (sampleDepth < depth - 0.01) { // Occluded
|
||||
occlusion += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
occlusion = 1.0 - (occlusion / float(NUM_SAMPLES));
|
||||
return vec3(occlusion);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 sceneColor = texture(sceneColor, inTexCoord).rgb;
|
||||
vec3 ao = ssao(inTexCoord);
|
||||
|
||||
// Apply ambient occlusion
|
||||
vec3 finalColor = sceneColor * (0.3 + 0.7 * ao); // Mix AO with direct lighting
|
||||
|
||||
outColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
]]
|
||||
|
||||
local volumetric_fragment_source = [[
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D sceneColor;
|
||||
layout(set = 0, binding = 1) uniform sampler2D depthBuffer;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pc;
|
||||
|
||||
const int NUM_SAMPLES = 100;
|
||||
const float DECAY_BASE = 0.96815;
|
||||
const float WEIGHT_BASE = 0.58767;
|
||||
const float EXPOSURE = 0.2;
|
||||
|
||||
void main() {
|
||||
vec2 texCoord = inTexCoord;
|
||||
vec2 lightScreenPos = vec2(0.5, 0.5); // Dummy light position
|
||||
vec2 deltaTexCoord = (texCoord - lightScreenPos);
|
||||
deltaTexCoord *= 1.0 / float(NUM_SAMPLES) * 0.5; // Scale for effect
|
||||
|
||||
vec3 color = texture(sceneColor, texCoord).rgb;
|
||||
|
||||
// Only apply god rays if we're looking towards the light
|
||||
float centerDistance = length(lightScreenPos - vec2(0.5, 0.5));
|
||||
if (centerDistance < 0.8) { // Light is visible on screen
|
||||
vec3 godRayColor = vec3(0.0);
|
||||
float weight = WEIGHT_BASE;
|
||||
|
||||
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
texCoord -= deltaTexCoord;
|
||||
vec3 sampleColor = texture(sceneColor, texCoord).rgb;
|
||||
godRayColor += sampleColor * weight;
|
||||
weight *= DECAY_BASE;
|
||||
}
|
||||
|
||||
color += godRayColor * EXPOSURE * 1.0; // Dummy intensity
|
||||
}
|
||||
|
||||
outColor = vec4(color, 1.0);
|
||||
}
|
||||
]]
|
||||
|
||||
local vertex_world_color_source = [[
|
||||
#version 450
|
||||
|
||||
@@ -996,6 +1248,13 @@ local function build_vertex_color_sources()
|
||||
}
|
||||
end
|
||||
|
||||
local function build_gui_2d_sources()
|
||||
return {
|
||||
vertex = gui_2d_vertex_source,
|
||||
fragment = gui_2d_fragment_source,
|
||||
}
|
||||
end
|
||||
|
||||
local function build_solid_color_sources(options)
|
||||
local color = normalize_color(options.color)
|
||||
local fragment = string.format([[
|
||||
@@ -1013,6 +1272,13 @@ void main() {
|
||||
}
|
||||
end
|
||||
|
||||
local function build_shadow_sources()
|
||||
return {
|
||||
vertex = shadow_vertex_source,
|
||||
fragment = shadow_fragment_source,
|
||||
}
|
||||
end
|
||||
|
||||
local function build_cube_rainbow_sources(options)
|
||||
return {
|
||||
vertex = vertex_world_color_source,
|
||||
@@ -1055,6 +1321,20 @@ local function build_pbr_sources(options)
|
||||
}
|
||||
end
|
||||
|
||||
local function build_ssgi_sources()
|
||||
return {
|
||||
vertex = fullscreen_vertex_source,
|
||||
fragment = ssgi_fragment_source,
|
||||
}
|
||||
end
|
||||
|
||||
local function build_volumetric_sources()
|
||||
return {
|
||||
vertex = fullscreen_vertex_source,
|
||||
fragment = volumetric_fragment_source,
|
||||
}
|
||||
end
|
||||
|
||||
local templates = {}
|
||||
|
||||
local function register_template_object(template)
|
||||
@@ -1068,9 +1348,11 @@ local function register_template_object(template)
|
||||
end
|
||||
|
||||
register_template_object(ShaderTemplate:new("vertex_color", build_vertex_color_sources))
|
||||
register_template_object(ShaderTemplate:new("gui_2d", build_gui_2d_sources))
|
||||
register_template_object(ShaderTemplate:new("solid_color", build_solid_color_sources, {
|
||||
color = {1.0, 1.0, 1.0, 1.0},
|
||||
}))
|
||||
register_template_object(ShaderTemplate:new("shadow", build_shadow_sources))
|
||||
register_template_object(ShaderTemplate:new("cube_rainbow", build_cube_rainbow_sources, {
|
||||
band_scale = 0.35,
|
||||
diagonal_scale = 0.25,
|
||||
@@ -1114,6 +1396,8 @@ register_template_object(ShaderTemplate:new("pbr", build_pbr_sources, {
|
||||
light_color = {1.0, 0.9, 0.6},
|
||||
light_intensity = 1.2,
|
||||
}))
|
||||
register_template_object(ShaderTemplate:new("ssgi", build_ssgi_sources))
|
||||
register_template_object(ShaderTemplate:new("volumetric", build_volumetric_sources))
|
||||
|
||||
shader_toolkit.templates = templates
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec4 fragColor;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = fragColor;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPos;
|
||||
layout(location = 1) in vec4 inColor;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics (ignored by basic shaders)
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pushConstants;
|
||||
|
||||
void main() {
|
||||
fragColor = inColor;
|
||||
vec4 worldPos = pushConstants.model * vec4(inPos, 1.0);
|
||||
gl_Position = pushConstants.viewProj * worldPos;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#version 450
|
||||
|
||||
void main() {
|
||||
// Empty fragment shader for shadow mapping
|
||||
// Depth is automatically written
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
// layout(location = 2) in vec2 inTexCoord; // Not used
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
gl_Position = pc.lightViewProj * pc.model * vec4(inPosition, 1.0);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D sceneColor;
|
||||
layout(set = 0, binding = 1) uniform sampler2D normalBuffer;
|
||||
layout(set = 0, binding = 2) uniform sampler2D depthBuffer;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pc;
|
||||
|
||||
const int NUM_SAMPLES = 16;
|
||||
const float SAMPLE_RADIUS = 0.5;
|
||||
|
||||
// Reconstruct world position from depth
|
||||
vec3 worldPosFromDepth(float depth, vec2 texCoord) {
|
||||
vec4 clipSpace = vec4(texCoord * 2.0 - 1.0, depth, 1.0);
|
||||
vec4 viewSpace = pc.proj * clipSpace; // Use proj as invProj for now (dummy)
|
||||
viewSpace /= viewSpace.w;
|
||||
vec4 worldSpace = pc.view * viewSpace; // Use view as invView for now (dummy)
|
||||
return worldSpace.xyz;
|
||||
}
|
||||
|
||||
vec3 ssao(vec2 texCoord) {
|
||||
float depth = texture(depthBuffer, texCoord).r;
|
||||
if (depth >= 1.0) return vec3(1.0); // Skybox
|
||||
|
||||
vec3 worldPos = worldPosFromDepth(depth, texCoord);
|
||||
vec3 normal = normalize(texture(normalBuffer, texCoord).rgb * 2.0 - 1.0);
|
||||
|
||||
float occlusion = 0.0;
|
||||
|
||||
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
// Generate sample position in hemisphere around normal
|
||||
float angle = (float(i) / float(NUM_SAMPLES)) * 6.283185;
|
||||
vec2 offset = vec2(cos(angle), sin(angle)) * SAMPLE_RADIUS;
|
||||
|
||||
vec2 sampleTexCoord = texCoord + offset / textureSize(depthBuffer, 0);
|
||||
float sampleDepth = texture(depthBuffer, sampleTexCoord).r;
|
||||
|
||||
if (sampleDepth < depth - 0.01) { // Occluded
|
||||
occlusion += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
occlusion = 1.0 - (occlusion / float(NUM_SAMPLES));
|
||||
return vec3(occlusion);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 sceneColor = texture(sceneColor, inTexCoord).rgb;
|
||||
vec3 ao = ssao(inTexCoord);
|
||||
|
||||
// Apply ambient occlusion
|
||||
vec3 finalColor = sceneColor * (0.3 + 0.7 * ao); // Mix AO with direct lighting
|
||||
|
||||
outColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D sceneColor;
|
||||
layout(set = 0, binding = 1) uniform sampler2D depthBuffer;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pc;
|
||||
|
||||
const int NUM_SAMPLES = 100;
|
||||
const float DECAY_BASE = 0.96815;
|
||||
const float WEIGHT_BASE = 0.58767;
|
||||
const float EXPOSURE = 0.2;
|
||||
|
||||
void main() {
|
||||
vec2 texCoord = inTexCoord;
|
||||
vec2 lightScreenPos = vec2(0.5, 0.5); // Dummy light position
|
||||
vec2 deltaTexCoord = (texCoord - lightScreenPos);
|
||||
deltaTexCoord *= 1.0 / float(NUM_SAMPLES) * 0.5; // Scale for effect
|
||||
|
||||
vec3 color = texture(sceneColor, texCoord).rgb;
|
||||
|
||||
// Only apply god rays if we're looking towards the light
|
||||
float centerDistance = length(lightScreenPos - vec2(0.5, 0.5));
|
||||
if (centerDistance < 0.8) { // Light is visible on screen
|
||||
vec3 godRayColor = vec3(0.0);
|
||||
float weight = WEIGHT_BASE;
|
||||
|
||||
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
texCoord -= deltaTexCoord;
|
||||
vec3 sampleColor = texture(sceneColor, texCoord).rgb;
|
||||
godRayColor += sampleColor * weight;
|
||||
weight *= DECAY_BASE;
|
||||
}
|
||||
|
||||
color += godRayColor * EXPOSURE * 1.0; // Dummy intensity
|
||||
}
|
||||
|
||||
outColor = vec4(color, 1.0);
|
||||
}
|
||||
@@ -316,6 +316,93 @@ std::vector<uint8_t> ReadShaderFile(const std::filesystem::path& path,
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ReadShaderSource(const std::string& source,
|
||||
VkShaderStageFlagBits stage,
|
||||
const std::string& label,
|
||||
ILogger* logger) {
|
||||
if (logger) {
|
||||
logger->Trace("GuiRenderer", "ReadShaderSource",
|
||||
"label=" + label + ", stage=" +
|
||||
std::to_string(static_cast<int>(stage)));
|
||||
}
|
||||
|
||||
if (source.empty()) {
|
||||
throw std::runtime_error("Shader source is empty");
|
||||
}
|
||||
|
||||
shaderc::Compiler compiler;
|
||||
shaderc::CompileOptions options;
|
||||
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
|
||||
|
||||
shaderc_shader_kind kind = ShadercKindFromStage(stage);
|
||||
auto result = compiler.CompileGlslToSpv(source, kind, label.c_str(), options);
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||
std::string error = result.GetErrorMessage();
|
||||
if (logger) {
|
||||
logger->Error("GuiRenderer shader compilation failed: " + label + "\n" + error);
|
||||
}
|
||||
throw std::runtime_error("Shader compilation failed: " + label + "\n" + error);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> spirv(result.cbegin(), result.cend());
|
||||
std::vector<uint8_t> buffer(spirv.size() * sizeof(uint32_t));
|
||||
if (!buffer.empty()) {
|
||||
std::memcpy(buffer.data(), spirv.data(), buffer.size());
|
||||
}
|
||||
if (logger) {
|
||||
logger->Trace("GuiRenderer", "ReadShaderSource",
|
||||
"compiledBytes=" + std::to_string(buffer.size()));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char* kGuiVertexSource = R"(
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPos;
|
||||
layout(location = 1) in vec4 inColor;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
// Extended fields for PBR/atmospherics (ignored by basic shaders)
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
// Atmospherics parameters
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pushConstants;
|
||||
|
||||
void main() {
|
||||
fragColor = inColor;
|
||||
vec4 worldPos = pushConstants.model * vec4(inPos, 1.0);
|
||||
gl_Position = pushConstants.viewProj * worldPos;
|
||||
}
|
||||
)";
|
||||
|
||||
const char* kGuiFragmentSource = R"(
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec4 fragColor;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = fragColor;
|
||||
}
|
||||
)";
|
||||
|
||||
} // namespace
|
||||
|
||||
GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFormat swapchainFormat,
|
||||
@@ -663,22 +750,45 @@ const std::vector<uint8_t>& GuiRenderer::LoadShaderBytes(const std::filesystem::
|
||||
return inserted.first->second;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& GuiRenderer::LoadShaderBytes(const std::string& cacheKey,
|
||||
const std::string& source,
|
||||
VkShaderStageFlagBits stage) {
|
||||
auto cached = shaderSpirvCache_.find(cacheKey);
|
||||
if (cached != shaderSpirvCache_.end()) {
|
||||
if (logger_) {
|
||||
logger_->Trace("GuiRenderer", "LoadShaderBytes",
|
||||
"cacheHit=true, key=" + cacheKey +
|
||||
", bytes=" + std::to_string(cached->second.size()));
|
||||
}
|
||||
return cached->second;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> shaderBytes = ReadShaderSource(source, stage, cacheKey, logger_.get());
|
||||
auto inserted = shaderSpirvCache_.emplace(cacheKey, std::move(shaderBytes));
|
||||
if (logger_) {
|
||||
logger_->Trace("GuiRenderer", "LoadShaderBytes",
|
||||
"cacheHit=false, key=" + cacheKey +
|
||||
", bytes=" + std::to_string(inserted.first->second.size()));
|
||||
}
|
||||
return inserted.first->second;
|
||||
}
|
||||
|
||||
void GuiRenderer::CreatePipeline(VkRenderPass renderPass, VkExtent2D extent) {
|
||||
// Load shader modules
|
||||
const std::filesystem::path vertexShaderPath =
|
||||
scriptDirectory_.parent_path() / "shaders" / "gui_2d.vert";
|
||||
const std::filesystem::path fragmentShaderPath =
|
||||
scriptDirectory_.parent_path() / "shaders" / "gui_2d.frag";
|
||||
const std::string vertexLabel = "inline:gui_2d.vert";
|
||||
const std::string fragmentLabel = "inline:gui_2d.frag";
|
||||
if (logger_) {
|
||||
logger_->Trace("GuiRenderer", "CreatePipeline",
|
||||
"renderPassIsNull=" + std::string(renderPass == VK_NULL_HANDLE ? "true" : "false") +
|
||||
", extent=" + std::to_string(extent.width) + "x" + std::to_string(extent.height) +
|
||||
", vertexShader=" + vertexShaderPath.string() +
|
||||
", fragmentShader=" + fragmentShaderPath.string());
|
||||
", vertexShader=" + vertexLabel +
|
||||
", fragmentShader=" + fragmentLabel);
|
||||
}
|
||||
|
||||
const auto& vertShaderCode = LoadShaderBytes(vertexShaderPath, VK_SHADER_STAGE_VERTEX_BIT);
|
||||
const auto& fragShaderCode = LoadShaderBytes(fragmentShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
const auto& vertShaderCode = LoadShaderBytes(vertexLabel, kGuiVertexSource,
|
||||
VK_SHADER_STAGE_VERTEX_BIT);
|
||||
const auto& fragShaderCode = LoadShaderBytes(fragmentLabel, kGuiFragmentSource,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
VkShaderModuleCreateInfo vertModuleInfo{};
|
||||
vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
|
||||
@@ -59,6 +59,9 @@ private:
|
||||
void GenerateGuiGeometry(const std::vector<GuiCommand>& commands, uint32_t width, uint32_t height);
|
||||
const std::vector<uint8_t>& LoadShaderBytes(const std::filesystem::path& path,
|
||||
VkShaderStageFlagBits stage);
|
||||
const std::vector<uint8_t>& LoadShaderBytes(const std::string& cacheKey,
|
||||
const std::string& source,
|
||||
VkShaderStageFlagBits stage);
|
||||
|
||||
VkDevice device_;
|
||||
VkPhysicalDevice physicalDevice_;
|
||||
|
||||
@@ -46,8 +46,45 @@ int main() {
|
||||
|
||||
// Test pipeline creation
|
||||
sdl3cpp::services::ShaderPaths shaderPaths;
|
||||
shaderPaths.vertex = "shaders/gui_2d.vert";
|
||||
shaderPaths.fragment = "shaders/gui_2d.frag";
|
||||
shaderPaths.vertex = "inline:gui_2d.vert";
|
||||
shaderPaths.fragment = "inline:gui_2d.frag";
|
||||
shaderPaths.vertexSource = R"(
|
||||
#version 450
|
||||
layout(location = 0) in vec3 inPos;
|
||||
layout(location = 1) in vec4 inColor;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 viewProj;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
float ambientStrength;
|
||||
float fogDensity;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
vec3 fogColor;
|
||||
float gamma;
|
||||
float exposure;
|
||||
int enableShadows;
|
||||
int enableFog;
|
||||
} pushConstants;
|
||||
void main() {
|
||||
fragColor = inColor;
|
||||
vec4 worldPos = pushConstants.model * vec4(inPos, 1.0);
|
||||
gl_Position = pushConstants.viewProj * worldPos;
|
||||
}
|
||||
)";
|
||||
shaderPaths.fragmentSource = R"(
|
||||
#version 450
|
||||
layout(location = 0) in vec4 fragColor;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
void main() {
|
||||
outColor = fragColor;
|
||||
}
|
||||
)";
|
||||
auto pipeline = backend->CreatePipeline(device, "test_shader", shaderPaths);
|
||||
Assert(pipeline != nullptr, "pipeline creation failed", failures);
|
||||
std::cout << "GXM pipeline created successfully\n";
|
||||
|
||||
Reference in New Issue
Block a user