diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24e23a1..cafc3d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,6 +184,7 @@ jobs: uses: actions/checkout@v7 with: path: SleakCraft + fetch-depth: 0 - name: Check out the engine uses: actions/checkout@v7 @@ -192,8 +193,10 @@ jobs: ref: ${{ needs.engine-ref.outputs.ref }} path: SleakCraft/Engine - - name: Install glslc - run: sudo apt-get update && sudo apt-get install -y glslc + - name: Install shader tools + run: | + sudo apt-get update + sudo apt-get install -y glslc glslang-tools # Byte comparison against the committed .spv is not usable here: glslc # embeds its own generator version, so a different toolchain produces @@ -206,7 +209,8 @@ jobs: for src in *.vert *.frag; do [ -e "$src" ] || continue case "$src" in *_gl.vert|*_gl.frag) continue ;; esac - if ! glslc "$src" -o /tmp/out.spv; then + if ! glslc -I ../../../Engine/assets/shaders/include -I include \ + "$src" -o /tmp/out.spv; then echo "FAILED to compile: $src" fail=1 fi @@ -228,6 +232,71 @@ jobs: [ $missing -eq 0 ] && echo "All voxel shaders have committed SPIR-V." exit $missing + # Committed .spv are compiled against the engine the game pins, so a + # change to a game shader, a game include, or an engine include between + # the base and head pins needs fresh SPIR-V. + - name: Edited GLSL and engine includes come with SPIR-V + working-directory: SleakCraft + env: + BASE: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || github.event.before }} + run: | + if [ ! -f Engine/scripts/check_spirv_fresh.py ]; then + echo "::warning::The engine has no scripts/check_spirv_fresh.py." + exit 0 + fi + if [ "${{ github.event_name }}" = "pull_request" ]; then + git fetch --no-tags origin "${{ github.base_ref }}" + fi + if ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then + echo "No base commit to compare against." + exit 0 + fi + BASE=$(git merge-base "$BASE" HEAD) + old_pin=$(git rev-parse "$BASE:Engine") + new_pin=$(git rev-parse "HEAD:Engine") + for tree in base head; do + mkdir -p "/tmp/$tree/Engine/assets/shaders" + cp -r Engine/assets/shaders/include "/tmp/$tree/Engine/assets/shaders/" 2>/dev/null || true + done + git archive "$BASE" Game/assets/shaders | tar -x -C /tmp/base + git archive HEAD Game/assets/shaders | tar -x -C /tmp/head + git diff --name-only "$BASE" HEAD -- Game/assets/shaders > /tmp/changed.txt + if git -C Engine fetch --no-tags --depth=1 origin "$old_pin" "$new_pin"; then + for pair in "base:$old_pin" "head:$new_pin"; do + tree=${pair%%:*} + pin=${pair#*:} + rm -rf "/tmp/$tree/Engine/assets/shaders/include" + if git -C Engine cat-file -e "$pin:assets/shaders/include" 2>/dev/null; then + git -C Engine archive "$pin" assets/shaders/include | tar -x -C "/tmp/$tree/Engine" + fi + done + git -C Engine diff --name-only "$old_pin" "$new_pin" -- assets/shaders/include \ + | sed 's|^|Engine/|' >> /tmp/changed.txt + else + echo "::warning::Engine pins are not on the remote, engine include changes are not checked." + fi + python3 Engine/scripts/check_spirv_fresh.py --base /tmp/base --head /tmp/head \ + --shaders Game/assets/shaders \ + --include Engine/assets/shaders/include \ + --include Game/assets/shaders/include \ + --changed /tmp/changed.txt + + - name: Every OpenGL shader validates + working-directory: SleakCraft/Game/assets/shaders + run: | + fail=0 + for src in *_gl.vert *_gl.frag; do + [ -e "$src" ] || continue + out="/tmp/gl_expanded.${src##*.}" + if ! glslangValidator -E -I../../../Engine/assets/shaders/include \ + -Iinclude "$src" > "$out" || + ! glslangValidator "$out"; then + echo "FAILED to validate: $src" + fail=1 + fi + done + exit $fail + # Compiling proves nothing about whether the game runs. Runners have no GPU, # but Mesa's software Vulkan (lavapipe) plus a virtual display is enough to # boot the real binary into a real world and catch startup crashes, missing @@ -323,6 +392,10 @@ jobs: echo "A crash signature appeared in the output." exit 1 fi + if grep -qE "Shader compilation failed|Shader program linking failed|Cannot read shader" smoke.log; then + echo "An OpenGL shader failed to build." + exit 1 + fi echo "Booted into a world and survived the run window." - name: Upload the smoke log diff --git a/Client/CMakeLists.txt b/Client/CMakeLists.txt index 1d9ac50..179816e 100644 --- a/Client/CMakeLists.txt +++ b/Client/CMakeLists.txt @@ -22,12 +22,17 @@ set(_asset_out_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets") set(_sleak_seen_outputs "") +# Optional third argument: subfolder of bin/assets to copy into function(sleak_track_asset_dir SRC_DIR LABEL) + set(_dst_root "${_asset_out_dir}") + if(ARGC GREATER 2) + set(_dst_root "${_asset_out_dir}/${ARGV2}") + endif() file(GLOB_RECURSE _files CONFIGURE_DEPENDS "${SRC_DIR}/*") set(_outputs "") foreach(_src ${_files}) file(RELATIVE_PATH _rel "${SRC_DIR}" "${_src}") - set(_dst "${_asset_out_dir}/${_rel}") + set(_dst "${_dst_root}/${_rel}") list(FIND _sleak_seen_outputs "${_dst}" _seen_idx) if(NOT _seen_idx EQUAL -1) continue() @@ -47,10 +52,29 @@ function(sleak_track_asset_dir SRC_DIR LABEL) endfunction() set(SLEAK_ASSET_OUTPUTS "") +# Built game SPIR-V wins over the committed copies +get_property(_game_spirv GLOBAL PROPERTY GAME_SPIRV_FILES) +foreach(_spv ${_game_spirv}) + get_filename_component(_name "${_spv}" NAME) + set(_dst "${_asset_out_dir}/shaders/${_name}") + add_custom_command( + OUTPUT "${_dst}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_spv}" "${_dst}" + DEPENDS "${_spv}" + COMMENT "Copy game SPIR-V: ${_name}" + VERBATIM + ) + list(APPEND SLEAK_ASSET_OUTPUTS "${_dst}") + list(APPEND _sleak_seen_outputs "${_dst}") +endforeach() +# Include libraries resolve engine first, as glslc -I does +sleak_track_asset_dir("${CMAKE_SOURCE_DIR}/Engine/assets/shaders/include" + "engine" "shaders/include") sleak_track_asset_dir("${CMAKE_SOURCE_DIR}/Game/assets" "game") sleak_track_asset_dir("${CMAKE_SOURCE_DIR}/Engine/assets" "engine") add_custom_target(CopyAssets ALL DEPENDS ${SLEAK_ASSET_OUTPUTS}) +add_dependencies(CopyAssets GameShaders) add_dependencies(SleakCraft CopyAssets) # --- Post-build: copy runtime DLLs (Windows) --- diff --git a/Client/src/main.cpp b/Client/src/main.cpp index 4ec10c3..29f2f37 100644 --- a/Client/src/main.cpp +++ b/Client/src/main.cpp @@ -23,9 +23,11 @@ static void PrintHelp(const char* exe) { << " -t Window title (use _ for spaces)\n" << " --fullscreen Start in fullscreen\n" << "\nWorld\n" - << " -world Load world if save exists, create new otherwise\n" + << " -world Load world if save exists, create new " + "otherwise\n" << " -seed Seed for new world (default: random)\n" - << " -rd Initial render distance in chunks (default: 8)\n" + << " -rd Initial render distance in chunks (default: " + "8)\n" << "\nGraphics\n" << " -msaa MSAA sample count: 1, 2, 4, 8\n" << " --vsync Enable VSync on launch\n" @@ -37,7 +39,8 @@ static void PrintHelp(const char* exe) { << " -campose x,y,z,yaw,pitch\n" << " Pin the camera, degrees (Debug or --bench)\n" << " --gpuprofile Log per-pass GPU timings (Vulkan)\n" - << " -capture Write frame N as a PPM, then exit (Vulkan)\n" + << " -capture Write frame N as a PPM, then exit (Vulkan, " + "OpenGL)\n" << " -capture-frame Frame index for -capture (default: 600)\n" << "\nDebug\n" << " --validate Enable Vulkan validation (Debug: always on)\n" diff --git a/Engine b/Engine index d319e9e..a393409 160000 --- a/Engine +++ b/Engine @@ -1 +1 @@ -Subproject commit d319e9e2dba42e0753efa4bc29625a27a18a6e57 +Subproject commit a393409321b5138653632927c1656297f6899f07 diff --git a/Game/CMakeLists.txt b/Game/CMakeLists.txt index f547fbb..a232e28 100644 --- a/Game/CMakeLists.txt +++ b/Game/CMakeLists.txt @@ -14,3 +14,58 @@ target_include_directories(SleakGame PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(SleakGame PRIVATE Engine) + +# --- SPIR-V shaders (committed .spv are only the fallback without glslc) --- +set(GAME_SHADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders") +set(GAME_SHADER_INCLUDE_DIR "${GAME_SHADER_DIR}/include") +# sleak/ is the engine's include namespace +if(EXISTS "${GAME_SHADER_INCLUDE_DIR}/sleak") + message(FATAL_ERROR + "${GAME_SHADER_INCLUDE_DIR}/sleak would shadow the engine include " + "library. Put game includes in include/craft/ instead.") +endif() +file(GLOB GAME_VULKAN_SHADERS CONFIGURE_DEPENDS + "${GAME_SHADER_DIR}/*.vert" + "${GAME_SHADER_DIR}/*.frag" +) +list(FILTER GAME_VULKAN_SHADERS EXCLUDE REGEX "_gl\\.(vert|frag)$") +file(GLOB_RECURSE GAME_SHADER_INCLUDES CONFIGURE_DEPENDS + "${GAME_SHADER_INCLUDE_DIR}/*.glsl") +# Engine include edits must rebuild game SPIR-V too +get_property(_engine_include_dir GLOBAL PROPERTY ENGINE_SHADER_INCLUDE_DIR) +get_property(_engine_includes GLOBAL PROPERTY ENGINE_SHADER_INCLUDES) +if(NOT _engine_include_dir) + set(_engine_include_dir "${CMAKE_SOURCE_DIR}/Engine/assets/shaders/include") +endif() + +find_program(GLSLC glslc + HINTS "$ENV{VULKAN_SDK}/Bin" "$ENV{VULKAN_SDK}/bin" "${VULKAN_SDK}/Bin") +set(GAME_SPIRV_FILES "") +if(GLSLC) + set(_spirv_dir "${CMAKE_CURRENT_BINARY_DIR}/shaders") + file(MAKE_DIRECTORY "${_spirv_dir}") + foreach(_src ${GAME_VULKAN_SHADERS}) + get_filename_component(_name "${_src}" NAME) + set(_spv "${_spirv_dir}/${_name}.spv") + add_custom_command( + OUTPUT "${_spv}" + COMMAND ${GLSLC} -I "${_engine_include_dir}" + -I "${GAME_SHADER_INCLUDE_DIR}" "${_src}" -o "${_spv}" + DEPENDS "${_src}" ${_engine_includes} ${GAME_SHADER_INCLUDES} + COMMENT "Compiling ${_name} to SPIR-V" + VERBATIM + ) + list(APPEND GAME_SPIRV_FILES "${_spv}") + endforeach() +else() + foreach(_src ${GAME_VULKAN_SHADERS}) + if(NOT EXISTS "${_src}.spv") + message(FATAL_ERROR + "glslc not found and ${_src} has no committed SPIR-V. " + "Install the Vulkan SDK or set VULKAN_SDK.") + endif() + endforeach() +endif() +add_custom_target(GameShaders ALL DEPENDS ${GAME_SPIRV_FILES}) +# Client stages these over the committed copies +set_property(GLOBAL PROPERTY GAME_SPIRV_FILES ${GAME_SPIRV_FILES}) diff --git a/Game/assets/shaders/flat_shader.frag b/Game/assets/shaders/flat_shader.frag index 0e892f3..946920f 100644 --- a/Game/assets/shaders/flat_shader.frag +++ b/Game/assets/shaders/flat_shader.frag @@ -1,4 +1,5 @@ #version 450 +#extension GL_GOOGLE_include_directive : require layout(location = 0) in vec3 fragWorldPos; layout(location = 1) in vec3 fragWorldNorm; @@ -10,21 +11,8 @@ layout(location = 0) out vec4 outColor; layout(set = 0, binding = 0) uniform sampler2D diffuseTexture; -layout(set = 2, binding = 0) uniform ShadowLightUBO { - vec4 uLightDir; - vec4 uLightColor; - vec4 uAmbient; // rgb = sky ambient color, a = intensity - vec4 uCameraPos; - mat4 uLightVP; - float uShadowBias; - float uShadowStrength; - float uShadowTexelSize; - float uLightSize; - vec4 uFogColor; - float uFogStart; - float uFogEnd; - vec2 _fogPad; // std140: must be vec2 (8B) not float[2] (stride-16 = 32B) -}; +#include "sleak/shadow_light.glsl" +#include "sleak/frame.glsl" layout(set = 3, binding = 0) uniform sampler2DShadow shadowMap; @@ -120,5 +108,5 @@ void main() { lit = mix(uFogColor.rgb, lit, fogFactor); } - outColor = vec4(lit, texColor.a); + outColor = SleakFrameGuard(vec4(lit, texColor.a)); } diff --git a/Game/assets/shaders/flat_shader.frag.spv b/Game/assets/shaders/flat_shader.frag.spv index 80e3efd..3bc97ba 100644 Binary files a/Game/assets/shaders/flat_shader.frag.spv and b/Game/assets/shaders/flat_shader.frag.spv differ diff --git a/Game/assets/shaders/flat_shader.vert b/Game/assets/shaders/flat_shader.vert index a05e82c..7becd59 100644 --- a/Game/assets/shaders/flat_shader.vert +++ b/Game/assets/shaders/flat_shader.vert @@ -1,4 +1,5 @@ #version 450 +#extension GL_GOOGLE_include_directive : require layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; @@ -10,21 +11,7 @@ layout(push_constant) uniform TransformPC { mat4 World; }; -layout(set = 2, binding = 0) uniform ShadowLightUBO { - vec4 uLightDir; // xyz = direction, w = pad - vec4 uLightColor; // rgb = color, a = intensity - vec4 uAmbient; // rgb = color, a = intensity - vec4 uCameraPos; // xyz = camera world pos, w = pad - mat4 uLightVP; // light view-projection matrix - float uShadowBias; - float uShadowStrength; - float uShadowTexelSize; - float uLightSize; - vec4 uFogColor; // rgb + alpha - float uFogStart; // distance where fog begins - float uFogEnd; // distance where fog fully obscures - vec2 _fogPad; // std140: must be vec2 (8B) not float[2] (stride-16 = 32B) -}; +#include "sleak/shadow_light.glsl" layout(location = 0) out vec3 fragWorldPos; layout(location = 1) out vec3 fragWorldNorm; diff --git a/Game/assets/shaders/flat_shader.vert.spv b/Game/assets/shaders/flat_shader.vert.spv index 2e7ab08..8afedd6 100644 Binary files a/Game/assets/shaders/flat_shader.vert.spv and b/Game/assets/shaders/flat_shader.vert.spv differ diff --git a/Game/assets/shaders/flat_shader_gl.frag b/Game/assets/shaders/flat_shader_gl.frag index 7d9cd8b..60e7a45 100644 --- a/Game/assets/shaders/flat_shader_gl.frag +++ b/Game/assets/shaders/flat_shader_gl.frag @@ -1,4 +1,5 @@ #version 450 core +#extension GL_GOOGLE_include_directive : require in vec3 fragWorldPos; in vec3 fragWorldNorm; @@ -39,6 +40,8 @@ layout(std140, binding = 5) uniform ShadowUBO { float _shadowPad0, _shadowPad1; }; +#include "sleak/frame.glsl" + layout(binding = 0) uniform sampler2D diffuseTexture; layout(binding = 3) uniform sampler2DShadow shadowMap; @@ -142,5 +145,5 @@ void main() { lit = mix(FogColor.rgb, lit, fogFactor); } - outColor = vec4(lit, texColor.a); + outColor = SleakFrameGuard(vec4(lit, texColor.a)); } diff --git a/Game/assets/shaders/flat_shader_gl.vert b/Game/assets/shaders/flat_shader_gl.vert index 0111c70..bdcb5d2 100644 --- a/Game/assets/shaders/flat_shader_gl.vert +++ b/Game/assets/shaders/flat_shader_gl.vert @@ -1,4 +1,5 @@ #version 450 core +#extension GL_GOOGLE_include_directive : require layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; @@ -10,16 +11,8 @@ layout(std140, binding = 0) uniform TransformUBO { mat4 World; }; -layout(std140, binding = 5) uniform ShadowUBO { - mat4 ShadowLightVP; - float ShadowBias; - float ShadowStrength; - float ShadowTexelSize; - float ShadowLightSize; - uint PCSSEnabled; - uint ShadowMapEnabled; - float _shadowPad0, _shadowPad1; -}; +#include "sleak/shadow_light.glsl" +#include "sleak/frame.glsl" out vec3 fragWorldPos; out vec3 fragWorldNorm; @@ -28,7 +21,7 @@ out vec2 fragUV; out vec4 fragShadowCoord; void main() { - gl_Position = WVP * vec4(inPosition, 1.0); + gl_Position = SleakFrameGuardClip(WVP * vec4(inPosition, 1.0)); vec4 worldPos = World * vec4(inPosition, 1.0); fragWorldPos = worldPos.xyz; @@ -39,5 +32,5 @@ void main() { fragColor = inColor; fragUV = inUV; - fragShadowCoord = ShadowLightVP * worldPos; + fragShadowCoord = uLightVP * worldPos; } diff --git a/Game/assets/shaders/gbuffer_gl.vert b/Game/assets/shaders/gbuffer_gl.vert index bd9f5dc..085e5a7 100644 --- a/Game/assets/shaders/gbuffer_gl.vert +++ b/Game/assets/shaders/gbuffer_gl.vert @@ -1,4 +1,5 @@ #version 450 core +#extension GL_GOOGLE_include_directive : require layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; @@ -10,16 +11,8 @@ layout(std140, binding = 0) uniform TransformUBO { mat4 World; }; -layout(std140, binding = 5) uniform ShadowUBO { - mat4 ShadowLightVP; - float ShadowBias; - float ShadowStrength; - float ShadowTexelSize; - float ShadowLightSize; - uint PCSSEnabled; - uint ShadowMapEnabled; - float _shadowPad0, _shadowPad1; -}; +#include "sleak/shadow_light.glsl" +#include "sleak/frame.glsl" out vec3 fragWorldPos; out vec3 fragWorldNorm; @@ -30,7 +23,7 @@ out vec2 fragUV; out vec4 fragShadowCoord; void main() { - gl_Position = WVP * vec4(inPosition, 1.0); + gl_Position = SleakFrameGuardClip(WVP * vec4(inPosition, 1.0)); vec4 worldPos = World * vec4(inPosition, 1.0); fragWorldPos = worldPos.xyz; @@ -48,5 +41,5 @@ void main() { fragWorldBit = B; fragColor = inColor; fragUV = inUV; - fragShadowCoord = ShadowLightVP * worldPos; + fragShadowCoord = uLightVP * worldPos; } diff --git a/Game/assets/shaders/gbuffer_voxel.vert b/Game/assets/shaders/gbuffer_voxel.vert index 56f8574..bf93bb6 100644 --- a/Game/assets/shaders/gbuffer_voxel.vert +++ b/Game/assets/shaders/gbuffer_voxel.vert @@ -1,4 +1,5 @@ #version 450 +#extension GL_GOOGLE_include_directive : require // ============================================================ // GBuffer Geometry Pass - Voxel Vertex Shader @@ -17,22 +18,8 @@ layout(push_constant) uniform TransformPC { mat4 World; }; -// Light/Shadow UBO (set 2, binding 0) — needed for shadow coordinate -layout(set = 2, binding = 0) uniform ShadowLightUBO { - vec4 uLightDir; - vec4 uLightColor; - vec4 uAmbient; - vec4 uCameraPos; - mat4 uLightVP; - float uShadowBias; - float uShadowStrength; - float uShadowTexelSize; - float uLightSize; - vec4 uFogColor; - float uFogStart; - float uFogEnd; - vec2 _fogPad; // std140: must be vec2 (8B) not float[2] (stride-16 = 32B) -}; +#include "sleak/shadow_light.glsl" +#include "sleak/frame.glsl" layout(location = 0) out vec3 fragWorldPos; layout(location = 1) out vec3 fragWorldNorm; @@ -45,6 +32,7 @@ layout(location = 6) out vec4 fragShadowCoord; void main() { gl_Position = WVP * vec4(inPosition, 1.0); gl_Position.y = -gl_Position.y; // Vulkan Y-axis flip + gl_Position = SleakFrameGuardClip(gl_Position); vec4 worldPos = World * vec4(inPosition, 1.0); fragWorldPos = worldPos.xyz; diff --git a/Game/assets/shaders/gbuffer_voxel.vert.spv b/Game/assets/shaders/gbuffer_voxel.vert.spv index 0204bf0..63927d6 100644 Binary files a/Game/assets/shaders/gbuffer_voxel.vert.spv and b/Game/assets/shaders/gbuffer_voxel.vert.spv differ diff --git a/Game/assets/shaders/lighting_pass_gl.frag b/Game/assets/shaders/lighting_pass_gl.frag index 04b31ac..ebce548 100644 --- a/Game/assets/shaders/lighting_pass_gl.frag +++ b/Game/assets/shaders/lighting_pass_gl.frag @@ -1,4 +1,5 @@ #version 450 core +#extension GL_GOOGLE_include_directive : require // Deferred lighting pass. // Reads the 4 GBuffer textures (bound at units 8-11), @@ -26,6 +27,8 @@ layout(binding = 16) uniform sampler2D brdfLUT; // Shadow map (already bound at unit 3 by the shadow pass) layout(binding = 3) uniform sampler2DShadow shadowMap; +#include "sleak/frame.glsl" + // ---- UBOs ---- struct LightData { vec3 Position; uint Type; @@ -138,7 +141,7 @@ float CalcShadowPCF(vec3 worldPos, float NdotL) { vec4 sc = ShadowLightVP * vec4(worldPos, 1.0); vec3 proj = sc.xyz / sc.w; proj.xy = proj.xy * 0.5 + 0.5; - proj.z = proj.z * 0.5 + 0.5; // remap [-1,1]-> [0,1] for OpenGL + proj.z = SleakNdcToDepth(proj.z); if (proj.z > 1.0 || any(lessThan(proj.xy, vec2(0.0))) || any(greaterThan(proj.xy, vec2(1.0)))) return 1.0; @@ -160,11 +163,9 @@ float CalcShadowPCF(vec3 worldPos, float NdotL) { return mix(1.0, fadedShadow, ShadowStrength * edgeFade); } -// Reconstruct world position from depth. OpenGL: no vertex Y-flip, and the -// default clip depth range is [-1,1], so the stored [0,1] depth maps to -// NDC.z = 2*depth-1 before InvViewProj. +// World position of a GBuffer texel from its depth vec3 ReconstructWorldPos(vec2 uv, float depth) { - vec4 ndc = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); + vec4 ndc = vec4(SleakUvToNdc(uv), SleakDepthToNdc(depth), 1.0); vec4 world = InvViewProj * ndc; return world.xyz / world.w; } @@ -305,5 +306,5 @@ void main() { // ---- Fog ---- color = ApplyFog(color, worldPos); - outColor = vec4(color, 1.0); + outColor = SleakFrameGuard(vec4(color, 1.0)); } diff --git a/Game/assets/shaders/shadow_depth_voxel.vert b/Game/assets/shaders/shadow_depth_voxel.vert index 006b837..a371bea 100644 --- a/Game/assets/shaders/shadow_depth_voxel.vert +++ b/Game/assets/shaders/shadow_depth_voxel.vert @@ -1,4 +1,5 @@ #version 450 +#extension GL_GOOGLE_include_directive : require // Shadow Depth Pass - Voxel Vertex Only // Compact VoxelVertex layout: only position needed for shadow map @@ -8,6 +9,8 @@ layout(location = 1) in vec3 inNormal; layout(location = 2) in vec4 inColor; layout(location = 3) in vec2 inUV; +#include "sleak/frame.glsl" + // Push constant: LightVP*World in slot 0, World in slot 1 layout(push_constant) uniform TransformPC { mat4 WVP; @@ -15,5 +18,5 @@ layout(push_constant) uniform TransformPC { }; void main() { - gl_Position = WVP * vec4(inPosition, 1.0); + gl_Position = SleakFrameGuardClip(WVP * vec4(inPosition, 1.0)); } diff --git a/Game/assets/shaders/shadow_depth_voxel.vert.spv b/Game/assets/shaders/shadow_depth_voxel.vert.spv index f108978..5374b5f 100644 Binary files a/Game/assets/shaders/shadow_depth_voxel.vert.spv and b/Game/assets/shaders/shadow_depth_voxel.vert.spv differ diff --git a/Game/assets/shaders/water_shader.frag b/Game/assets/shaders/water_shader.frag index 5621827..5af8058 100644 --- a/Game/assets/shaders/water_shader.frag +++ b/Game/assets/shaders/water_shader.frag @@ -1,4 +1,5 @@ #version 450 +#extension GL_GOOGLE_include_directive : require layout(location = 0) in vec3 fragWorldPos; layout(location = 1) in vec3 fragNormal; @@ -7,28 +8,8 @@ layout(location = 4) in float fragTime; layout(location = 0) out vec4 outColor; -// NOTE: see water_shader.vert — no MaterialUBO in the Vulkan layout. -// Time comes from uCameraPos.w (interpolated via fragTime). -layout(set = 2, binding = 0) uniform ShadowLightUBO { - vec4 uLightDir; - vec4 uLightColor; - vec4 uAmbient; - vec4 uCameraPos; - mat4 uLightVP; - float uShadowBias; - float uShadowStrength; - float uShadowTexelSize; - float uLightSize; - vec4 uFogColor; // horizon - float uFogStart; - float uFogEnd; - vec2 _fogPad; // std140: must be vec2 (8B), NOT float[2] (stride-16 = 32B) - vec4 uFogZenithColor; // zenith - float uHeightFogTop; - float uHeightFogDensity; - float uHeightFogFalloff; - float uHeightFogEnabled; -}; +#include "sleak/shadow_light.glsl" +#include "sleak/frame.glsl" layout(set = 3, binding = 0) uniform sampler2DShadow shadowMap; @@ -553,5 +534,5 @@ void main() { color = mix(w.backdrop, color, w.edge); else alpha = FallbackAlpha(P, fragNormal); - outColor = vec4(clamp(color, vec3(0.0), vec3(MAX_HDR)), alpha); + outColor = SleakFrameGuard(vec4(clamp(color, vec3(0.0), vec3(MAX_HDR)), alpha)); } diff --git a/Game/assets/shaders/water_shader.frag.spv b/Game/assets/shaders/water_shader.frag.spv index 425cc8f..3e1f3e1 100644 Binary files a/Game/assets/shaders/water_shader.frag.spv and b/Game/assets/shaders/water_shader.frag.spv differ diff --git a/Game/assets/shaders/water_shader.vert b/Game/assets/shaders/water_shader.vert index 6bbb803..617a14d 100644 --- a/Game/assets/shaders/water_shader.vert +++ b/Game/assets/shaders/water_shader.vert @@ -1,4 +1,5 @@ #version 450 +#extension GL_GOOGLE_include_directive : require layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; @@ -10,24 +11,8 @@ layout(push_constant) uniform TransformPC { mat4 World; }; -// NOTE: The engine's Vulkan pipeline layout has no MaterialUBO slot (set 1 is -// the bone UBO, used only by skinned shaders). Time is transported via -// uCameraPos.w, which LightManager populates with a monotonic scene clock. -layout(set = 2, binding = 0) uniform ShadowLightUBO { - vec4 uLightDir; - vec4 uLightColor; - vec4 uAmbient; - vec4 uCameraPos; - mat4 uLightVP; - float uShadowBias; - float uShadowStrength; - float uShadowTexelSize; - float uLightSize; - vec4 uFogColor; - float uFogStart; - float uFogEnd; - vec2 _fogPad; // std140: must be vec2 (8B) not float[2] (stride-16 = 32B) -}; +#include "sleak/shadow_light.glsl" +#include "sleak/frame.glsl" // Scene read block (VertexLayoutDesc::transparentReadsScene) layout(set = 3, binding = 5) uniform SceneReadUBO { @@ -53,7 +38,7 @@ void main() { fragWorldPos = inPosition; fragNormal = inNormal; - fragTime = uCameraPos.w; // scene clock stashed in cam.w by LightManager + fragTime = uFrame.Time; float normalBias = uLightDir.w; fragShadowCoord = uLightVP * (vec4(inPosition, 1.0) + vec4(inNormal * normalBias, 0.0)); diff --git a/Game/assets/shaders/water_shader.vert.spv b/Game/assets/shaders/water_shader.vert.spv index 73123eb..3b949a3 100644 Binary files a/Game/assets/shaders/water_shader.vert.spv and b/Game/assets/shaders/water_shader.vert.spv differ diff --git a/Game/assets/shaders/water_shader_gl.frag b/Game/assets/shaders/water_shader_gl.frag index 696b055..5a7bb56 100644 --- a/Game/assets/shaders/water_shader_gl.frag +++ b/Game/assets/shaders/water_shader_gl.frag @@ -1,4 +1,5 @@ #version 450 core +#extension GL_GOOGLE_include_directive : require in vec3 fragWorldPos; in vec3 fragNormal; @@ -47,6 +48,8 @@ layout(std140, binding = 5) uniform ShadowUBO { layout(binding = 3) uniform sampler2DShadow shadowMap; +#include "sleak/frame.glsl" + // Scene read (VertexLayoutDesc::transparentReadsScene) layout(binding = 4) uniform sampler2D sceneColor; // tonemapped + fogged layout(binding = 5) uniform sampler2D sceneDepth; @@ -598,5 +601,5 @@ void main() { color = mix(w.backdrop, color, w.edge); else alpha = FallbackAlpha(P, fragNormal); - outColor = vec4(color, alpha); + outColor = SleakFrameGuard(vec4(color, alpha)); } diff --git a/Game/assets/shaders/water_shader_gl.vert b/Game/assets/shaders/water_shader_gl.vert index 378e49e..710f21e 100644 --- a/Game/assets/shaders/water_shader_gl.vert +++ b/Game/assets/shaders/water_shader_gl.vert @@ -1,4 +1,5 @@ #version 450 core +#extension GL_GOOGLE_include_directive : require layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; @@ -21,6 +22,8 @@ layout(std140, binding = 1) uniform MaterialUBO { float MatOpacity; float MatAlphaCutoff; float _matPad1, _matPad2; }; +#include "sleak/frame.glsl" + layout(std140, binding = 5) uniform ShadowUBO { mat4 ShadowLightVP; float ShadowBias; @@ -45,6 +48,6 @@ void main() { fragWorldPos = inPosition; fragNormal = inNormal; fragColor = inColor; - fragTime = MatTiling.x; // game time from SetTiling() + fragTime = uFrame.Time; fragShadowCoord = ShadowLightVP * vec4(inPosition, 1.0); } diff --git a/Game/src/MainScene.cpp b/Game/src/MainScene.cpp index a188f92..4cf4cfb 100644 --- a/Game/src/MainScene.cpp +++ b/Game/src/MainScene.cpp @@ -298,11 +298,6 @@ void MainScene::Update(float deltaTime) { m_chunkManager.SetShadowLightVP(lm ? lm->GetShadowLightVP() : nullptr); m_chunkManager.Update(pos.GetX(), pos.GetY(), pos.GetZ()); m_chunkManager.RenderColumns(); - - // Animate water — pass game time through material tiling.x - if (m_waterMaterial) { - m_waterMaterial->SetTiling(m_gameTime, 1.0f); - } m_chunkManager.RenderWater(); m_blockInteraction.RenderOutline(); diff --git a/scripts/compile_shaders.sh b/scripts/compile_shaders.sh index fdb2cc4..bf7ec9f 100755 --- a/scripts/compile_shaders.sh +++ b/scripts/compile_shaders.sh @@ -11,6 +11,11 @@ SHADER_DIRS=( "$ROOT_DIR/Engine/assets/shaders" "$ROOT_DIR/Game/assets/shaders" ) +# Include libraries: engine sleak/, game craft/ +INCLUDE_FLAGS=( + -I "$ROOT_DIR/Engine/assets/shaders/include" + -I "$ROOT_DIR/Game/assets/shaders/include" +) if ! command -v glslc >/dev/null; then echo "error: glslc not found" >&2 @@ -28,7 +33,7 @@ for dir in "${SHADER_DIRS[@]}"; do for shader in "$dir"/*.vert "$dir"/*.frag "$dir"/*.comp; do [[ -f "$shader" ]] || continue case "$(basename "$shader")" in *_gl.*) continue ;; esac - if glslc "$shader" -o "$shader.spv"; then + if glslc "${INCLUDE_FLAGS[@]}" "$shader" -o "$shader.spv"; then echo "compiled ${shader#$ROOT_DIR/} -> ${shader#$ROOT_DIR/}.spv" count=$((count + 1)) else