From 4164daa76ea5985aa0d6c2755c6c5e8bdd762dbc Mon Sep 17 00:00:00 2001 From: Try Date: Sat, 18 Jul 2026 21:20:24 +0200 Subject: [PATCH 01/29] cache ray-hits in sparse gbuffer --- game/graphics/renderer.h | 3 + game/graphics/shaders.cpp | 2 + game/graphics/shaders.h | 4 +- shader/CMakeLists.txt | 2 + shader/lighting/light.frag | 6 +- shader/lighting/pt/pathtrace_common.glsl | 6 +- shader/lighting/surfels/surf_alloc.comp | 11 +- shader/lighting/surfels/surf_common.glsl | 4 +- shader/lighting/surfels/surf_compact.comp | 13 ++ shader/lighting/surfels/surf_freelist.comp | 14 ++ shader/lighting/surfels/surf_pathtrace.comp | 2 +- shader/lighting/surfels/surf_raycast.comp | 147 ++++++++++++++++++++ shader/lighting/surfels/surf_update.comp | 2 - 13 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 shader/lighting/surfels/surf_freelist.comp create mode 100644 shader/lighting/surfels/surf_raycast.comp diff --git a/game/graphics/renderer.h b/game/graphics/renderer.h index e47b9bcb6..5e0af2e0d 100644 --- a/game/graphics/renderer.h +++ b/game/graphics/renderer.h @@ -217,6 +217,9 @@ class Renderer final { Tempest::StorageImage surfCnts, surfBins; Tempest::StorageBuffer surfBinsCtrl, surfList; + Tempest::StorageBuffer gbuffFree; + Tempest::StorageImage gbuffDiff, gbuffNorm, gbuffHitT; + Tempest::StorageImage dbgImage; } surf; diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 19aa4a299..6cdc5eab1 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -261,8 +261,10 @@ void Shaders::compileShaders() { surfAlloc = computeShader("surf_alloc.comp.sprv"); surfApply = computeShader("surf_apply.comp.sprv"); + surfFList = computeShader("surf_freelist.comp.sprv"); surfPathtrace = computeShader("surf_pathtrace.comp.sprv"); + surfRaycast = computeShader("surf_raycast.comp.sprv"); } if(Shaders::isVsmSupported()) { diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index 1d2f50e79..f0b755c8f 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -90,10 +90,10 @@ class Shaders { Tempest::RenderPipeline surfDbg; Tempest::ComputePipeline surfBinClear, surfBinPass, surfBinAlloc, surfBinSort; - Tempest::ComputePipeline surfAlloc, surfApply; + Tempest::ComputePipeline surfAlloc, surfApply, surfFList; Tempest::ComputePipeline surfUpdate, surfCulling, surfDecimate, surfCompact; - Tempest::ComputePipeline surfPathtrace; + Tempest::ComputePipeline surfPathtrace, surfRaycast; // Epipolar Tempest::ComputePipeline fogEpipolarVsm; diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index ec2f3736a..e1f74bd4f 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -319,6 +319,7 @@ add_shader(surf_compact.comp lighting/surfels/surf_compact.comp) add_shader(surf_alloc.comp lighting/surfels/surf_alloc.comp) add_shader(surf_apply.comp lighting/surfels/surf_apply.comp) +add_shader(surf_freelist.comp lighting/surfels/surf_freelist.comp) add_shader(surf_bin_clear.comp lighting/surfels/surf_bin_clear.comp) add_shader(surf_bin_alloc.comp lighting/surfels/surf_bin_alloc.comp) @@ -326,6 +327,7 @@ add_shader(surf_bin_pass.comp lighting/surfels/surf_bin_pass.comp) add_shader(surf_bin_sort.comp lighting/surfels/surf_bin_sort.comp) add_shader(surf_pathtrace.comp lighting/surfels/surf_pathtrace.comp) +add_shader(surf_raycast.comp lighting/surfels/surf_raycast.comp) # Pathtracing add_shader(pathtrace.frag lighting/pt/pathtrace.frag -DRAY_QUERY -DRAY_QUERY_AT) diff --git a/shader/lighting/light.frag b/shader/lighting/light.frag index e4450a1a6..eed07306c 100644 --- a/shader/lighting/light.frag +++ b/shader/lighting/light.frag @@ -21,9 +21,9 @@ layout(push_constant, std140) uniform Pbo { layout(binding = 0, std140) uniform UboScene { SceneDesc scene; }; -layout(binding = 1) uniform sampler2D gbufDiffuse; -layout(binding = 2) uniform usampler2D gbufNormal; -layout(binding = 3) uniform sampler2D depth; +layout(binding = 1) uniform texture2D gbufDiffuse; +layout(binding = 2) uniform utexture2D gbufNormal; +layout(binding = 3) uniform texture2D depth; #if defined(VIRTUAL_SHADOW) layout(binding = 5, std430) readonly buffer Omni { uint pageTblOmni[]; }; diff --git a/shader/lighting/pt/pathtrace_common.glsl b/shader/lighting/pt/pathtrace_common.glsl index 7a537a4bd..18c1e314b 100644 --- a/shader/lighting/pt/pathtrace_common.glsl +++ b/shader/lighting/pt/pathtrace_common.glsl @@ -3,7 +3,7 @@ #include "random.glsl" -const float TMax = 1e30f; +const float TMax = 500*100; struct HitResolve { vec4 diff; @@ -40,7 +40,7 @@ float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, inout rayQueryEXT rayQuery; rayQueryInitializeEXT(rayQuery, topLevelAS, flags, CM_ShadowCaster, - rayOrigin, tMin, rayDirection, 500*100); + rayOrigin, tMin, rayDirection, TMax); rayQueryProceedAlphaTest(rayQuery); // rayQueryProceedAlphaTest(rayQuery, rngState); if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) @@ -55,7 +55,7 @@ HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, rayQueryEXT rayQuery; rayQueryInitializeEXT(rayQuery, topLevelAS, flags, 0xFF, - rayOrigin, tMin, rayDirection, 500*100); + rayOrigin, tMin, rayDirection, TMax); rayQueryProceedAlphaTest(rayQuery); // rayQueryProceedAlphaTest(rayQuery, rngState); if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) { diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp index 8a680cdbb..4c412f2c8 100644 --- a/shader/lighting/surfels/surf_alloc.comp +++ b/shader/lighting/surfels/surf_alloc.comp @@ -24,7 +24,7 @@ layout(binding = 1) uniform texture2D irradiance; layout(binding = 2) uniform utexture2D gbufNormal; layout(binding = 3) uniform texture2D depth; layout(binding = 4, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; } surf; -// +layout(binding = 5, std430) buffer SB1 { uint count; uint ids[]; } freelist; layout(binding = 6, r32ui) uniform uimage2D surfCnts; layout(binding = 7, r32ui) uniform uimage2D surfBins; layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; @@ -85,6 +85,13 @@ void fragCoverageVote(float wSum) { atomicMin(fragVote, b); } +uint allocPayload() { + uint i = atomicAdd(freelist.count, 1); + if(i 0; stride >>= 1) { + if(laneID < stride) { + reduction[laneID].rgb += reduction[laneID + stride].rgb; + reduction[laneID].a += reduction[laneID + stride].a; + } + barrier(); + } + } + +void storeGBuf(uint ptr, const HitResolve hit) { + if(ptr==0) + return; //error + uint x = ((ptr >> 0) & 0xFFF); + uint y = ((ptr >> 12) & 0xFFF); + + ivec2 at = ivec2(x,y)*8 + ivec2(gl_LocalInvocationID.xy); + imageStore(gbufDiff, at, vec4(hit.diff.rgb, 1)); + imageStore(gbufNorm, at, vec4(hit.norm.xyz*0.5+0.5, 1)); + } + +void main() { + const float eps = 0.0001; + const uint numPaths = NumThreads; + const uint surfelId = pass==0 ? gl_WorkGroupID.x : (header.count-gl_WorkGroupID.z-1); + // if(surfelId>=header.count) + // return; + + const Surfel p = surfels[surfelId]; + const mat3 tbn = toTangent(decodeNormal(p.norm)); + + Random rngState = Random(pcgHash(gl_LocalInvocationIndex)); + + const uint i = gl_LocalInvocationIndex; + const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); + + const HitResolve hit = raycast(p.pos, ray, rngState); + const vec3 pt = evalLight(p.pos, ray, hit); + + hitCnt = 0; + barrier(); + + if(hit.rayT!=TMax) + atomicAdd(hitCnt, 1); + + storeGBuf(p.payload, hit); + + reduction[i] = vec4(pt.rgb, 1.0/clamp(hit.rayT*0.4, 10.0, 1*p.radius)); + barrier(); + + reduceIrr(); + + if(gl_LocalInvocationIndex==0) { + const float hitMean = float(numPaths)/max(reduction[0].a, eps); //harmonic mean + surfels[surfelId].radiusMean = hitMean; + surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); + } + } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_update.comp b/shader/lighting/surfels/surf_update.comp index bdfefd2e7..7f216b2f5 100644 --- a/shader/lighting/surfels/surf_update.comp +++ b/shader/lighting/surfels/surf_update.comp @@ -37,10 +37,8 @@ void main() { const float rMin = pixelToWorld(MinCoverage, pos4.z/pos4.w); const float rMax = pixelToWorld(DefaultCoverage, pos4.z/pos4.w); const float radiusMean = max(rMin, min(s.radiusMean, rMax)); - const int radiusPix = DefaultCoverage; //min(worldToPixel(s.pos, s.radiusMean), DefaultCoverage); surfels[surfelId].fragCoord = fragCoord; - surfels[surfelId].radiusPix = radiusPix; surfels[surfelId].radius = rMax; surfels[surfelId].radiusMean = radiusMean; } From 714ebfb4189dd7a44c9509c2da2d8a015cd04439 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 19 Jul 2026 00:29:17 +0200 Subject: [PATCH 02/29] deferred lighting for surfels --- game/graphics/shaders.cpp | 1 + game/graphics/shaders.h | 2 +- shader/CMakeLists.txt | 1 + shader/lighting/surfels/surf_common.glsl | 10 ++ shader/lighting/surfels/surf_freelist.comp | 4 +- shader/lighting/surfels/surf_lighting.comp | 134 +++++++++++++++++++++ shader/lighting/surfels/surf_raycast.comp | 21 ++-- 7 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 shader/lighting/surfels/surf_lighting.comp diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 6cdc5eab1..76fc88d2e 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -265,6 +265,7 @@ void Shaders::compileShaders() { surfPathtrace = computeShader("surf_pathtrace.comp.sprv"); surfRaycast = computeShader("surf_raycast.comp.sprv"); + surLighting = computeShader("surf_lighting.comp.sprv"); } if(Shaders::isVsmSupported()) { diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index f0b755c8f..d6e774c68 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -93,7 +93,7 @@ class Shaders { Tempest::ComputePipeline surfAlloc, surfApply, surfFList; Tempest::ComputePipeline surfUpdate, surfCulling, surfDecimate, surfCompact; - Tempest::ComputePipeline surfPathtrace, surfRaycast; + Tempest::ComputePipeline surfPathtrace, surfRaycast, surLighting; // Epipolar Tempest::ComputePipeline fogEpipolarVsm; diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index e1f74bd4f..be5fc299e 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -328,6 +328,7 @@ add_shader(surf_bin_sort.comp lighting/surfels/surf_bin_sort.comp) add_shader(surf_pathtrace.comp lighting/surfels/surf_pathtrace.comp) add_shader(surf_raycast.comp lighting/surfels/surf_raycast.comp) +add_shader(surf_lighting.comp lighting/surfels/surf_lighting.comp) # Pathtracing add_shader(pathtrace.frag lighting/pt/pathtrace.frag -DRAY_QUERY -DRAY_QUERY_AT) diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 289c9771a..1516d0830 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -30,6 +30,16 @@ struct Candidate { vec4 norm; // norm, padd }; +uint packAtlassPos(uint x, uint y) { + return (x) | (y << 12) | 0x80000000; + } + +ivec2 unpackAtlassPos(uint ptr) { + uint x = ((ptr >> 0) & 0xFFF); + uint y = ((ptr >> 12) & 0xFFF); + return ivec2(x, y); + } + bool isSurfelVisible(const Surfel s, ivec2 bboxMin, ivec2 bboxMax) { const ivec2 at = s.fragCoord; const int radius = DefaultCoverage; diff --git a/shader/lighting/surfels/surf_freelist.comp b/shader/lighting/surfels/surf_freelist.comp index ea93402ea..f526d6413 100644 --- a/shader/lighting/surfels/surf_freelist.comp +++ b/shader/lighting/surfels/surf_freelist.comp @@ -1,5 +1,7 @@ #version 450 +#include "lighting/surfels/surf_common.glsl" + layout(local_size_x = 64) in; layout(binding = 0, std430) buffer SB0 { int count; uint idx[]; }; @@ -10,5 +12,5 @@ void main() { count = 0; if(id < idx.length()) - idx[id] = (id | 0x80000000); + idx[id] = packAtlassPos(id%128, id/128); } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp new file mode 100644 index 000000000..6cc6e3cdf --- /dev/null +++ b/shader/lighting/surfels/surf_lighting.comp @@ -0,0 +1,134 @@ +#version 460 + +#define LIGHTING 1 +#define RAY_QUERY +#define RAY_QUERY_AT + +#include "lighting/rt/rt_common.glsl" +#include "lighting/pt/pathtrace_common.glsl" +#include "lighting/surfels/surf_common.glsl" +#include "lighting/tonemapping.glsl" + +#include "scene.glsl" +#include "common.glsl" +#include "random.glsl" + +layout(local_size_x = 8, local_size_y = 8) in; + +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; + +layout(std140, push_constant) uniform Push { + uint pass; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +layout(binding = 1, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; +layout(binding = 2) uniform sampler2D skyLUT; +layout(binding = 3) uniform texture2D gbufDiff; +layout(binding = 4) uniform texture2D gbufNorm; +layout(binding = 5) uniform texture2D gbufHit; + +mat3 toTangent(vec3 norm) { + mat3 tangent; + // Compute a tangent frame and rotate the half vector to world space + vec3 up = abs(norm.z) < 0.999f ? vec3(0.0f, 0.0f, 1.0f) : vec3(1.0f, 0.0f, 0.0f); + tangent[0] = normalize(cross(up, norm)); + tangent[1] = cross(norm, tangent[0]); + tangent[2] = norm; + return tangent; + } + +float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { + vec3 shRay = rayDirection; + + float lamb = max(dot(norm, rayDirection), 0); + if(lamb==0) + return lamb; + + Random rngState = Random(0); + float shadow = rayQueryProceedShadow(rayOrigin, shRay, rngState); + return (lamb * shadow); + } + +vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, HitResolve hit) { + vec3 thruput = min(textureAlbedo(hit.diff.rgb), 0.95); + float depth = hit.rayT; + vec3 color = vec3(0); + + if(hit.rayT==TMax) { + vec3 sky = textureSkyLUT(skyLUT, vec3(0,RPlanet+max(rayOrigin.y*0.01,0),0), rayDirection, scene.sunDir) * scene.GSunIntensity; + color += sky; + color += (vec3(0.3, 0.26, 1)*0.15); //HACK for the night sky + return color; + } + + rayOrigin = (rayOrigin + rayDirection * hit.rayT); + + vec3 direct = vec3(0); + direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir) * scene.sunColor; + color += thruput*direct*Fd_Lambert; + + return color; + } + +HitResolve loadGBuf(uint ptr) { + uint x = ((ptr >> 0) & 0xFFF); + uint y = ((ptr >> 12) & 0xFFF); + + ivec2 at = ivec2(x,y)*8 + ivec2(gl_LocalInvocationID.xy); + + HitResolve hit; + hit.diff = texelFetch(gbufDiff, at, 0); + hit.norm = texelFetch(gbufNorm, at, 0).xyz*2.0-1.0; + hit.rayT = texelFetch(gbufHit, at, 0).x*TMax; + + return hit; + } + +shared vec3 reduction[NumThreads]; +shared uint hitCnt; + +void reduceIrr() { + uint laneID = gl_LocalInvocationIndex; + + for(uint stride = NumThreads/2; stride > 0; stride >>= 1) { + if(laneID < stride) { + reduction[laneID] += reduction[laneID + stride]; + } + barrier(); + } + } + +void main() { + const float eps = 0.0001; + const uint numPaths = NumThreads; + const uint surfelId = pass==0 ? gl_WorkGroupID.x : (header.count-gl_WorkGroupID.z-1); + + const Surfel p = surfels[surfelId]; + const mat3 tbn = toTangent(decodeNormal(p.norm)); + + Random rngState = Random(pcgHash(gl_LocalInvocationIndex)); + + const uint i = gl_LocalInvocationIndex; + const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); + + const HitResolve hit = loadGBuf(p.payload); + const vec3 pt = evalLight(p.pos, ray, hit); + + hitCnt = 0; + barrier(); + + if(hit.rayT!=TMax) + atomicAdd(hitCnt, 1); + + reduction[i] = vec3(pt.rgb); + barrier(); + + reduceIrr(); + + if(gl_LocalInvocationIndex==0) { + //NOTE: not updating harmonic mean - it's frozen after RT-pass + surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); + } + } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index 91ada1ca2..cd5a3e73d 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -27,6 +27,7 @@ layout(binding = 1, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; layout(binding = 2) uniform sampler2D skyLUT; layout(binding = 3, rgba8) uniform image2D gbufDiff; layout(binding = 4, rgba8) uniform image2D gbufNorm; +layout(binding = 5, r16) uniform image2D gbufHit; mat3 toTangent(vec3 norm) { mat3 tangent; @@ -82,16 +83,15 @@ vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, HitResolve hit) { return color; } -shared vec4 reduction[NumThreads]; -shared uint hitCnt; +shared float reduction[NumThreads]; +shared uint hitCnt; void reduceIrr() { uint laneID = gl_LocalInvocationIndex; for(uint stride = NumThreads/2; stride > 0; stride >>= 1) { if(laneID < stride) { - reduction[laneID].rgb += reduction[laneID + stride].rgb; - reduction[laneID].a += reduction[laneID + stride].a; + reduction[laneID] += reduction[laneID + stride]; } barrier(); } @@ -100,12 +100,11 @@ void reduceIrr() { void storeGBuf(uint ptr, const HitResolve hit) { if(ptr==0) return; //error - uint x = ((ptr >> 0) & 0xFFF); - uint y = ((ptr >> 12) & 0xFFF); - ivec2 at = ivec2(x,y)*8 + ivec2(gl_LocalInvocationID.xy); + ivec2 at = unpackAtlassPos(ptr)*8 + ivec2(gl_LocalInvocationID.xy); imageStore(gbufDiff, at, vec4(hit.diff.rgb, 1)); imageStore(gbufNorm, at, vec4(hit.norm.xyz*0.5+0.5, 1)); + imageStore(gbufHit, at, vec4(hit.rayT/TMax)); } void main() { @@ -125,6 +124,7 @@ void main() { const HitResolve hit = raycast(p.pos, ray, rngState); const vec3 pt = evalLight(p.pos, ray, hit); + storeGBuf(p.payload, hit); hitCnt = 0; barrier(); @@ -132,16 +132,13 @@ void main() { if(hit.rayT!=TMax) atomicAdd(hitCnt, 1); - storeGBuf(p.payload, hit); - - reduction[i] = vec4(pt.rgb, 1.0/clamp(hit.rayT*0.4, 10.0, 1*p.radius)); + reduction[i] = 1.0/clamp(hit.rayT*0.4, 10.0, 1*p.radius); barrier(); reduceIrr(); if(gl_LocalInvocationIndex==0) { - const float hitMean = float(numPaths)/max(reduction[0].a, eps); //harmonic mean + const float hitMean = float(numPaths)/max(reduction[0], eps); //harmonic mean surfels[surfelId].radiusMean = hitMean; - surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); } } \ No newline at end of file From bcebe86885b83362122790a40d83751cc0643aeb Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 19 Jul 2026 00:29:17 +0200 Subject: [PATCH 03/29] deferred lighting for surfels --- game/graphics/renderer.cpp | 46 +++++-- game/graphics/shaders.cpp | 1 + game/graphics/shaders.h | 2 +- shader/CMakeLists.txt | 1 + shader/lighting/surfels/surf_common.glsl | 10 ++ shader/lighting/surfels/surf_freelist.comp | 4 +- shader/lighting/surfels/surf_lighting.comp | 134 +++++++++++++++++++++ shader/lighting/surfels/surf_raycast.comp | 21 ++-- 8 files changed, 197 insertions(+), 22 deletions(-) create mode 100644 shader/lighting/surfels/surf_lighting.comp diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index c7851eeb4..ed827d7fb 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -1949,9 +1949,9 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor const auto binCount = tileCount(zbuffer.size(), DefaultCoverage); auto& scene = wview.sceneGlobals(); - auto& dbgImage = usesImage2d (surf.dbgImage, TextureFormat::RGBA8, zbuffer.size()); - auto& irrImage = usesImage2d (surf.irrImage, TextureFormat::RGBA16F, zbuffer.size()); - auto& surfels = usesSsboInit(surf.surfels, shaders.surfAlloc.sizeofBuffer(4, maxSurfels)); + auto& dbgImage = usesImage2d (surf.dbgImage, TextureFormat::RGBA8, zbuffer.size()); + auto& irrImage = usesImage2d (surf.irrImage, TextureFormat::RGBA16F, zbuffer.size()); + auto& surfels = usesSsboInit(surf.surfels, shaders.surfAlloc.sizeofBuffer(4, maxSurfels)); auto& surfCnts = usesImage2d(surf.surfCnts, TextureFormat::R32U, binCount); auto& surfBins = usesImage2d(surf.surfBins, TextureFormat::R32U, binCount); @@ -1968,6 +1968,13 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor cmd.setDebugMarker("Surfels"); + if(surf.gbuffFree.isEmpty()) { + auto& gbuffFree = usesSsbo(surf.gbuffFree, shaders.surfFList.sizeofBuffer(0, maxSurfels)); + cmd.setBinding(0, gbuffFree); + cmd.setPipeline(shaders.surfFList); + cmd.dispatchThreads(maxSurfels); + } + // prev-frame { cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); @@ -1986,6 +1993,7 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor cmd.setBinding(1, hiz.hiZ); cmd.setBinding(2, zbuffer); cmd.setBinding(3, surfels); + cmd.setBinding(4, surf.gbuffFree); // cmd.setBinding(6, surfCnts); cmd.setBinding(7, surfBins); @@ -2041,7 +2049,7 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor cmd.setBinding(2, gbufNormal); cmd.setBinding(3, zbuffer); cmd.setBinding(4, surfels); - // + cmd.setBinding(5, surf.gbuffFree); cmd.setBinding(6, surfCnts); cmd.setBinding(7, surfBins); cmd.setBinding(8, surfList); @@ -2123,14 +2131,29 @@ void Renderer::surfelsBinning(Tempest::Encoder& cmd, Wor } void Renderer::surfelsTrace(Tempest::Encoder& cmd, WorldView& wview, StorageBuffer& surfels, bool postPass) { + static int giMethod = 0; + auto& scene = wview.sceneGlobals(); + const Tempest::ComputePipeline* pso = &shaders.surfRaycast; + if(giMethod==1) + pso = &shaders.surfPathtrace; + + const uint32_t gbufTilesX = uint32_t(std::sqrt(surf.maxSurfels)); + const uint32_t gbufTilesY = (surf.maxSurfels+gbufTilesX-1)/gbufTilesX; + + auto& gbuffDiff = usesImage2d (surf.gbuffDiff, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); + auto& gbuffNorm = usesImage2d (surf.gbuffNorm, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); + auto& gbuffHitT = usesImage2d (surf.gbuffHitT, TextureFormat::R16, gbufTilesX*8, gbufTilesY*8); + const uint32_t pass = postPass ? 1 : 0; cmd.setPushData(pass); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(1, surfels); cmd.setBinding(2, sky.viewCldLut); - // + cmd.setBinding(3, gbuffDiff); + cmd.setBinding(4, gbuffNorm); + cmd.setBinding(5, gbuffHitT); cmd.setBinding(6, scene.rtScene.tlas); cmd.setBinding(7, Sampler::trillinear()); cmd.setBinding(8, scene.rtScene.tex); @@ -2138,10 +2161,17 @@ void Renderer::surfelsTrace(Tempest::Encoder& cmd, World cmd.setBinding(10,scene.rtScene.ibo); cmd.setBinding(11,scene.rtScene.rtDesc); - cmd.setPipeline(shaders.surfPathtrace); - //cmd.dispatchThreads(maxSurfels); + cmd.setPipeline(*pso); const uint32_t offset = postPass ? sizeof(uint32_t) : 0; - cmd.dispatchIndirect(surfels, offset); + if(!(giMethod==0 && postPass==false)) { + cmd.dispatchIndirect(surfels, offset); + // cmd.dispatchThreads(maxSurfels); + } + + if(giMethod==0) { + cmd.setPipeline(shaders.surLighting); + cmd.dispatchIndirect(surfels, offset); + } } void Renderer::prepareIrradiance(Encoder& cmd, WorldView& wview) { diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 6cdc5eab1..76fc88d2e 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -265,6 +265,7 @@ void Shaders::compileShaders() { surfPathtrace = computeShader("surf_pathtrace.comp.sprv"); surfRaycast = computeShader("surf_raycast.comp.sprv"); + surLighting = computeShader("surf_lighting.comp.sprv"); } if(Shaders::isVsmSupported()) { diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index f0b755c8f..d6e774c68 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -93,7 +93,7 @@ class Shaders { Tempest::ComputePipeline surfAlloc, surfApply, surfFList; Tempest::ComputePipeline surfUpdate, surfCulling, surfDecimate, surfCompact; - Tempest::ComputePipeline surfPathtrace, surfRaycast; + Tempest::ComputePipeline surfPathtrace, surfRaycast, surLighting; // Epipolar Tempest::ComputePipeline fogEpipolarVsm; diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index e1f74bd4f..be5fc299e 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -328,6 +328,7 @@ add_shader(surf_bin_sort.comp lighting/surfels/surf_bin_sort.comp) add_shader(surf_pathtrace.comp lighting/surfels/surf_pathtrace.comp) add_shader(surf_raycast.comp lighting/surfels/surf_raycast.comp) +add_shader(surf_lighting.comp lighting/surfels/surf_lighting.comp) # Pathtracing add_shader(pathtrace.frag lighting/pt/pathtrace.frag -DRAY_QUERY -DRAY_QUERY_AT) diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 289c9771a..1516d0830 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -30,6 +30,16 @@ struct Candidate { vec4 norm; // norm, padd }; +uint packAtlassPos(uint x, uint y) { + return (x) | (y << 12) | 0x80000000; + } + +ivec2 unpackAtlassPos(uint ptr) { + uint x = ((ptr >> 0) & 0xFFF); + uint y = ((ptr >> 12) & 0xFFF); + return ivec2(x, y); + } + bool isSurfelVisible(const Surfel s, ivec2 bboxMin, ivec2 bboxMax) { const ivec2 at = s.fragCoord; const int radius = DefaultCoverage; diff --git a/shader/lighting/surfels/surf_freelist.comp b/shader/lighting/surfels/surf_freelist.comp index ea93402ea..f526d6413 100644 --- a/shader/lighting/surfels/surf_freelist.comp +++ b/shader/lighting/surfels/surf_freelist.comp @@ -1,5 +1,7 @@ #version 450 +#include "lighting/surfels/surf_common.glsl" + layout(local_size_x = 64) in; layout(binding = 0, std430) buffer SB0 { int count; uint idx[]; }; @@ -10,5 +12,5 @@ void main() { count = 0; if(id < idx.length()) - idx[id] = (id | 0x80000000); + idx[id] = packAtlassPos(id%128, id/128); } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp new file mode 100644 index 000000000..6cc6e3cdf --- /dev/null +++ b/shader/lighting/surfels/surf_lighting.comp @@ -0,0 +1,134 @@ +#version 460 + +#define LIGHTING 1 +#define RAY_QUERY +#define RAY_QUERY_AT + +#include "lighting/rt/rt_common.glsl" +#include "lighting/pt/pathtrace_common.glsl" +#include "lighting/surfels/surf_common.glsl" +#include "lighting/tonemapping.glsl" + +#include "scene.glsl" +#include "common.glsl" +#include "random.glsl" + +layout(local_size_x = 8, local_size_y = 8) in; + +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; + +layout(std140, push_constant) uniform Push { + uint pass; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +layout(binding = 1, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; +layout(binding = 2) uniform sampler2D skyLUT; +layout(binding = 3) uniform texture2D gbufDiff; +layout(binding = 4) uniform texture2D gbufNorm; +layout(binding = 5) uniform texture2D gbufHit; + +mat3 toTangent(vec3 norm) { + mat3 tangent; + // Compute a tangent frame and rotate the half vector to world space + vec3 up = abs(norm.z) < 0.999f ? vec3(0.0f, 0.0f, 1.0f) : vec3(1.0f, 0.0f, 0.0f); + tangent[0] = normalize(cross(up, norm)); + tangent[1] = cross(norm, tangent[0]); + tangent[2] = norm; + return tangent; + } + +float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { + vec3 shRay = rayDirection; + + float lamb = max(dot(norm, rayDirection), 0); + if(lamb==0) + return lamb; + + Random rngState = Random(0); + float shadow = rayQueryProceedShadow(rayOrigin, shRay, rngState); + return (lamb * shadow); + } + +vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, HitResolve hit) { + vec3 thruput = min(textureAlbedo(hit.diff.rgb), 0.95); + float depth = hit.rayT; + vec3 color = vec3(0); + + if(hit.rayT==TMax) { + vec3 sky = textureSkyLUT(skyLUT, vec3(0,RPlanet+max(rayOrigin.y*0.01,0),0), rayDirection, scene.sunDir) * scene.GSunIntensity; + color += sky; + color += (vec3(0.3, 0.26, 1)*0.15); //HACK for the night sky + return color; + } + + rayOrigin = (rayOrigin + rayDirection * hit.rayT); + + vec3 direct = vec3(0); + direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir) * scene.sunColor; + color += thruput*direct*Fd_Lambert; + + return color; + } + +HitResolve loadGBuf(uint ptr) { + uint x = ((ptr >> 0) & 0xFFF); + uint y = ((ptr >> 12) & 0xFFF); + + ivec2 at = ivec2(x,y)*8 + ivec2(gl_LocalInvocationID.xy); + + HitResolve hit; + hit.diff = texelFetch(gbufDiff, at, 0); + hit.norm = texelFetch(gbufNorm, at, 0).xyz*2.0-1.0; + hit.rayT = texelFetch(gbufHit, at, 0).x*TMax; + + return hit; + } + +shared vec3 reduction[NumThreads]; +shared uint hitCnt; + +void reduceIrr() { + uint laneID = gl_LocalInvocationIndex; + + for(uint stride = NumThreads/2; stride > 0; stride >>= 1) { + if(laneID < stride) { + reduction[laneID] += reduction[laneID + stride]; + } + barrier(); + } + } + +void main() { + const float eps = 0.0001; + const uint numPaths = NumThreads; + const uint surfelId = pass==0 ? gl_WorkGroupID.x : (header.count-gl_WorkGroupID.z-1); + + const Surfel p = surfels[surfelId]; + const mat3 tbn = toTangent(decodeNormal(p.norm)); + + Random rngState = Random(pcgHash(gl_LocalInvocationIndex)); + + const uint i = gl_LocalInvocationIndex; + const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); + + const HitResolve hit = loadGBuf(p.payload); + const vec3 pt = evalLight(p.pos, ray, hit); + + hitCnt = 0; + barrier(); + + if(hit.rayT!=TMax) + atomicAdd(hitCnt, 1); + + reduction[i] = vec3(pt.rgb); + barrier(); + + reduceIrr(); + + if(gl_LocalInvocationIndex==0) { + //NOTE: not updating harmonic mean - it's frozen after RT-pass + surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); + } + } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index 91ada1ca2..cd5a3e73d 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -27,6 +27,7 @@ layout(binding = 1, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; layout(binding = 2) uniform sampler2D skyLUT; layout(binding = 3, rgba8) uniform image2D gbufDiff; layout(binding = 4, rgba8) uniform image2D gbufNorm; +layout(binding = 5, r16) uniform image2D gbufHit; mat3 toTangent(vec3 norm) { mat3 tangent; @@ -82,16 +83,15 @@ vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, HitResolve hit) { return color; } -shared vec4 reduction[NumThreads]; -shared uint hitCnt; +shared float reduction[NumThreads]; +shared uint hitCnt; void reduceIrr() { uint laneID = gl_LocalInvocationIndex; for(uint stride = NumThreads/2; stride > 0; stride >>= 1) { if(laneID < stride) { - reduction[laneID].rgb += reduction[laneID + stride].rgb; - reduction[laneID].a += reduction[laneID + stride].a; + reduction[laneID] += reduction[laneID + stride]; } barrier(); } @@ -100,12 +100,11 @@ void reduceIrr() { void storeGBuf(uint ptr, const HitResolve hit) { if(ptr==0) return; //error - uint x = ((ptr >> 0) & 0xFFF); - uint y = ((ptr >> 12) & 0xFFF); - ivec2 at = ivec2(x,y)*8 + ivec2(gl_LocalInvocationID.xy); + ivec2 at = unpackAtlassPos(ptr)*8 + ivec2(gl_LocalInvocationID.xy); imageStore(gbufDiff, at, vec4(hit.diff.rgb, 1)); imageStore(gbufNorm, at, vec4(hit.norm.xyz*0.5+0.5, 1)); + imageStore(gbufHit, at, vec4(hit.rayT/TMax)); } void main() { @@ -125,6 +124,7 @@ void main() { const HitResolve hit = raycast(p.pos, ray, rngState); const vec3 pt = evalLight(p.pos, ray, hit); + storeGBuf(p.payload, hit); hitCnt = 0; barrier(); @@ -132,16 +132,13 @@ void main() { if(hit.rayT!=TMax) atomicAdd(hitCnt, 1); - storeGBuf(p.payload, hit); - - reduction[i] = vec4(pt.rgb, 1.0/clamp(hit.rayT*0.4, 10.0, 1*p.radius)); + reduction[i] = 1.0/clamp(hit.rayT*0.4, 10.0, 1*p.radius); barrier(); reduceIrr(); if(gl_LocalInvocationIndex==0) { - const float hitMean = float(numPaths)/max(reduction[0].a, eps); //harmonic mean + const float hitMean = float(numPaths)/max(reduction[0], eps); //harmonic mean surfels[surfelId].radiusMean = hitMean; - surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); } } \ No newline at end of file From 48226c5d93d80559df53c7ab21fb8923be9af373 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 19 Jul 2026 19:56:10 +0200 Subject: [PATCH 04/29] test: render-pass based splttig of surfels --- game/graphics/renderer.cpp | 105 ++++++++++++++++-------- game/graphics/renderer.h | 5 ++ game/graphics/shaders.cpp | 15 ++++ game/graphics/shaders.h | 1 + shader/CMakeLists.txt | 3 + shader/lighting/surfels/surf_apply.comp | 2 +- shader/lighting/surfels/surf_apply.frag | 48 +++++++++++ shader/lighting/surfels/surf_apply.vert | 50 +++++++++++ 8 files changed, 193 insertions(+), 36 deletions(-) create mode 100644 shader/lighting/surfels/surf_apply.frag create mode 100644 shader/lighting/surfels/surf_apply.vert diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index ed827d7fb..fa2d9b933 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -368,6 +368,22 @@ ZBuffer& Renderer::usesZBuffer(Tempest::ZBuffer& ret, Tempest::TextureFormat frm return ret; } +Attachment& Renderer::usesAttachment(Tempest::Attachment& ret, Tempest::TextureFormat frm, Tempest::Size size) { + if(textureCast(ret).format()==frm && ret.size()==size) + return ret; + Resources::recycle(std::move(ret)); + ret = Resources::device().attachment(frm, size); + return ret; + } + +Attachment& Renderer::usesAttachment(Tempest::Attachment& ret, Tempest::TextureFormat frm, uint32_t w, uint32_t h) { + if(textureCast(ret).format()==frm && uint32_t(ret.w())==w && uint32_t(ret.h())==h) + return ret; + Resources::recycle(std::move(ret)); + ret = Resources::device().attachment(frm, w, h); + return ret; + } + StorageBuffer& Renderer::usesSsbo(Tempest::StorageBuffer& ret, size_t size) { if(ret.byteSize()==size) return ret; @@ -2022,22 +2038,7 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor static bool apply = true; if(apply) { surfelsBinning(cmd, wview, tileSize, false); - - cmd.setPushData(push); - cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, irrImage); - cmd.setBinding(2, gbufNormal); - cmd.setBinding(3, zbuffer); - cmd.setBinding(4, surfels); - // - cmd.setBinding(6, surfCnts); - cmd.setBinding(7, surfBins); - cmd.setBinding(8, surfList); - // - cmd.setBinding(11, dbgImage); - - cmd.setPipeline(shaders.surfApply); - cmd.dispatchThreads(zbuffer.size()); + surfelsApply(cmd, wview, tileSize, false); } // current-frame @@ -2067,24 +2068,58 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor if(apply) { surfelsBinning(cmd, wview, tileSize, true); + surfelsApply(cmd, wview, tileSize, true); + } + } - push.pass = 1; - cmd.setPushData(push); - cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, irrImage); - cmd.setBinding(2, gbufNormal); - cmd.setBinding(3, zbuffer); - cmd.setBinding(4, surfels); - // - cmd.setBinding(6, surfCnts); - cmd.setBinding(7, surfBins); - cmd.setBinding(8, surfList); - // - cmd.setBinding(11, dbgImage); +void Renderer::surfelsApply(Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass) { + auto& scene = wview.sceneGlobals(); - cmd.setPipeline(shaders.surfApply); - cmd.dispatchThreads(zbuffer.size()); - } + auto& dbgImage = surf.dbgImage; + auto& irrImage = surf.irrImage; + auto& surfels = surf.surfels; + auto& surfCnts = surf.surfCnts; + auto& surfBins = surf.surfBins; + auto& surfList = surf.surfList; + + auto& irrImage2 = usesAttachment(surf.irrImage2, TextureFormat::RGBA16F, zbuffer.size()); + (void)irrImage2; + + struct Push { + Vec3 originLwc; + uint32_t tileSize; + uint32_t pass; + } push = {}; + push.originLwc = scene.originLwc; + push.tileSize = uint32_t(tileSize); + push.pass = postPass ? 1 : 0; + + cmd.setPushData(push); + cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(1, irrImage); + cmd.setBinding(2, gbufNormal); + cmd.setBinding(3, zbuffer); + cmd.setBinding(4, surfels); + // + cmd.setBinding(6, surfCnts); + cmd.setBinding(7, surfBins); + cmd.setBinding(8, surfList); + // + cmd.setBinding(11, dbgImage); + + cmd.setPipeline(shaders.surfApply); + cmd.dispatchThreads(zbuffer.size()); + + cmd.setFramebuffer({{irrImage2, Vec4(0), Tempest::Preserve}}); + cmd.setPushData(push); + cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(2, gbufNormal); + cmd.setBinding(3, zbuffer); + cmd.setBinding(4, surfels); + cmd.setPipeline(shaders.surfApplyFrag); + cmd.draw(nullptr, 0, 6, 0, surf.maxSurfels); + + cmd.setFramebuffer({}); } void Renderer::surfelsBinning(Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass) { @@ -2142,9 +2177,9 @@ void Renderer::surfelsTrace(Tempest::Encoder& cmd, World const uint32_t gbufTilesX = uint32_t(std::sqrt(surf.maxSurfels)); const uint32_t gbufTilesY = (surf.maxSurfels+gbufTilesX-1)/gbufTilesX; - auto& gbuffDiff = usesImage2d (surf.gbuffDiff, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); - auto& gbuffNorm = usesImage2d (surf.gbuffNorm, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); - auto& gbuffHitT = usesImage2d (surf.gbuffHitT, TextureFormat::R16, gbufTilesX*8, gbufTilesY*8); + auto& gbuffDiff = usesImage2d(surf.gbuffDiff, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); + auto& gbuffNorm = usesImage2d(surf.gbuffNorm, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); + auto& gbuffHitT = usesImage2d(surf.gbuffHitT, TextureFormat::R16, gbufTilesX*8, gbufTilesY*8); const uint32_t pass = postPass ? 1 : 0; cmd.setPushData(pass); diff --git a/game/graphics/renderer.h b/game/graphics/renderer.h index 5e0af2e0d..67a3c36bd 100644 --- a/game/graphics/renderer.h +++ b/game/graphics/renderer.h @@ -49,6 +49,8 @@ class Renderer final { Tempest::StorageImage& usesImage2d(Tempest::StorageImage& ret, Tempest::TextureFormat frm, Tempest::Size sz, bool mips = false); Tempest::StorageImage& usesImage3d(Tempest::StorageImage& ret, Tempest::TextureFormat frm, uint32_t w, uint32_t h, uint32_t d, bool mips = false); Tempest::ZBuffer& usesZBuffer(Tempest::ZBuffer& ret, Tempest::TextureFormat frm, uint32_t w, uint32_t h); + Tempest::Attachment& usesAttachment(Tempest::Attachment&ret, Tempest::TextureFormat frm, Tempest::Size sz); + Tempest::Attachment& usesAttachment(Tempest::Attachment&ret, Tempest::TextureFormat frm, uint32_t w, uint32_t h); Tempest::StorageBuffer& usesSsbo(Tempest::StorageBuffer& ret, size_t size); Tempest::StorageBuffer& usesSsboInit(Tempest::StorageBuffer& ret, size_t size); Tempest::StorageBuffer& usesScratch(Tempest::StorageBuffer& ret, size_t size); @@ -66,6 +68,7 @@ class Renderer final { void prepareEpipolar (Tempest::Encoder& cmd, WorldView& wview); void prepareSurfels (Tempest::Encoder& cmd, WorldView& wview); + void surfelsApply (Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass); void surfelsBinning (Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass); void surfelsTrace (Tempest::Encoder& cmd, WorldView& wview, Tempest::StorageBuffer& surfels, bool postPass); @@ -213,6 +216,8 @@ class Renderer final { const uint32_t maxSurfels = 16*1024; Tempest::StorageBuffer surfels; + Tempest::Attachment irrImage2; + Tempest::StorageImage irrImage; Tempest::StorageImage surfCnts, surfBins; Tempest::StorageBuffer surfBinsCtrl, surfList; diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 76fc88d2e..2f6fbe500 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -266,6 +266,21 @@ void Shaders::compileShaders() { surfPathtrace = computeShader("surf_pathtrace.comp.sprv"); surfRaycast = computeShader("surf_raycast.comp.sprv"); surLighting = computeShader("surf_lighting.comp.sprv"); + + { + RenderState state; + state.setCullFaceMode(RenderState::CullMode::NoCull); + state.setZTestMode (RenderState::ZTestMode::Less); + state.setBlendSource (RenderState::BlendMode::One); + state.setBlendDest (RenderState::BlendMode::One); + state.setZWriteEnabled(false); + + auto sh = GothicShader::get("surf_apply.vert.sprv"); + auto vs = device.shader(sh.data,sh.len); + sh = GothicShader::get("surf_apply.frag.sprv"); + auto fs = device.shader(sh.data,sh.len); + surfApplyFrag = device.pipeline(Triangles,state,vs,fs); + } } if(Shaders::isVsmSupported()) { diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index d6e774c68..8d649f462 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -92,6 +92,7 @@ class Shaders { Tempest::ComputePipeline surfAlloc, surfApply, surfFList; Tempest::ComputePipeline surfUpdate, surfCulling, surfDecimate, surfCompact; + Tempest::RenderPipeline surfApplyFrag; Tempest::ComputePipeline surfPathtrace, surfRaycast, surLighting; diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index be5fc299e..d83c0d41e 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -321,6 +321,9 @@ add_shader(surf_alloc.comp lighting/surfels/surf_alloc.comp) add_shader(surf_apply.comp lighting/surfels/surf_apply.comp) add_shader(surf_freelist.comp lighting/surfels/surf_freelist.comp) +add_shader(surf_apply.vert lighting/surfels/surf_apply.vert) +add_shader(surf_apply.frag lighting/surfels/surf_apply.frag) + add_shader(surf_bin_clear.comp lighting/surfels/surf_bin_clear.comp) add_shader(surf_bin_alloc.comp lighting/surfels/surf_bin_alloc.comp) add_shader(surf_bin_pass.comp lighting/surfels/surf_bin_pass.comp) diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 8c7a00478..4a9160e4f 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -20,7 +20,7 @@ layout(binding = 1, rgba16f) uniform image2D irradiance; layout(binding = 2) uniform utexture2D gbufNormal; layout(binding = 3) uniform texture2D depth; layout(binding = 4, std430) readonly buffer SB0 { SurfHeader header; Surfel surfels[]; }; -layout(binding = 5, std430) buffer SB1 { uint usage[]; }; +// layout(binding = 6, r32ui) uniform uimage2D surfCnts; layout(binding = 7, r32ui) uniform uimage2D surfBins; layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; diff --git a/shader/lighting/surfels/surf_apply.frag b/shader/lighting/surfels/surf_apply.frag new file mode 100644 index 000000000..14aa9955e --- /dev/null +++ b/shader/lighting/surfels/surf_apply.frag @@ -0,0 +1,48 @@ +#version 450 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(location = 0) out vec4 outColor; + +layout(location = 0) in vec3 surfPos; +layout(location = 1) in float surfRadius; +layout(location = 2) in vec3 surfNorm; +layout(location = 3) in vec4 surfIrr; + +layout(std140, push_constant) uniform Push { + vec3 originLwc; + uint tileSize; + uint pass; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +layout(binding = 1, rgba16f) uniform image2D irradiance; +layout(binding = 2) uniform utexture2D gbufNormal; +layout(binding = 3) uniform texture2D depth; +layout(binding = 4, std430) readonly buffer SB0 { SurfHeader header; Surfel surfels[]; }; + +vec3 worldPos(float z, ivec2 fragCoord, ivec2 screenSize) { + const mat4 inv = scene.viewProjectLwcInv; + const vec2 uv = vec2(fragCoord+0.5)*scene.screenResInv; + const vec4 pos = inv*vec4(uv * 2.0 - 1.0, z, 1); + return pos.xyz/pos.w + originLwc; + } + +void main() { + const ivec2 fragCoord = ivec2(gl_FragCoord.xy); + const ivec2 screenSize = ivec2(textureSize(depth,0)); + + const float z = texelFetch(depth, fragCoord, 0).x; + const vec3 wpos = worldPos(z, fragCoord, screenSize); + const vec3 norm = normalFetch(gbufNormal, fragCoord); + + const float w = calculteWeight(surfPos, surfNorm, surfIrr.w, surfRadius, wpos, norm); + const vec3 color = surfIrr.rgb * scene.exposure; + if(w <= 0.0) + discard; + + outColor = vec4(color*w, w); + } diff --git a/shader/lighting/surfels/surf_apply.vert b/shader/lighting/surfels/surf_apply.vert new file mode 100644 index 000000000..4d1d20af5 --- /dev/null +++ b/shader/lighting/surfels/surf_apply.vert @@ -0,0 +1,50 @@ +#version 450 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(location = 0) out vec3 surfPos; +layout(location = 1) out float surfRadius; +layout(location = 2) out vec3 surfNorm; +layout(location = 3) out vec4 surfIrr; + +layout(std140, push_constant) uniform Push { + vec3 originLwc; + uint tileSize; + uint pass; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +layout(binding = 1, rgba16f) uniform image2D irradiance; +layout(binding = 2) uniform utexture2D gbufNormal; +layout(binding = 3) uniform texture2D depth; +layout(binding = 4, std430) readonly buffer SB0 { SurfHeader header; Surfel surfels[]; }; + +const ivec2 vert[] = ivec2[6]( + ivec2(-1, -1), + ivec2(+1, -1), + ivec2(-1, +1), + + ivec2(-1, +1), + ivec2(+1, -1), + ivec2(+1, +1) + ); + +void main() { + const uint id = gl_InstanceIndex; + if(id >= header.count) { + gl_Position = vec4(uintBitsToFloat(0x7fc00000)); + return; + } + + const Surfel s = surfels[id]; + surfPos = s.pos; + surfRadius = s.radius; + surfNorm = decodeNormal(s.norm); + surfIrr = vec4(s.irradiance, s.radiusMean); + + vec2 pos = (s.fragCoord + DefaultCoverage*vert[gl_VertexIndex])*scene.screenResInv; + gl_Position = vec4(pos*2.0-1.0, 0, 1); + } From e5c92bbb7ec6f98686fbb4ba5a4d02be190a6616 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 19 Jul 2026 21:14:22 +0200 Subject: [PATCH 05/29] optimization in progress --- game/graphics/renderer.cpp | 6 +-- shader/lighting/surfels/surf_apply.comp | 50 ++++++++++++++----------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index fa2d9b933..2ecfa87d6 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -2075,7 +2075,6 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor void Renderer::surfelsApply(Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass) { auto& scene = wview.sceneGlobals(); - auto& dbgImage = surf.dbgImage; auto& irrImage = surf.irrImage; auto& surfels = surf.surfels; auto& surfCnts = surf.surfCnts; @@ -2104,12 +2103,10 @@ void Renderer::surfelsApply(Tempest::Encoder& cmd, World cmd.setBinding(6, surfCnts); cmd.setBinding(7, surfBins); cmd.setBinding(8, surfList); - // - cmd.setBinding(11, dbgImage); cmd.setPipeline(shaders.surfApply); cmd.dispatchThreads(zbuffer.size()); - + /* cmd.setFramebuffer({{irrImage2, Vec4(0), Tempest::Preserve}}); cmd.setPushData(push); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); @@ -2120,6 +2117,7 @@ void Renderer::surfelsApply(Tempest::Encoder& cmd, World cmd.draw(nullptr, 0, 6, 0, surf.maxSurfels); cmd.setFramebuffer({}); + */ } void Renderer::surfelsBinning(Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass) { diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 4a9160e4f..32a3e8278 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -4,7 +4,7 @@ #include "scene.glsl" #include "common.glsl" -layout(local_size_x = 16, local_size_y = 16) in; +layout(local_size_x = 32, local_size_y = 16) in; const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; @@ -24,13 +24,15 @@ layout(binding = 4, std430) readonly buffer SB0 { SurfHeader header; Surfel surf layout(binding = 6, r32ui) uniform uimage2D surfCnts; layout(binding = 7, r32ui) uniform uimage2D surfBins; layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; -// -layout(binding = 11, rgba8) uniform image2D dbgImage; -shared vec4 surfelsPos [NumThreads]; -shared vec4 surfelsNorm[NumThreads]; -shared vec4 surfelsIrr [NumThreads]; -shared uint surfelCount; +struct TSurfel { + vec4 pos; + vec4 norm; + vec4 irr; + }; + +shared TSurfel tSurfels[NumThreads]; +shared uint surfelCount; vec3 worldPos(float z, ivec2 fragCoord, ivec2 screenSize) { const mat4 inv = scene.viewProjectLwcInv; @@ -40,11 +42,12 @@ vec3 worldPos(float z, ivec2 fragCoord, ivec2 screenSize) { } void applyBatch(inout vec4 clr, const vec3 wpos, const vec3 norm, const uint surfCount) { - for(int i=0; i=tSurfels.length()) + return; + + TSurfel sx; + sx.pos = vec4(s.pos, s.radiusMean); + sx.norm = vec4(decodeNormal(s.norm), s.radius); + sx.irr = vec4(s.irradiance, 0); + tSurfels[id] = sx; } void main() { @@ -96,26 +104,25 @@ void main() { surfelCount = 0; vec4 clr = vec4(0); - for(uint i=0; i0) clr.rgb /= clr.a; imageStore(irradiance, fragCoord, vec4(clr.rgb, clr.a)); } - - imageStore(dbgImage, fragCoord, vec4(clr.a)); } From 3c6e78d8994b6248d23d610f5924fd39795db203 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 19 Jul 2026 21:57:25 +0200 Subject: [PATCH 06/29] Update surf_apply.comp --- shader/lighting/surfels/surf_apply.comp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 32a3e8278..113747a04 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -4,7 +4,7 @@ #include "scene.glsl" #include "common.glsl" -layout(local_size_x = 32, local_size_y = 16) in; +layout(local_size_x = 16, local_size_y = 16) in; const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; @@ -42,7 +42,7 @@ vec3 worldPos(float z, ivec2 fragCoord, ivec2 screenSize) { } void applyBatch(inout vec4 clr, const vec3 wpos, const vec3 norm, const uint surfCount) { - for(int i=0; i=cnt) + break; + + barrier(); + surfelCount = 0; + } if(!valid) return; From ba65ba9f7a978c1e3b024c3466fe3d7f32c480d1 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 19 Jul 2026 23:37:35 +0200 Subject: [PATCH 07/29] experiment with surfel coverage --- game/graphics/renderer.cpp | 28 ++++++++++++------------ shader/lighting/surfels/surf_alloc.comp | 5 ++--- shader/lighting/surfels/surf_common.glsl | 3 ++- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index 2ecfa87d6..7b9579eb0 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -1959,15 +1959,15 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor if(settings.giMethod!=GiMethod::IrrC || !settings.zCloudShadowScale) return; - const uint32_t maxSurfels = surf.maxSurfels; - const int32_t tileSize = 128; - const int32_t DefaultCoverage = 128; - const auto binCount = tileCount(zbuffer.size(), DefaultCoverage); + const uint32_t maxSurfels = surf.maxSurfels; + const int32_t TileSize = 64; + const int32_t LargeTile = 128; + const auto binCount = tileCount(zbuffer.size(), TileSize); auto& scene = wview.sceneGlobals(); - auto& dbgImage = usesImage2d (surf.dbgImage, TextureFormat::RGBA8, zbuffer.size()); - auto& irrImage = usesImage2d (surf.irrImage, TextureFormat::RGBA16F, zbuffer.size()); - auto& surfels = usesSsboInit(surf.surfels, shaders.surfAlloc.sizeofBuffer(4, maxSurfels)); + auto& dbgImage = usesImage2d (surf.dbgImage, TextureFormat::RGBA8, zbuffer.size()); + auto& irrImage = usesImage2d (surf.irrImage, TextureFormat::RGBA16F, zbuffer.size()); + auto& surfels = usesSsboInit(surf.surfels, shaders.surfAlloc.sizeofBuffer(4, maxSurfels)); auto& surfCnts = usesImage2d(surf.surfCnts, TextureFormat::R32U, binCount); auto& surfBins = usesImage2d(surf.surfBins, TextureFormat::R32U, binCount); @@ -1979,7 +1979,7 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor uint32_t pass; } push = {}; push.originLwc = scene.originLwc; - push.tileSize = uint32_t(tileSize); + push.tileSize = uint32_t(TileSize); push.pass = 0; cmd.setDebugMarker("Surfels"); @@ -2003,7 +2003,7 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor static bool gc = true; if(gc) { - surfelsBinning(cmd, wview, tileSize, false); + surfelsBinning(cmd, wview, TileSize, false); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(1, hiz.hiZ); @@ -2037,8 +2037,8 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor static bool apply = true; if(apply) { - surfelsBinning(cmd, wview, tileSize, false); - surfelsApply(cmd, wview, tileSize, false); + surfelsBinning(cmd, wview, TileSize, false); + surfelsApply(cmd, wview, TileSize, false); } // current-frame @@ -2057,7 +2057,7 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor // cmd.setBinding(11, dbgImage); - const auto tc = tileCount(zbuffer.size(), 128); + const auto tc = tileCount(zbuffer.size(), LargeTile); cmd.setPipeline(shaders.surfAlloc); cmd.dispatch(tc); } @@ -2067,8 +2067,8 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor } if(apply) { - surfelsBinning(cmd, wview, tileSize, true); - surfelsApply(cmd, wview, tileSize, true); + surfelsBinning(cmd, wview, TileSize, true); + surfelsApply(cmd, wview, TileSize, true); } } diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp index 4c412f2c8..a6df9eb69 100644 --- a/shader/lighting/surfels/surf_alloc.comp +++ b/shader/lighting/surfels/surf_alloc.comp @@ -15,7 +15,6 @@ const int MaxQueue = 256; layout(std140, push_constant) uniform Push { vec3 originLwc; - uint tileSize; }; layout(binding = 0, std140) uniform UboScene { SceneDesc scene; @@ -203,10 +202,10 @@ void main() { barrier(); const ivec2 screenSize = ivec2(textureSize(depth,0)); - const ivec2 tileCount = ivec2(tileSize)/ivec2(gl_WorkGroupSize.xy); + const ivec2 tileCount = ivec2(LargeTile)/ivec2(gl_WorkGroupSize.xy); for(int y=0; y Date: Mon, 20 Jul 2026 00:32:19 +0200 Subject: [PATCH 08/29] enable shadowmap for lighting --- game/graphics/renderer.cpp | 8 ++++-- shader/lighting/surfels/surf_lighting.comp | 33 +++++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index 7b9579eb0..6a85b34f4 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -432,7 +432,8 @@ void Renderer::resetShadowmap() { for(int i=0; i& cmd, World auto& surfBins = surf.surfBins; auto& surfList = surf.surfList; - auto& irrImage2 = usesAttachment(surf.irrImage2, TextureFormat::RGBA16F, zbuffer.size()); - (void)irrImage2; + // auto& irrImage2 = usesAttachment(surf.irrImage2, TextureFormat::RGBA16F, zbuffer.size()); + // (void)irrImage2; struct Push { Vec3 originLwc; @@ -2193,6 +2194,7 @@ void Renderer::surfelsTrace(Tempest::Encoder& cmd, World cmd.setBinding(9, scene.rtScene.vbo); cmd.setBinding(10,scene.rtScene.ibo); cmd.setBinding(11,scene.rtScene.rtDesc); + cmd.setBinding(12,shadowMap[1]); cmd.setPipeline(*pso); const uint32_t offset = postPass ? sizeof(uint32_t) : 0; diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index 6cc6e3cdf..8741604f1 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -28,6 +28,24 @@ layout(binding = 2) uniform sampler2D skyLUT; layout(binding = 3) uniform texture2D gbufDiff; layout(binding = 4) uniform texture2D gbufNorm; layout(binding = 5) uniform texture2D gbufHit; +layout(binding = 12) uniform sampler2D textureSm1; + +float shadowResolve(in vec4 sh, float z) { + z = clamp(z,0,0.99); + sh = step(sh,vec4(z)); + return 0.25*(sh.x+sh.y+sh.z+sh.w); + } + +vec4 shadowSample(in sampler2D shadowMap, vec2 shPos) { + shPos.xy = shPos.xy*vec2(0.5,0.5)+vec2(0.5); + return textureGather(shadowMap,shPos); + } + +float calcShadow(vec3 shPos1) { + vec4 lay1 = shadowSample(textureSm1,shPos1.xy); + float v1 = shadowResolve(lay1,shPos1.z); + return v1; + } mat3 toTangent(vec3 norm) { mat3 tangent; @@ -40,14 +58,21 @@ mat3 toTangent(vec3 norm) { } float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { - vec3 shRay = rayDirection; - float lamb = max(dot(norm, rayDirection), 0); if(lamb==0) return lamb; - Random rngState = Random(0); - float shadow = rayQueryProceedShadow(rayOrigin, shRay, rngState); + vec4 shCoord = scene.viewShadow[1]*vec4(rayOrigin,1); + if(abs(shCoord.x)>=shCoord.w && abs(shCoord.y)>=shCoord.w) { +#if 1 + return 1; +#else + Random rngState = Random(0); + float shadow = rayQueryProceedShadow(rayOrigin, rayDirection, rngState); + return (lamb * shadow); +#endif + } + float shadow = calcShadow(shCoord.xyz/shCoord.w); return (lamb * shadow); } From acac68ba99de11581618118cbfd8568bf46dd3c7 Mon Sep 17 00:00:00 2001 From: Try Date: Tue, 21 Jul 2026 18:31:30 +0200 Subject: [PATCH 09/29] optimize surfel storage --- shader/lighting/surfels/surf_apply.comp | 30 ++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 113747a04..7510fec15 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -25,10 +25,22 @@ layout(binding = 6, r32ui) uniform uimage2D surfCnts; layout(binding = 7, r32ui) uniform uimage2D surfBins; layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; +uvec2 packHalf4x16(vec4 v) { + uint lowBits = packHalf2x16(v.xy); + uint highBits = packHalf2x16(v.zw); + return uvec2(lowBits, highBits); + } + +vec4 unpackHalf4x16(uvec2 packedVal) { + vec2 xy = unpackHalf2x16(packedVal.x); + vec2 zw = unpackHalf2x16(packedVal.y); + return vec4(xy, zw); + } + struct TSurfel { - vec4 pos; - vec4 norm; - vec4 irr; + uvec2 pos; + uvec2 norm; + vec4 irr; }; shared TSurfel tSurfels[NumThreads]; @@ -38,13 +50,15 @@ vec3 worldPos(float z, ivec2 fragCoord, ivec2 screenSize) { const mat4 inv = scene.viewProjectLwcInv; const vec2 uv = vec2(fragCoord+0.5)*scene.screenResInv; const vec4 pos = inv*vec4(uv * 2.0 - 1.0, z, 1); - return pos.xyz/pos.w + originLwc; + return pos.xyz/pos.w; } void applyBatch(inout vec4 clr, const vec3 wpos, const vec3 norm, const uint surfCount) { for(int i=0; i Date: Tue, 21 Jul 2026 21:30:03 +0200 Subject: [PATCH 10/29] change default surfel coverage --- game/graphics/renderer.cpp | 33 ++++---------- game/graphics/renderer.h | 9 ++-- game/graphics/shaders.cpp | 15 ------- game/graphics/shaders.h | 1 - shader/CMakeLists.txt | 3 -- shader/lighting/surfels/surf_alloc.comp | 4 -- shader/lighting/surfels/surf_apply.frag | 48 --------------------- shader/lighting/surfels/surf_apply.vert | 50 ---------------------- shader/lighting/surfels/surf_common.glsl | 2 +- shader/lighting/surfels/surf_freelist.comp | 5 ++- 10 files changed, 18 insertions(+), 152 deletions(-) delete mode 100644 shader/lighting/surfels/surf_apply.frag delete mode 100644 shader/lighting/surfels/surf_apply.vert diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index 6a85b34f4..2f188c468 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -596,8 +596,7 @@ void Renderer::dbgDraw(Tempest::Painter& p) { //tex.push_back(&textureCast(shadowMap[0])); //tex.push_back(&textureCast(vsm.pageData)); //tex.push_back(&textureCast(swrt.outputImage)); - tex.push_back(&textureCast(surf.dbgImage)); - //tex.push_back(&textureCast(surf.irrImage)); + tex.push_back(&textureCast(surf.irrImage)); static int size = 400; int left = 10; @@ -1961,12 +1960,11 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor return; const uint32_t maxSurfels = surf.maxSurfels; - const int32_t TileSize = 64; + const int32_t TileSize = 96; const int32_t LargeTile = 128; const auto binCount = tileCount(zbuffer.size(), TileSize); auto& scene = wview.sceneGlobals(); - auto& dbgImage = usesImage2d (surf.dbgImage, TextureFormat::RGBA8, zbuffer.size()); auto& irrImage = usesImage2d (surf.irrImage, TextureFormat::RGBA16F, zbuffer.size()); auto& surfels = usesSsboInit(surf.surfels, shaders.surfAlloc.sizeofBuffer(4, maxSurfels)); @@ -1987,6 +1985,7 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor if(surf.gbuffFree.isEmpty()) { auto& gbuffFree = usesSsbo(surf.gbuffFree, shaders.surfFList.sizeofBuffer(0, maxSurfels)); + cmd.setPushData(surf.gbufTilesX); cmd.setBinding(0, gbuffFree); cmd.setPipeline(shaders.surfFList); cmd.dispatchThreads(maxSurfels); @@ -2055,8 +2054,6 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor cmd.setBinding(6, surfCnts); cmd.setBinding(7, surfBins); cmd.setBinding(8, surfList); - // - cmd.setBinding(11, dbgImage); const auto tc = tileCount(zbuffer.size(), LargeTile); cmd.setPipeline(shaders.surfAlloc); @@ -2107,18 +2104,6 @@ void Renderer::surfelsApply(Tempest::Encoder& cmd, World cmd.setPipeline(shaders.surfApply); cmd.dispatchThreads(zbuffer.size()); - /* - cmd.setFramebuffer({{irrImage2, Vec4(0), Tempest::Preserve}}); - cmd.setPushData(push); - cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(2, gbufNormal); - cmd.setBinding(3, zbuffer); - cmd.setBinding(4, surfels); - cmd.setPipeline(shaders.surfApplyFrag); - cmd.draw(nullptr, 0, 6, 0, surf.maxSurfels); - - cmd.setFramebuffer({}); - */ } void Renderer::surfelsBinning(Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass) { @@ -2173,12 +2158,12 @@ void Renderer::surfelsTrace(Tempest::Encoder& cmd, World if(giMethod==1) pso = &shaders.surfPathtrace; - const uint32_t gbufTilesX = uint32_t(std::sqrt(surf.maxSurfels)); - const uint32_t gbufTilesY = (surf.maxSurfels+gbufTilesX-1)/gbufTilesX; + const uint32_t gbufX = surf.gbufTilesX * uint32_t(surf.gbufTile.x); + const uint32_t gbufY = surf.gbufTilesY * uint32_t(surf.gbufTile.y); - auto& gbuffDiff = usesImage2d(surf.gbuffDiff, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); - auto& gbuffNorm = usesImage2d(surf.gbuffNorm, TextureFormat::RGBA8, gbufTilesX*8, gbufTilesY*8); - auto& gbuffHitT = usesImage2d(surf.gbuffHitT, TextureFormat::R16, gbufTilesX*8, gbufTilesY*8); + auto& gbuffDiff = usesImage2d(surf.gbuffDiff, TextureFormat::RGBA8, gbufX, gbufY); + auto& gbuffNorm = usesImage2d(surf.gbuffNorm, TextureFormat::RGBA8, gbufX, gbufY); + auto& gbuffHitT = usesImage2d(surf.gbuffHitT, TextureFormat::R16, gbufX, gbufY); const uint32_t pass = postPass ? 1 : 0; cmd.setPushData(pass); @@ -2493,7 +2478,7 @@ void Renderer::drawSurfelsDbg(Encoder& cmd, const WorldView& wvie if(settings.giMethod!=GiMethod::IrrC) return; - static bool enable = false; + static bool enable = true; if(!enable) return; diff --git a/game/graphics/renderer.h b/game/graphics/renderer.h index 67a3c36bd..df54c2aff 100644 --- a/game/graphics/renderer.h +++ b/game/graphics/renderer.h @@ -213,19 +213,18 @@ class Renderer final { } gi; struct { - const uint32_t maxSurfels = 16*1024; + const Tempest::IVec2 gbufTile = {8, 8}; + const uint32_t gbufTilesX = 256; + const uint32_t gbufTilesY = 256; + const uint32_t maxSurfels = gbufTilesX*gbufTilesY; Tempest::StorageBuffer surfels; - Tempest::Attachment irrImage2; - Tempest::StorageImage irrImage; Tempest::StorageImage surfCnts, surfBins; Tempest::StorageBuffer surfBinsCtrl, surfList; Tempest::StorageBuffer gbuffFree; Tempest::StorageImage gbuffDiff, gbuffNorm, gbuffHitT; - - Tempest::StorageImage dbgImage; } surf; struct { diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 2f6fbe500..76fc88d2e 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -266,21 +266,6 @@ void Shaders::compileShaders() { surfPathtrace = computeShader("surf_pathtrace.comp.sprv"); surfRaycast = computeShader("surf_raycast.comp.sprv"); surLighting = computeShader("surf_lighting.comp.sprv"); - - { - RenderState state; - state.setCullFaceMode(RenderState::CullMode::NoCull); - state.setZTestMode (RenderState::ZTestMode::Less); - state.setBlendSource (RenderState::BlendMode::One); - state.setBlendDest (RenderState::BlendMode::One); - state.setZWriteEnabled(false); - - auto sh = GothicShader::get("surf_apply.vert.sprv"); - auto vs = device.shader(sh.data,sh.len); - sh = GothicShader::get("surf_apply.frag.sprv"); - auto fs = device.shader(sh.data,sh.len); - surfApplyFrag = device.pipeline(Triangles,state,vs,fs); - } } if(Shaders::isVsmSupported()) { diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index 8d649f462..d6e774c68 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -92,7 +92,6 @@ class Shaders { Tempest::ComputePipeline surfAlloc, surfApply, surfFList; Tempest::ComputePipeline surfUpdate, surfCulling, surfDecimate, surfCompact; - Tempest::RenderPipeline surfApplyFrag; Tempest::ComputePipeline surfPathtrace, surfRaycast, surLighting; diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index d83c0d41e..be5fc299e 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -321,9 +321,6 @@ add_shader(surf_alloc.comp lighting/surfels/surf_alloc.comp) add_shader(surf_apply.comp lighting/surfels/surf_apply.comp) add_shader(surf_freelist.comp lighting/surfels/surf_freelist.comp) -add_shader(surf_apply.vert lighting/surfels/surf_apply.vert) -add_shader(surf_apply.frag lighting/surfels/surf_apply.frag) - add_shader(surf_bin_clear.comp lighting/surfels/surf_bin_clear.comp) add_shader(surf_bin_alloc.comp lighting/surfels/surf_bin_alloc.comp) add_shader(surf_bin_pass.comp lighting/surfels/surf_bin_pass.comp) diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp index a6df9eb69..6fff98be4 100644 --- a/shader/lighting/surfels/surf_alloc.comp +++ b/shader/lighting/surfels/surf_alloc.comp @@ -27,8 +27,6 @@ layout(binding = 5, std430) buffer SB1 { uint count; uint ids[]; } freelist; layout(binding = 6, r32ui) uniform uimage2D surfCnts; layout(binding = 7, r32ui) uniform uimage2D surfBins; layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; -// -layout(binding = 11, rgba8) uniform image2D dbgImage; shared Candidate pointQueue[MaxQueue]; shared int pointQueueSize; @@ -175,8 +173,6 @@ void mainSubtile(const ivec2 at, const ivec2 fragCoord, const ivec2 screenSize) coverage += amendCoverage(fragCoord, wpos, norm); } - - imageStore(dbgImage, fragCoord, vec4(coverage)); } ivec2 remap(ivec2 subtile, ivec2 tileCnt) { diff --git a/shader/lighting/surfels/surf_apply.frag b/shader/lighting/surfels/surf_apply.frag deleted file mode 100644 index 14aa9955e..000000000 --- a/shader/lighting/surfels/surf_apply.frag +++ /dev/null @@ -1,48 +0,0 @@ -#version 450 - -#include "lighting/surfels/surf_common.glsl" -#include "scene.glsl" -#include "common.glsl" - -layout(location = 0) out vec4 outColor; - -layout(location = 0) in vec3 surfPos; -layout(location = 1) in float surfRadius; -layout(location = 2) in vec3 surfNorm; -layout(location = 3) in vec4 surfIrr; - -layout(std140, push_constant) uniform Push { - vec3 originLwc; - uint tileSize; - uint pass; - }; -layout(binding = 0, std140) uniform UboScene { - SceneDesc scene; - }; -layout(binding = 1, rgba16f) uniform image2D irradiance; -layout(binding = 2) uniform utexture2D gbufNormal; -layout(binding = 3) uniform texture2D depth; -layout(binding = 4, std430) readonly buffer SB0 { SurfHeader header; Surfel surfels[]; }; - -vec3 worldPos(float z, ivec2 fragCoord, ivec2 screenSize) { - const mat4 inv = scene.viewProjectLwcInv; - const vec2 uv = vec2(fragCoord+0.5)*scene.screenResInv; - const vec4 pos = inv*vec4(uv * 2.0 - 1.0, z, 1); - return pos.xyz/pos.w + originLwc; - } - -void main() { - const ivec2 fragCoord = ivec2(gl_FragCoord.xy); - const ivec2 screenSize = ivec2(textureSize(depth,0)); - - const float z = texelFetch(depth, fragCoord, 0).x; - const vec3 wpos = worldPos(z, fragCoord, screenSize); - const vec3 norm = normalFetch(gbufNormal, fragCoord); - - const float w = calculteWeight(surfPos, surfNorm, surfIrr.w, surfRadius, wpos, norm); - const vec3 color = surfIrr.rgb * scene.exposure; - if(w <= 0.0) - discard; - - outColor = vec4(color*w, w); - } diff --git a/shader/lighting/surfels/surf_apply.vert b/shader/lighting/surfels/surf_apply.vert deleted file mode 100644 index 4d1d20af5..000000000 --- a/shader/lighting/surfels/surf_apply.vert +++ /dev/null @@ -1,50 +0,0 @@ -#version 450 - -#include "lighting/surfels/surf_common.glsl" -#include "scene.glsl" -#include "common.glsl" - -layout(location = 0) out vec3 surfPos; -layout(location = 1) out float surfRadius; -layout(location = 2) out vec3 surfNorm; -layout(location = 3) out vec4 surfIrr; - -layout(std140, push_constant) uniform Push { - vec3 originLwc; - uint tileSize; - uint pass; - }; -layout(binding = 0, std140) uniform UboScene { - SceneDesc scene; - }; -layout(binding = 1, rgba16f) uniform image2D irradiance; -layout(binding = 2) uniform utexture2D gbufNormal; -layout(binding = 3) uniform texture2D depth; -layout(binding = 4, std430) readonly buffer SB0 { SurfHeader header; Surfel surfels[]; }; - -const ivec2 vert[] = ivec2[6]( - ivec2(-1, -1), - ivec2(+1, -1), - ivec2(-1, +1), - - ivec2(-1, +1), - ivec2(+1, -1), - ivec2(+1, +1) - ); - -void main() { - const uint id = gl_InstanceIndex; - if(id >= header.count) { - gl_Position = vec4(uintBitsToFloat(0x7fc00000)); - return; - } - - const Surfel s = surfels[id]; - surfPos = s.pos; - surfRadius = s.radius; - surfNorm = decodeNormal(s.norm); - surfIrr = vec4(s.irradiance, s.radiusMean); - - vec2 pos = (s.fragCoord + DefaultCoverage*vert[gl_VertexIndex])*scene.screenResInv; - gl_Position = vec4(pos*2.0-1.0, 0, 1); - } diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 6adac478c..4376b8b76 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -5,7 +5,7 @@ const float SKY_DEPTH = 0.999995; const int MinCoverage = 8; // in pixels -const int DefaultCoverage = 64; // in pixels +const int DefaultCoverage = 96; // in pixels const int LargeTile = 128; // in pixels const uint MaxInTile = 512; // ~32px (~6x6) per surfel diff --git a/shader/lighting/surfels/surf_freelist.comp b/shader/lighting/surfels/surf_freelist.comp index f526d6413..289a02795 100644 --- a/shader/lighting/surfels/surf_freelist.comp +++ b/shader/lighting/surfels/surf_freelist.comp @@ -4,6 +4,9 @@ layout(local_size_x = 64) in; +layout(std140, push_constant) uniform Push { + uint gbufTileX; + }; layout(binding = 0, std430) buffer SB0 { int count; uint idx[]; }; void main() { @@ -12,5 +15,5 @@ void main() { count = 0; if(id < idx.length()) - idx[id] = packAtlassPos(id%128, id/128); + idx[id] = packAtlassPos(id%gbufTileX, id/gbufTileX); } \ No newline at end of file From 432336669af42e8953bb62f81be6350ac73992d6 Mon Sep 17 00:00:00 2001 From: Try Date: Tue, 21 Jul 2026 22:28:44 +0200 Subject: [PATCH 11/29] optimization in progress --- shader/lighting/surfels/surf_apply.comp | 36 ++++++++++++++----------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 7510fec15..8ec0d6c0d 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -25,6 +25,15 @@ layout(binding = 6, r32ui) uniform uimage2D surfCnts; layout(binding = 7, r32ui) uniform uimage2D surfBins; layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; +struct TSurfel { + vec4 pos; + uvec2 norm; + uvec2 irr; + }; + +shared TSurfel tSurfels[NumThreads]; +shared uint surfelCount; + uvec2 packHalf4x16(vec4 v) { uint lowBits = packHalf2x16(v.xy); uint highBits = packHalf2x16(v.zw); @@ -37,31 +46,30 @@ vec4 unpackHalf4x16(uvec2 packedVal) { return vec4(xy, zw); } -struct TSurfel { - uvec2 pos; - uvec2 norm; - vec4 irr; - }; - -shared TSurfel tSurfels[NumThreads]; -shared uint surfelCount; +TSurfel packSurfel(const Surfel s) { + TSurfel sx; + sx.pos = vec4(s.pos, min(s.radiusMean, s.radius)); + sx.norm = packHalf4x16(vec4(decodeNormal(s.norm), s.radius)); + sx.irr = packHalf4x16(vec4(s.irradiance, 0)); + return sx; + } vec3 worldPos(float z, ivec2 fragCoord, ivec2 screenSize) { const mat4 inv = scene.viewProjectLwcInv; const vec2 uv = vec2(fragCoord+0.5)*scene.screenResInv; const vec4 pos = inv*vec4(uv * 2.0 - 1.0, z, 1); - return pos.xyz/pos.w; + return pos.xyz/pos.w + originLwc; } void applyBatch(inout vec4 clr, const vec3 wpos, const vec3 norm, const uint surfCount) { for(int i=0; i=tSurfels.length()) return; - TSurfel sx; - sx.pos = packHalf4x16(vec4(s.pos - originLwc, s.radiusMean)); - sx.norm = packHalf4x16(vec4(decodeNormal(s.norm), s.radius)); - sx.irr = vec4(s.irradiance, 0); - tSurfels[id] = sx; + tSurfels[id] = packSurfel(s); } void main() { From 2b1c4762825e72c97c1211c2e86678a6281872c6 Mon Sep 17 00:00:00 2001 From: Try Date: Tue, 21 Jul 2026 23:05:34 +0200 Subject: [PATCH 12/29] optimize apply pass --- shader/lighting/surfels/surf_apply.comp | 14 +++++---- shader/lighting/surfels/surf_common.glsl | 37 +++++------------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 8ec0d6c0d..89cf8e9ba 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -47,10 +47,13 @@ vec4 unpackHalf4x16(uvec2 packedVal) { } TSurfel packSurfel(const Surfel s) { + float rMax = min(s.radius, 65000); + float rEff = min(s.radiusMean, rMax*0.5); + TSurfel sx; - sx.pos = vec4(s.pos, min(s.radiusMean, s.radius)); - sx.norm = packHalf4x16(vec4(decodeNormal(s.norm), s.radius)); - sx.irr = packHalf4x16(vec4(s.irradiance, 0)); + sx.pos = vec4(s.pos, rEff); + sx.norm = packHalf4x16(vec4(decodeNormal(s.norm), rMax)); + sx.irr = packHalf4x16(vec4(s.irradiance, rMax-rEff)); return sx; } @@ -66,10 +69,11 @@ void applyBatch(inout vec4 clr, const vec3 wpos, const vec3 norm, const uint sur const TSurfel sx = tSurfels[i]; const vec4 pos = vec4(sx.pos); const vec4 nx = unpackHalf4x16(sx.norm); - const float w = calculteWeight(pos.xyz, nx.xyz, pos.w, nx.w, wpos, norm); + const vec4 irr = unpackHalf4x16(sx.irr); + const float w = calculteWeight(pos.xyz, nx.xyz, pos.w, nx.w, irr.w, wpos, norm); if(w <= 0.0) continue; - const vec3 color = unpackHalf4x16(sx.irr).rgb; + const vec3 color = irr.rgb; clr += vec4(color*w, w); } } diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 4376b8b76..53304b95a 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -112,7 +112,7 @@ uint surfHash(vec3 pos, float cellSize, uint inorm) { #endif } -float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, const vec3 wpos, const vec3 wnorm) { +float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float rDiff, const vec3 wpos, const vec3 wnorm) { // An Approximate Global Illumination System for Computer Generated Films // https://www.tabellion.org/et/paper/siggraph_2004_gi_for_films.pdf // https://cgg.mff.cuni.cz/~jaroslav/papers/2008-irradiance_caching_class/03-greg-ic.pdf @@ -121,39 +121,18 @@ float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float dotN = dot(wnorm, snorm); dist = max(dist, 0.0001); -#if 0 - float ePos = dist/rEff; - float eNorm = sqrt(max(1 - 1*dotN, 0)) / sqrt(1.0 - cos(M_PI/6.0)); // Eq. 4 - float w = 1.0 - max(ePos, eNorm); // Eq. 2 - - float eOccl = dot((ldir/dist), 0.5*(snorm+wnorm))*0.5+0.5; // allow small occlusion - //float eOccl = 1.0 - clamp(-dot(ldir, wnorm), 0, 1)*0.5; // allow small occlusion - //float eOccl = (dot(ldir, snorm) > 0.1*dist) ? 0.1 : 1; - return w*eOccl; -#elif 1 // Wendland C2 inspired falloff - rEff = min(rEff, rMax*0.5); - float q = max(min(dist,rMax)-rEff, 0)/(rMax-rEff); + float q = max(min(dist,rMax)-rEff, 0)/rDiff; float wPos = pow(1-q, 4.0)*(4.0*q + 1.0); float wNorm = pow(max(dotN, 0.0), 2.0); float wOccl = 1.0 - max(dot((ldir/dist), snorm), 0.0); return wPos * wNorm * wOccl; -#elif 0 - float wPos = 1.0 - smoothstep(min(rEff,rMax*0.6), rMax, dist); - float wNorm = pow(max(dotN, 0.0), 2.0); - float wOccl = 1.0 - max(dot((ldir/dist), snorm), 0.0); - return wPos * wNorm * wOccl; -#elif 0 - float wPos = max(1.0 - dist/rMax, 0.0)*(rEff/dist); - float wNorm = pow(max(dotN, 0.0), 2.0); - float wOccl = dot((ldir/dist), snorm)*0.5+0.5; // allow small occlusion - return wPos * wNorm * wOccl; -#else - float ePos = max(dist/rEff, 0.0); - float eNorm = sqrt(max(1 - 1*dotN, 0)); - float eOccl = dot((ldir/dist), 0.5*(snorm+wnorm))*0.5+0.5; // allow small occlusion - return (1.0*eOccl)/max(ePos + eNorm, 0.0001) - 1.0; -#endif + } + +float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, const vec3 wpos, const vec3 wnorm) { + rMax = min(rMax, 65000); + rEff = min(rEff, rMax*0.5); + return calculteWeight(spos, snorm, rEff, rMax, rMax-rEff, wpos, wnorm); } vec3 surfDebugColor(Surfel s, uint sId) { From 0979831fe7230d1cdc32bb64cebf46145b4c8bb1 Mon Sep 17 00:00:00 2001 From: Try Date: Wed, 22 Jul 2026 00:36:36 +0200 Subject: [PATCH 13/29] fixup surfel apply/gc --- game/graphics/renderer.cpp | 7 ++++--- shader/lighting/surfels/surf_apply.comp | 4 +--- shader/lighting/surfels/surf_common.glsl | 8 ++++---- shader/lighting/surfels/surf_decimate.comp | 14 +++++++++++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index 2f188c468..cd4bfba02 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -2019,10 +2019,11 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor cmd.setPipeline(shaders.surfCulling); cmd.dispatchThreads(maxSurfels); - cmd.setPushData(push); - cmd.setPipeline(shaders.surfBinSort); // assist with stable GC - cmd.dispatch(surfBins.size()); + //cmd.setPushData(push); + //cmd.setPipeline(shaders.surfBinSort); // assist with stable GC + //cmd.dispatch(surfBins.size()); + cmd.setPushData(push); cmd.setPipeline(shaders.surfDecimate); cmd.dispatchThreads(maxSurfels); diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 89cf8e9ba..2f41b5dbb 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -79,8 +79,6 @@ void applyBatch(inout vec4 clr, const vec3 wpos, const vec3 norm, const uint sur } void pullSurfels(ivec2 bboxMin, ivec2 bboxMax, uint ptr, uint cnt, uint i) { - const uint laneID = gl_LocalInvocationIndex; - if(i >= cnt) return; @@ -129,7 +127,7 @@ void main() { for(uint i=0;; i+=NumThreads) { barrier(); - pullSurfels(bboxMin, bboxMax, ptr, cnt, 0+laneID); + pullSurfels(bboxMin, bboxMax, ptr, cnt, i+laneID); barrier(); if(!sky) { diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 53304b95a..882874550 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -4,10 +4,10 @@ #include "common.glsl" const float SKY_DEPTH = 0.999995; -const int MinCoverage = 8; // in pixels -const int DefaultCoverage = 96; // in pixels -const int LargeTile = 128; // in pixels -const uint MaxInTile = 512; // ~32px (~6x6) per surfel +const int MinCoverage = 8; // in pixels +const int DefaultCoverage = 96; // in pixels +const int LargeTile = 128; // in pixels +const uint MaxInTile = 1024; // ~32px (~6x6) per surfel struct SurfHeader { uint count; diff --git a/shader/lighting/surfels/surf_decimate.comp b/shader/lighting/surfels/surf_decimate.comp index 1c6707f18..c122f3cf4 100644 --- a/shader/lighting/surfels/surf_decimate.comp +++ b/shader/lighting/surfels/surf_decimate.comp @@ -48,11 +48,19 @@ void main() { const uint cnt = imageLoad(surfCnts, cTileId).x; const uint ptr = imageLoad(surfBins, cTileId).x; + /* + if(cnt>1024) { + // too many + surfels[surfelId].radius = 0; + return; + } + */ + float weight = 0; for(uint i=0; i=surfelId) - break; // surfels are sorted + if(sId==surfelId) + break; const Surfel s = surfels[sId]; const ivec2 sTileId = (s.fragCoord/int(tileSize)); @@ -61,7 +69,7 @@ void main() { if(sTileId.y!=tileId.y && tileId.y%2==0) continue; - const float w = calculteWeight(s.pos, decodeNormal(s.norm), s.radiusMean, s.radius, wpos, norm); + const float w = calculteWeight(s.pos, decodeNormal(s.norm), s.radiusMean, s.radius, wpos, norm); weight += w; if(weight>edge) { From babd551bb27eb297396b1873a14ea16dc3717c1e Mon Sep 17 00:00:00 2001 From: Try Date: Wed, 22 Jul 2026 19:21:53 +0200 Subject: [PATCH 14/29] update sky sampling --- game/graphics/renderer.cpp | 41 ++++++++++++++++++++------------------ game/graphics/renderer.h | 2 ++ shader/sky/sky_common.glsl | 2 +- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index cd4bfba02..35f241b07 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -94,6 +94,9 @@ Renderer::Renderer(Tempest::Swapchain& swapchain) settings.swrEnabled = Gothic::options().swRenderingPreset>0; settings.swrtEnabled = Gothic::options().doSoftwareRT; + sky.sampler = Tempest::Sampler::bilinear(); + sky.sampler.vClamp = ClampMode::ClampToEdge; + sky.cloudsLut = device.image2d (sky.lutRGBAFormat, 2, 1); sky.transLut = device.attachment(sky.lutRGBFormat, 256, 64); sky.multiScatLut = device.attachment(sky.lutRGBFormat, 32, 32); @@ -527,7 +530,7 @@ void Renderer::prepareSky(Tempest::Encoder& cmd, WorldVi sz = Vec2(float(sky.multiScatLut.w()), float(sky.multiScatLut.h())); cmd.setFramebuffer({{sky.multiScatLut, Tempest::Discard, Tempest::Preserve}}); - cmd.setBinding(0, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(0, sky.transLut, sky.sampler); cmd.setPushData(&sz, sizeof(sz)); cmd.setPipeline(shaders.skyMultiScattering); cmd.draw(nullptr, 0, 3); @@ -536,9 +539,9 @@ void Renderer::prepareSky(Tempest::Encoder& cmd, WorldVi auto sz = Vec2(float(sky.viewLut.w()), float(sky.viewLut.h())); cmd.setFramebuffer({{sky.viewLut, Tempest::Discard, Tempest::Preserve}}); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(2, sky.multiScatLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(3, sky.cloudsLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(1, sky.transLut, sky.sampler); + cmd.setBinding(2, sky.multiScatLut, sky.sampler); + cmd.setBinding(3, sky.cloudsLut, sky.sampler); cmd.setPushData(&sz, sizeof(sz)); cmd.setPipeline(shaders.skyViewLut); cmd.draw(nullptr, 0, 3); @@ -546,7 +549,7 @@ void Renderer::prepareSky(Tempest::Encoder& cmd, WorldVi sz = Vec2(float(sky.viewCldLut.w()), float(sky.viewCldLut.h())); cmd.setFramebuffer({{sky.viewCldLut, Tempest::Discard, Tempest::Preserve}}); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, sky.viewLut); + cmd.setBinding(1, sky.viewLut, sky.sampler); cmd.setBinding(2, *wview.sky().cloudsDay() .lay[0], Sampler::trillinear()); cmd.setBinding(3, *wview.sky().cloudsDay() .lay[1], Sampler::trillinear()); cmd.setBinding(4, *wview.sky().cloudsNight().lay[0], Sampler::trillinear()); @@ -1664,7 +1667,7 @@ void Renderer::drawReflections(Encoder& cmd, const WorldView& wvi cmd.setBinding(3, gbufNormal, Sampler::nearest (ClampMode::ClampToEdge)); cmd.setBinding(4, zbuffer, Sampler::nearest (ClampMode::ClampToEdge)); cmd.setBinding(5, sceneDepth, Sampler::nearest (ClampMode::ClampToEdge)); - cmd.setBinding(6, sky.viewCldLut); + cmd.setBinding(6, sky.viewCldLut, sky.sampler); cmd.setPipeline(pso); if(Gothic::options().doMeshShading) { cmd.dispatchMeshThreads(gbufDiffuse.size()); @@ -1789,8 +1792,8 @@ void Renderer::drawSky(Encoder& cmd, const WorldView& wview) { cmd.setDebugMarker("Sky"); if(sky.quality==PathTrace) { cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(2, sky.multiScatLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(1, sky.transLut, sky.sampler); + cmd.setBinding(2, sky.multiScatLut, sky.sampler); cmd.setBinding(3, sky.cloudsLut, Sampler::bilinear(ClampMode::ClampToEdge)); cmd.setBinding(4, zbuffer, Sampler::nearest()); cmd.setBinding(5, shadowMap[1], Resources::shadowSampler()); @@ -1801,10 +1804,10 @@ void Renderer::drawSky(Encoder& cmd, const WorldView& wview) { auto& skyShader = sky.quality==VolumetricLQ ? shaders.sky : shaders.skySep; cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(2, sky.multiScatLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(3, sky.viewLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(4, sky.fogLut3D); + cmd.setBinding(1, sky.transLut, sky.sampler); + cmd.setBinding(2, sky.multiScatLut, sky.sampler); + cmd.setBinding(3, sky.viewLut, sky.sampler); + cmd.setBinding(4, sky.fogLut3D, Sampler::bilinear(ClampMode::ClampToEdge)); if(sky.quality!=VolumetricLQ) cmd.setBinding(5, sky.fogLut3DMs, Sampler::bilinear(ClampMode::ClampToEdge)); cmd.setBinding(6, *wview.sky().cloudsDay() .lay[0], Sampler::trillinear()); @@ -2170,7 +2173,7 @@ void Renderer::surfelsTrace(Tempest::Encoder& cmd, World cmd.setPushData(pass); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(1, surfels); - cmd.setBinding(2, sky.viewCldLut); + cmd.setBinding(2, sky.viewCldLut, sky.sampler); cmd.setBinding(3, gbuffDiff); cmd.setBinding(4, gbuffNorm); cmd.setBinding(5, gbuffHitT); @@ -2202,7 +2205,7 @@ void Renderer::prepareIrradiance(Encoder& cmd, WorldView& wview) cmd.setFramebuffer({}); cmd.setBinding(0, sky.irradianceLut); cmd.setBinding(1, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(2, sky.viewCldLut); + cmd.setBinding(2, sky.viewCldLut, sky.sampler); cmd.setPipeline(shaders.irradiance); cmd.dispatch(1); } @@ -2322,7 +2325,7 @@ void Renderer::prepareGi(Encoder& cmd, WorldView& wview) { cmd.setBinding(2, probesGBuffDiff, Sampler::nearest()); cmd.setBinding(3, probesGBuffNorm, Sampler::nearest()); cmd.setBinding(4, probesGBuffRayT, Sampler::nearest()); - cmd.setBinding(5, sky.viewCldLut, Sampler::bilinear()); + cmd.setBinding(5, sky.viewCldLut, sky.sampler); cmd.setBinding(6, shadowMap[1], Sampler::bilinear()); cmd.setBinding(7, probesLightingPrev, Sampler::nearest()); cmd.setBinding(8, hashTable); @@ -2351,9 +2354,9 @@ void Renderer::prepareExposure(Encoder& cmd, WorldView& wview) { cmd.setDebugMarker("Exposure"); cmd.setFramebuffer({}); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, sky.viewCldLut); - cmd.setBinding(2, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(3, sky.cloudsLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(1, sky.viewCldLut, sky.sampler); + cmd.setBinding(2, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(3, sky.cloudsLut, Sampler::bilinear(ClampMode::ClampToEdge)); cmd.setBinding(4, sky.irradianceLut); cmd.setPushData(&push, sizeof(push)); cmd.setPipeline(shaders.skyExposure); @@ -2417,7 +2420,7 @@ void Renderer::drawPathtrace(Tempest::Encoder& cmd, Worl cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); //cmd.setBinding(1, zbuffer); cmd.setBinding(2, sky.irradianceLut); - cmd.setBinding(3, sky.viewCldLut); + cmd.setBinding(3, sky.viewCldLut, sky.sampler); //cmd.setBinding(4, shadowMap[1], Sampler::bilinear()); cmd.setBinding(4, Resources::fallbackBlack(), Sampler::bilinear()); // diff --git a/game/graphics/renderer.h b/game/graphics/renderer.h index df54c2aff..64f0bc189 100644 --- a/game/graphics/renderer.h +++ b/game/graphics/renderer.h @@ -172,6 +172,8 @@ class Renderer final { Tempest::TextureFormat lutRGBFormat = Tempest::TextureFormat::R11G11B10UF; Tempest::TextureFormat lutRGBAFormat = Tempest::TextureFormat::RGBA16F; + Tempest::Sampler sampler = Tempest::Sampler::bilinear(); + bool lutIsInitialized = false; Tempest::Attachment transLut, multiScatLut, viewLut, viewCldLut; Tempest::StorageImage cloudsLut, fogLut3D, fogLut3DMs; diff --git a/shader/sky/sky_common.glsl b/shader/sky/sky_common.glsl index db3a84424..d0344a640 100644 --- a/shader/sky/sky_common.glsl +++ b/shader/sky/sky_common.glsl @@ -80,7 +80,7 @@ vec3 textureLUT(sampler2D tex, vec3 pos, vec3 sunDir) { vec2 uv; uv.x = 0.5 + 0.5*cosAngle; uv.y = (height - RPlanet)/(RAtmos - RPlanet); - return texture(tex, uv).rgb; + return textureLod(tex, uv, 0).rgb; } #endif From d4d270c90b2a206003c6b9d979d8eed0710ff80e Mon Sep 17 00:00:00 2001 From: Try Date: Wed, 22 Jul 2026 21:00:35 +0200 Subject: [PATCH 15/29] do not evaluate lighting in surfel gbuffer pass --- shader/lighting/surfels/surf_raycast.comp | 34 ----------------------- 1 file changed, 34 deletions(-) diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index cd5a3e73d..e62ca87bf 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -39,18 +39,6 @@ mat3 toTangent(vec3 norm) { return tangent; } -float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { - vec3 shRay = rayDirection; - - float lamb = max(dot(norm, rayDirection), 0); - if(lamb==0) - return lamb; - - Random rngState = Random(0); - float shadow = rayQueryProceedShadow(rayOrigin, shRay, rngState); - return (lamb * shadow); - } - HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { HitResolve hit = rayQueryProceedPrimary(rayOrigin, rayDirection, -1, rngState); if(hit.rayT<0) { @@ -62,27 +50,6 @@ HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { return hit; } -vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, HitResolve hit) { - vec3 thruput = min(textureAlbedo(hit.diff.rgb), 0.95); - float depth = hit.rayT; - vec3 color = vec3(0); - - if(hit.rayT==TMax) { - vec3 sky = textureSkyLUT(skyLUT, vec3(0,RPlanet+max(rayOrigin.y*0.01,0),0), rayDirection, scene.sunDir) * scene.GSunIntensity; - color += sky; - color += (vec3(0.3, 0.26, 1)*0.15); //HACK for the night sky - return color; - } - - rayOrigin = (rayOrigin + rayDirection * hit.rayT); - - vec3 direct = vec3(0); - direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir) * scene.sunColor; - color += thruput*direct*Fd_Lambert; - - return color; - } - shared float reduction[NumThreads]; shared uint hitCnt; @@ -123,7 +90,6 @@ void main() { const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); const HitResolve hit = raycast(p.pos, ray, rngState); - const vec3 pt = evalLight(p.pos, ray, hit); storeGBuf(p.payload, hit); hitCnt = 0; From 550e54586db975dca79dc6274494cd12b2bd9374 Mon Sep 17 00:00:00 2001 From: Try Date: Wed, 22 Jul 2026 21:50:21 +0200 Subject: [PATCH 16/29] sky fixup --- game/graphics/renderer.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index 35f241b07..dd0969d23 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -541,7 +541,7 @@ void Renderer::prepareSky(Tempest::Encoder& cmd, WorldVi cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(1, sky.transLut, sky.sampler); cmd.setBinding(2, sky.multiScatLut, sky.sampler); - cmd.setBinding(3, sky.cloudsLut, sky.sampler); + cmd.setBinding(3, sky.cloudsLut, Sampler::bilinear(ClampMode::ClampToEdge)); cmd.setPushData(&sz, sizeof(sz)); cmd.setPipeline(shaders.skyViewLut); cmd.draw(nullptr, 0, 3); @@ -924,7 +924,7 @@ void Renderer::drawSunMoon(Tempest::Encoder& cmd, const cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(1, isSun ? wview.sky().sunImage() : wview.sky().moonImage()); - cmd.setBinding(2, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(2, sky.transLut, sky.sampler); cmd.setPushData(push); cmd.setPipeline(shaders.sun); cmd.draw(nullptr, 0, 6); @@ -1859,12 +1859,12 @@ void Renderer::prepareFog(Encoder& cmd, WorldView& wview auto& shader = sky.quality==VolumetricLQ ? shaders.fogViewLut3d : shaders.fogViewLutSep; cmd.setFramebuffer({}); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(2, sky.multiScatLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(1, sky.transLut, sky.sampler); + cmd.setBinding(2, sky.multiScatLut, sky.sampler); cmd.setBinding(3, sky.cloudsLut, Sampler::bilinear(ClampMode::ClampToEdge)); - cmd.setBinding(4, sky.fogLut3D); + cmd.setBinding(4, sky.fogLut3D, Sampler::bilinear(ClampMode::ClampToEdge)); if(sky.quality==VolumetricHQ || sky.quality==Epipolar) - cmd.setBinding(5, sky.fogLut3DMs, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(5, sky.fogLut3DMs); cmd.setPipeline(shader); cmd.dispatchThreads(uint32_t(sky.fogLut3D.w()), uint32_t(sky.fogLut3D.h())); } @@ -1917,7 +1917,7 @@ void Renderer::prepareFog(Encoder& cmd, WorldView& wview cmd.setBinding(2, wview.sceneGlobals().uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(3, epipolar.epipoles); cmd.setBinding(4, zbuffer); - cmd.setBinding(5, sky.transLut, Sampler::bilinear(ClampMode::ClampToEdge)); + cmd.setBinding(5, sky.transLut, sky.sampler); cmd.setBinding(6, sky.cloudsLut, Sampler::bilinear(ClampMode::ClampToEdge)); cmd.setBinding(7, sky.fogLut3DMs, Sampler::bilinear(ClampMode::ClampToEdge)); cmd.setPipeline(shaders.vsmFogTrace); From ddd2c7552a97c1a1728af37947bf42b35ee97d82 Mon Sep 17 00:00:00 2001 From: Try Date: Wed, 22 Jul 2026 21:50:44 +0200 Subject: [PATCH 17/29] display allocated amount in frame --- shader/lighting/ambient_light.frag | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shader/lighting/ambient_light.frag b/shader/lighting/ambient_light.frag index 1ad95cbf9..8d73eecd4 100644 --- a/shader/lighting/ambient_light.frag +++ b/shader/lighting/ambient_light.frag @@ -16,7 +16,7 @@ layout(binding = 2) uniform usampler2D gbufNormal; #if defined(SURFEL_GI) layout(binding = 3) uniform sampler2D surfGi; layout(binding = 4) uniform sampler2D ssao; -layout(binding = 5, std430) readonly buffer SB0 { uint count; } surf; +layout(binding = 5, std430) readonly buffer SB0 { uint count, one1, one2, added; } surf; #elif defined(SSAO) layout(binding = 3) uniform texture2D irradiance; layout(binding = 4) uniform sampler2D ssao; @@ -97,6 +97,10 @@ void main() { outColor = vec4(1); return; } + if(drawInt(fragCoord.xy-ivec2(100,120), int(surf.added))>0) { + outColor = vec4(1); + return; + } #endif outColor = vec4(color, 1); From dca325514c38071d17eb49286a953ae2a6182479 Mon Sep 17 00:00:00 2001 From: Try Date: Wed, 22 Jul 2026 23:45:42 +0200 Subject: [PATCH 18/29] surfel culling in progress --- shader/lighting/surfels/surf_alloc.comp | 9 ++++----- shader/lighting/surfels/surf_culling.comp | 15 ++++++++++++++- shader/lighting/surfels/surf_dbg.frag | 4 +++- shader/lighting/surfels/surf_dbg.vert | 4 ++-- shader/lighting/surfels/surf_decimate.comp | 3 +++ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp index 6fff98be4..7a9d40960 100644 --- a/shader/lighting/surfels/surf_alloc.comp +++ b/shader/lighting/surfels/surf_alloc.comp @@ -8,7 +8,6 @@ layout(local_size_x = 16, local_size_y = 16) in; const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; -const float NormalBias = 0.0030; const uint Salt = 0x9D; const float MaxAttempts = 1; const int MaxQueue = 256; @@ -33,11 +32,11 @@ shared int pointQueueSize; shared uint fragVote; -vec3 worldPos(float z, vec3 normal, ivec2 fragCoord, ivec2 screenSize) { +vec3 worldPos(float z, ivec2 fragCoord) { const mat4 inv = scene.viewProjectLwcInv; const vec2 uv = vec2(fragCoord+0.5)*scene.screenResInv; const vec4 pos = inv*vec4(uv * 2.0 - 1.0, z, 1); - return (pos.xyz + normal*NormalBias)/pos.w + originLwc; + return pos.xyz/pos.w + originLwc; } float pixelToWorld(float pixelRadius, float z) { @@ -130,7 +129,7 @@ void allocSurfel(ivec2 fragCoord, const ivec2 screenSize, const uint vote) { const vec3 norm = normalFetch(gbufNormal, fragCoord); const float z = texelFetch(depth, fragCoord, 0).x; - const vec3 wpos = worldPos(z, norm, fragCoord, screenSize); + const vec3 wpos = worldPos(z, fragCoord); if(laneID==0 && pointQueueSize= SKY_DEPTH); const vec3 norm = normalFetch(gbufNormal, fragCoord); - const vec3 wpos = worldPos(z, norm, fragCoord, screenSize); + const vec3 wpos = worldPos(z, fragCoord); float coverage = (sky ? 1.0 : estimateCoverage(fragCoord, wpos, norm, 0)); diff --git a/shader/lighting/surfels/surf_culling.comp b/shader/lighting/surfels/surf_culling.comp index fdd0c4222..1b10df940 100644 --- a/shader/lighting/surfels/surf_culling.comp +++ b/shader/lighting/surfels/surf_culling.comp @@ -96,6 +96,12 @@ bool frustrumTest(const vec4 sphere) { return true; } +vec3 unproject(ivec2 fragCoord) { + const float z = texelFetch(depth,fragCoord,0).x; + const vec2 uv = vec2(fragCoord+0.5) * scene.screenResInv; + return projectiveUnproject(scene.projectInv, vec3(uv*2.0-1.0, z)); + } + bool isSurfelVisible(const vec4 p, float radiusMean, ivec2 fragCoord) { if(!frustrumTest(p)) return false; @@ -107,10 +113,17 @@ bool isSurfelVisible(const vec4 p, float radiusMean, ivec2 fragCoord) { 0<=fragCoord.y && fragCoord.y radiusMean) { + if((lz-viewPos.z) > p.w) { + // TODO: two-sided HiZ + return false; + } + /* + const vec3 dp = unproject(fragCoord) - viewPos; + if(dot(dp,dp) > p.w*p.w) { // TODO: two-sided HiZ return false; } + */ } #endif diff --git a/shader/lighting/surfels/surf_dbg.frag b/shader/lighting/surfels/surf_dbg.frag index 7e2ba6364..8c36473fd 100644 --- a/shader/lighting/surfels/surf_dbg.frag +++ b/shader/lighting/surfels/surf_dbg.frag @@ -7,7 +7,7 @@ layout(binding = 0, std140) uniform UboScene { SceneDesc scene; }; -layout(binding = 1, std430) readonly buffer SB0 { uvec4 count; Surfel surfels[]; }; +layout(binding = 1, std430) readonly buffer SB0 { SurfHeader header; Surfel surfels[]; }; layout(binding = 2) uniform usampler2D gbufNormal; layout(binding = 3) uniform sampler2D depth; @@ -46,6 +46,8 @@ void main(void) { //vec3 clr = surfDebugColor(p, instanceIndex) * (1.0-qDist); //vec3 clr = 3.0 * p.irradiance * (1.0-qDist) * scene.exposure; vec3 clr = p.irradiance * (1.0-qDist); + if(instanceIndex >= header.count-header.added) + clr = vec3(0,1,0); outColor = vec4(clr,1.0); // outColor = vec4(1,0,0,1.0); } diff --git a/shader/lighting/surfels/surf_dbg.vert b/shader/lighting/surfels/surf_dbg.vert index 81f0c72ea..7e6aa1fd4 100644 --- a/shader/lighting/surfels/surf_dbg.vert +++ b/shader/lighting/surfels/surf_dbg.vert @@ -7,7 +7,7 @@ layout(binding = 0, std140) uniform UboScene { SceneDesc scene; }; -layout(binding = 1, std430) readonly buffer SB0 { uvec4 count; Surfel surfels[]; }; +layout(binding = 1, std430) readonly buffer SB0 { SurfHeader header; Surfel surfels[]; }; layout(binding = 3) uniform sampler2D depth; layout(location = 0) out vec3 center; @@ -50,7 +50,7 @@ float pixelToWorld(float pixelRadius, float z) { void main() { const uint surfelId = gl_InstanceIndex; - if(surfelId>=count.x) { + if(surfelId>=header.count) { gl_Position = vec4(0); return; } diff --git a/shader/lighting/surfels/surf_decimate.comp b/shader/lighting/surfels/surf_decimate.comp index c122f3cf4..7a2d88528 100644 --- a/shader/lighting/surfels/surf_decimate.comp +++ b/shader/lighting/surfels/surf_decimate.comp @@ -34,10 +34,12 @@ void main() { if(p.radius==0) return; // disabled +#if 0 if(!(-DefaultCoverage <= p.fragCoord.x && p.fragCoord.x < sz.x+DefaultCoverage)) return; if(!(-DefaultCoverage <= p.fragCoord.y && p.fragCoord.y < sz.y+DefaultCoverage)) return; +#endif const vec3 wpos = p.pos; const vec3 norm = decodeNormal(p.norm); @@ -64,6 +66,7 @@ void main() { const Surfel s = surfels[sId]; const ivec2 sTileId = (s.fragCoord/int(tileSize)); + if(sTileId.x!=tileId.x && tileId.x%2==0) continue; if(sTileId.y!=tileId.y && tileId.y%2==0) From 4eb1e0a2b36187a414d4c8d370ddca833308fffb Mon Sep 17 00:00:00 2001 From: Try Date: Wed, 22 Jul 2026 23:47:03 +0200 Subject: [PATCH 19/29] fix viewvec in sky shader --- shader/sky/sky.frag | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/shader/sky/sky.frag b/shader/sky/sky.frag index 3bf4d074a..9b5020048 100644 --- a/shader/sky/sky.frag +++ b/shader/sky/sky.frag @@ -31,6 +31,13 @@ vec3 inverse(vec3 pos) { return (ret.xyz/ret.w)/100.f; } +vec3 viewVec() { + const vec2 uv = gl_FragCoord.xy*scene.screenResInv; + const vec3 pos = vec3(uv*2.0-1.0, 1.0); + vec4 ret = scene.viewProjectInv*vec4(pos,1.0); + return normalize(ret.xyz/ret.w - scene.camPos); //TODO: LWC + } + vec3 atmosphere(vec3 view, vec3 sunDir) { const vec3 viewPos = vec3(0.0, RPlanet + scene.plPosY, 0.0); return textureSkyLUT(skyLUT, viewPos, view, sunDir); @@ -89,11 +96,9 @@ vec3 applyClouds(vec3 skyColor) { } void main() { - const vec2 uv = gl_FragCoord.xy*scene.screenResInv; - const vec2 inPos = (2.0*gl_FragCoord.xy)*scene.screenResInv - 1.0; - - vec3 view = normalize(inverse(vec3(inPos,1.0))); - vec3 sunDir = scene.sunDir; + const vec2 uv = gl_FragCoord.xy*scene.screenResInv; + const vec3 view = viewVec(); + const vec3 sunDir = scene.sunDir; // accounted in additive fog later vec3 maxFog = textureLod(fogLut, vec3(uv, textureSize(fogLut,0).z-1), 0).rgb; From c9b59ae7ff02ad6ea0a58fdcd1b71048f7756b4f Mon Sep 17 00:00:00 2001 From: Try Date: Thu, 23 Jul 2026 01:17:25 +0200 Subject: [PATCH 20/29] micro optimization --- shader/lighting/surfels/surf_apply.comp | 3 ++- shader/lighting/surfels/surf_common.glsl | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 2f41b5dbb..f7da8d3dd 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -124,7 +124,7 @@ void main() { surfelCount = 0; vec4 clr = vec4(0); - for(uint i=0;; i+=NumThreads) { + for(uint i=0;;) { barrier(); pullSurfels(bboxMin, bboxMax, ptr, cnt, i+laneID); @@ -134,6 +134,7 @@ void main() { applyBatch(clr, wpos, norm, surfelCount); } + i += NumThreads; if(i>=cnt) break; diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 882874550..05e1e4e29 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -116,16 +116,19 @@ float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, // An Approximate Global Illumination System for Computer Generated Films // https://www.tabellion.org/et/paper/siggraph_2004_gi_for_films.pdf // https://cgg.mff.cuni.cz/~jaroslav/papers/2008-irradiance_caching_class/03-greg-ic.pdf - vec3 ldir = wpos - spos; - float dist = length(ldir); - float dotN = dot(wnorm, snorm); + float dotN = dot(wnorm, snorm); + if(dotN <= 0) + return 0; + + vec3 ldir = wpos - spos; + float dist = length(ldir); dist = max(dist, 0.0001); // Wendland C2 inspired falloff float q = max(min(dist,rMax)-rEff, 0)/rDiff; float wPos = pow(1-q, 4.0)*(4.0*q + 1.0); float wNorm = pow(max(dotN, 0.0), 2.0); - float wOccl = 1.0 - max(dot((ldir/dist), snorm), 0.0); + float wOccl = 1.0 - max(dot(ldir, snorm)/dist, 0.0); return wPos * wNorm * wOccl; } From 76fcbf74e285d12162be00f96f31538fb55b2697 Mon Sep 17 00:00:00 2001 From: Try Date: Thu, 23 Jul 2026 20:08:12 +0200 Subject: [PATCH 21/29] surfels: handle water hits --- game/graphics/renderer.cpp | 12 +++---- shader/common.glsl | 6 ++++ shader/lighting/pt/pathtrace.frag | 5 +-- shader/lighting/pt/pathtrace_common.glsl | 8 +++-- shader/lighting/surfels/surf_lighting.comp | 38 ++++++++++++++------- shader/lighting/surfels/surf_pathtrace.comp | 4 +-- shader/lighting/surfels/surf_raycast.comp | 25 +++++++++----- shader/materials/main.frag | 3 +- shader/water/underwater.frag | 3 +- 9 files changed, 64 insertions(+), 40 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index dd0969d23..74915e386 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -1967,13 +1967,13 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor const int32_t LargeTile = 128; const auto binCount = tileCount(zbuffer.size(), TileSize); - auto& scene = wview.sceneGlobals(); - auto& irrImage = usesImage2d (surf.irrImage, TextureFormat::RGBA16F, zbuffer.size()); - auto& surfels = usesSsboInit(surf.surfels, shaders.surfAlloc.sizeofBuffer(4, maxSurfels)); + auto& scene = wview.sceneGlobals(); + auto& irrImage = usesImage2d (surf.irrImage, TextureFormat::RGBA16F, zbuffer.size()); + auto& surfels = usesSsboInit(surf.surfels, shaders.surfAlloc.sizeofBuffer(4, maxSurfels)); - auto& surfCnts = usesImage2d(surf.surfCnts, TextureFormat::R32U, binCount); - auto& surfBins = usesImage2d(surf.surfBins, TextureFormat::R32U, binCount); - auto& surfList = usesSsbo (surf.surfList, 16*maxSurfels*sizeof(uint32_t)); + auto& surfCnts = usesImage2d(surf.surfCnts, TextureFormat::R32U, binCount); + auto& surfBins = usesImage2d(surf.surfBins, TextureFormat::R32U, binCount); + auto& surfList = usesSsbo (surf.surfList, 16*maxSurfels*sizeof(uint32_t)); struct Push { Vec3 originLwc; diff --git a/shader/common.glsl b/shader/common.glsl index 27b2ca928..f5624a51c 100644 --- a/shader/common.glsl +++ b/shader/common.glsl @@ -72,6 +72,12 @@ float fresnel(const vec3 incident, const vec3 normal, const float ior) { return kr; } +vec3 waterTransmittance(float raylen) { + const float depth = raylen / 5000.0; // 50 meters + const vec3 transmittance = exp(-depth * vec3(4,2,1) * 1.25); + return transmittance; + } + float safeacos(const float x) { return acos(clamp(x, -1.0, 1.0)); } diff --git a/shader/lighting/pt/pathtrace.frag b/shader/lighting/pt/pathtrace.frag index ce0220662..279a2197c 100644 --- a/shader/lighting/pt/pathtrace.frag +++ b/shader/lighting/pt/pathtrace.frag @@ -1,6 +1,5 @@ #version 460 -#define LIGHTING 1 #define RAY_QUERY #define RAY_QUERY_AT @@ -201,9 +200,7 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection) { } if(underWater) { - const float depth = hit.rayT / 5000.0; // 50 meters - const vec3 transmittance = exp(-depth * vec3(4,2,1) * 1.25); - thruput *= transmittance; + thruput *= waterTransmittance(hit.rayT); } if(hit.water) { diff --git a/shader/lighting/pt/pathtrace_common.glsl b/shader/lighting/pt/pathtrace_common.glsl index 18c1e314b..14d6b9eff 100644 --- a/shader/lighting/pt/pathtrace_common.glsl +++ b/shader/lighting/pt/pathtrace_common.glsl @@ -48,13 +48,13 @@ float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, inout return 0; } -HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, float mipOverride, inout Random rngState) { +HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, float mipOverride, uint mask, inout Random rngState) { // CullBack due to vegetation uint flags = gl_RayFlagsSkipAABBEXT | gl_RayFlagsCullBackFacingTrianglesEXT; float tMin = 2; rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, topLevelAS, flags, 0xFF, + rayQueryInitializeEXT(rayQuery, topLevelAS, flags, mask, rayOrigin, tMin, rayDirection, TMax); rayQueryProceedAlphaTest(rayQuery); // rayQueryProceedAlphaTest(rayQuery, rngState); @@ -98,4 +98,8 @@ HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, return ret; } +HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, float mipOverride, inout Random rngState) { + return rayQueryProceedPrimary(rayOrigin, rayDirection, mipOverride, 0xFF, rngState); + } + #endif \ No newline at end of file diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index 8741604f1..db08a24d4 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -30,6 +30,13 @@ layout(binding = 4) uniform texture2D gbufNorm; layout(binding = 5) uniform texture2D gbufHit; layout(binding = 12) uniform sampler2D textureSm1; +struct GBuf { + vec3 diff; + vec3 norm; + float rayT; + float rayW; + }; + float shadowResolve(in vec4 sh, float z) { z = clamp(z,0,0.99); sh = step(sh,vec4(z)); @@ -76,19 +83,23 @@ float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { return (lamb * shadow); } -vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, HitResolve hit) { - vec3 thruput = min(textureAlbedo(hit.diff.rgb), 0.95); - float depth = hit.rayT; +vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, GBuf hit) { + vec3 thruput = vec3(1.0); vec3 color = vec3(0); + if(hit.rayW > 0) { + thruput *= waterTransmittance(hit.rayW); + } + if(hit.rayT==TMax) { vec3 sky = textureSkyLUT(skyLUT, vec3(0,RPlanet+max(rayOrigin.y*0.01,0),0), rayDirection, scene.sunDir) * scene.GSunIntensity; color += sky; color += (vec3(0.3, 0.26, 1)*0.15); //HACK for the night sky - return color; + return thruput*color; } - rayOrigin = (rayOrigin + rayDirection * hit.rayT); + thruput *= min(textureAlbedo(hit.diff.rgb), 0.95); + rayOrigin = (rayOrigin + rayDirection * hit.rayT); vec3 direct = vec3(0); direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir) * scene.sunColor; @@ -97,16 +108,19 @@ vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, HitResolve hit) { return color; } -HitResolve loadGBuf(uint ptr) { +GBuf loadGBuf(uint ptr) { uint x = ((ptr >> 0) & 0xFFF); uint y = ((ptr >> 12) & 0xFFF); ivec2 at = ivec2(x,y)*8 + ivec2(gl_LocalInvocationID.xy); + vec4 d = texelFetch(gbufDiff, at, 0); + vec4 n = texelFetch(gbufNorm, at, 0); - HitResolve hit; - hit.diff = texelFetch(gbufDiff, at, 0); - hit.norm = texelFetch(gbufNorm, at, 0).xyz*2.0-1.0; - hit.rayT = texelFetch(gbufHit, at, 0).x*TMax; + GBuf hit; + hit.diff = d.rgb; + hit.norm = n.xyz*2.0-1.0; + hit.rayT = texelFetch(gbufHit, at, 0).x * TMax; + hit.rayW = 0; //n.w * hit.rayT; return hit; } @@ -138,8 +152,8 @@ void main() { const uint i = gl_LocalInvocationIndex; const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); - const HitResolve hit = loadGBuf(p.payload); - const vec3 pt = evalLight(p.pos, ray, hit); + const GBuf hit = loadGBuf(p.payload); + const vec3 pt = evalLight(p.pos, ray, hit); hitCnt = 0; barrier(); diff --git a/shader/lighting/surfels/surf_pathtrace.comp b/shader/lighting/surfels/surf_pathtrace.comp index ea35ee3bb..52b668f36 100644 --- a/shader/lighting/surfels/surf_pathtrace.comp +++ b/shader/lighting/surfels/surf_pathtrace.comp @@ -100,9 +100,7 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { /* if(underWater) { - const float depth = hit.rayT / 5000.0; // 50 meters - const vec3 transmittance = exp(-depth * vec3(4,2,1) * 1.25); - thruput *= transmittance; + thruput *= waterTransmittance(hit.rayT); } if(hit.water) { diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index e62ca87bf..94c379422 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -1,6 +1,5 @@ #version 460 -#define LIGHTING 1 #define RAY_QUERY #define RAY_QUERY_AT @@ -39,12 +38,19 @@ mat3 toTangent(vec3 norm) { return tangent; } -HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { - HitResolve hit = rayQueryProceedPrimary(rayOrigin, rayDirection, -1, rngState); +HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, out float wHit) { + Random rngState = Random(0); // inactive + HitResolve hit = rayQueryProceedPrimary(rayOrigin, rayDirection, -1, rngState); if(hit.rayT<0) { hit.rayT = -hit.rayT; // backfaces are fine hit.norm = -hit.norm; } + if(hit.water) { + wHit = hit.rayT; + + const uint CM_Level = CM_Opaque | CM_Transparent; + hit = rayQueryProceedPrimary(rayOrigin, rayDirection, -1, CM_Level, rngState); + } if(hit.rayT==TMax) hit.diff = vec4(0,0,1,1); return hit; @@ -64,13 +70,15 @@ void reduceIrr() { } } -void storeGBuf(uint ptr, const HitResolve hit) { +void storeGBuf(uint ptr, const HitResolve hit, const float wHit) { if(ptr==0) return; //error + float wHitUNorm = (wHit / hit.rayT); + ivec2 at = unpackAtlassPos(ptr)*8 + ivec2(gl_LocalInvocationID.xy); imageStore(gbufDiff, at, vec4(hit.diff.rgb, 1)); - imageStore(gbufNorm, at, vec4(hit.norm.xyz*0.5+0.5, 1)); + imageStore(gbufNorm, at, vec4(hit.norm.xyz*0.5+0.5, wHitUNorm)); imageStore(gbufHit, at, vec4(hit.rayT/TMax)); } @@ -84,13 +92,12 @@ void main() { const Surfel p = surfels[surfelId]; const mat3 tbn = toTangent(decodeNormal(p.norm)); - Random rngState = Random(pcgHash(gl_LocalInvocationIndex)); - const uint i = gl_LocalInvocationIndex; const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); - const HitResolve hit = raycast(p.pos, ray, rngState); - storeGBuf(p.payload, hit); + float wHit = 0; + const HitResolve hit = raycast(p.pos, ray, wHit); + storeGBuf(p.payload, hit, wHit); hitCnt = 0; barrier(); diff --git a/shader/materials/main.frag b/shader/materials/main.frag index 10a84a477..f672893ac 100644 --- a/shader/materials/main.frag +++ b/shader/materials/main.frag @@ -268,8 +268,7 @@ vec4 underWaterColorDepth(vec3 normal) { } vec3 waterScatter(vec3 back, vec3 normal, float len) { - float depth = len / 5000.0; // 50 meters - vec3 transmittance = exp(-depth * vec3(4,2,1)*1.25); + vec3 transmittance = waterTransmittance(len); // note: less sun light and less obsevable light transmittance = transmittance*transmittance; diff --git a/shader/water/underwater.frag b/shader/water/underwater.frag index f8dd31862..cf6424dd0 100644 --- a/shader/water/underwater.frag +++ b/shader/water/underwater.frag @@ -14,8 +14,7 @@ layout(binding = 1) uniform sampler2D zbuffer; // fixme: copy-paste vec4 waterScatter(vec3 back, vec3 normal, const float len) { - const float depth = len / 5000.0; // 50 meters - const vec3 transmittance = exp(-depth * vec3(4,2,1) * 1.25); + const vec3 transmittance = waterTransmittance(len); #if defined(SCATTERING) const float f = fresnel(scene.sunDir,normal,IorWater); const vec3 scatter = f * scene.sunColor * (1-exp(-len/20000.0)) * scene.exposure; From 8911fce5cb698b5c32d14dbdc6605cf278e54b22 Mon Sep 17 00:00:00 2001 From: Try Date: Thu, 23 Jul 2026 21:13:25 +0200 Subject: [PATCH 22/29] fixup --- shader/lighting/surfels/surf_lighting.comp | 2 +- shader/lighting/surfels/surf_raycast.comp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index db08a24d4..6c00753f4 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -120,7 +120,7 @@ GBuf loadGBuf(uint ptr) { hit.diff = d.rgb; hit.norm = n.xyz*2.0-1.0; hit.rayT = texelFetch(gbufHit, at, 0).x * TMax; - hit.rayW = 0; //n.w * hit.rayT; + hit.rayW = n.w * hit.rayT; return hit; } diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index 94c379422..64ffd765a 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -38,7 +38,7 @@ mat3 toTangent(vec3 norm) { return tangent; } -HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, out float wHit) { +HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, inout float wHit) { Random rngState = Random(0); // inactive HitResolve hit = rayQueryProceedPrimary(rayOrigin, rayDirection, -1, rngState); if(hit.rayT<0) { From cd43214fe9b8d76dfd71755c19c24d90407ba3e2 Mon Sep 17 00:00:00 2001 From: Try Date: Thu, 23 Jul 2026 21:41:54 +0200 Subject: [PATCH 23/29] fixup water rays --- shader/lighting/pt/pathtrace_common.glsl | 5 ++++- shader/lighting/surfels/surf_raycast.comp | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/shader/lighting/pt/pathtrace_common.glsl b/shader/lighting/pt/pathtrace_common.glsl index 14d6b9eff..e338578eb 100644 --- a/shader/lighting/pt/pathtrace_common.glsl +++ b/shader/lighting/pt/pathtrace_common.glsl @@ -60,7 +60,10 @@ HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, // rayQueryProceedAlphaTest(rayQuery, rngState); if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) { HitResolve ret; - ret.rayT = TMax; + ret.rayT = TMax; + ret.diff = vec4(0); + ret.norm = vec3(0); + ret.water = false; return ret; } diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index 64ffd765a..4f6508ef2 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -46,7 +46,9 @@ HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, inout float wHit) { hit.norm = -hit.norm; } if(hit.water) { - wHit = hit.rayT; + //NOTE: probably need to trace a 'helper' ray from surfel + // to top/sun to determinate if surfel is underwater or now + wHit = hit.norm.y<0 ? hit.rayT : 0; // heuristic to track only underwater hits const uint CM_Level = CM_Opaque | CM_Transparent; hit = rayQueryProceedPrimary(rayOrigin, rayDirection, -1, CM_Level, rngState); @@ -74,7 +76,7 @@ void storeGBuf(uint ptr, const HitResolve hit, const float wHit) { if(ptr==0) return; //error - float wHitUNorm = (wHit / hit.rayT); + float wHitUNorm = (hit.rayT>0) ? (wHit / hit.rayT) : 0; ivec2 at = unpackAtlassPos(ptr)*8 + ivec2(gl_LocalInvocationID.xy); imageStore(gbufDiff, at, vec4(hit.diff.rgb, 1)); From 0718aef7a8524d670652d771b330e08943ea536e Mon Sep 17 00:00:00 2001 From: Try Date: Fri, 24 Jul 2026 01:07:45 +0200 Subject: [PATCH 24/29] fixup hit mean calculation --- shader/lighting/surfels/surf_lighting.comp | 11 ++++---- shader/lighting/surfels/surf_raycast.comp | 30 ---------------------- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index 6c00753f4..0c5127e33 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -125,7 +125,7 @@ GBuf loadGBuf(uint ptr) { return hit; } -shared vec3 reduction[NumThreads]; +shared vec4 reduction[NumThreads]; shared uint hitCnt; void reduceIrr() { @@ -158,16 +158,17 @@ void main() { hitCnt = 0; barrier(); - if(hit.rayT!=TMax) - atomicAdd(hitCnt, 1); + //atomicAdd(hitCnt, 1); - reduction[i] = vec3(pt.rgb); + float hitInv = 1.0/clamp(hit.rayT*0.4, 10.0, p.radius); + reduction[i] = vec4(pt.rgb, hitInv); barrier(); reduceIrr(); if(gl_LocalInvocationIndex==0) { - //NOTE: not updating harmonic mean - it's frozen after RT-pass + const float hitMean = float(numPaths)/max(reduction[0].w, eps); //harmonic mean surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); + surfels[surfelId].radiusMean = hitMean; } } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index 4f6508ef2..2cab7cd9e 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -58,20 +58,6 @@ HitResolve raycast(vec3 rayOrigin, vec3 rayDirection, inout float wHit) { return hit; } -shared float reduction[NumThreads]; -shared uint hitCnt; - -void reduceIrr() { - uint laneID = gl_LocalInvocationIndex; - - for(uint stride = NumThreads/2; stride > 0; stride >>= 1) { - if(laneID < stride) { - reduction[laneID] += reduction[laneID + stride]; - } - barrier(); - } - } - void storeGBuf(uint ptr, const HitResolve hit, const float wHit) { if(ptr==0) return; //error @@ -100,20 +86,4 @@ void main() { float wHit = 0; const HitResolve hit = raycast(p.pos, ray, wHit); storeGBuf(p.payload, hit, wHit); - - hitCnt = 0; - barrier(); - - if(hit.rayT!=TMax) - atomicAdd(hitCnt, 1); - - reduction[i] = 1.0/clamp(hit.rayT*0.4, 10.0, 1*p.radius); - barrier(); - - reduceIrr(); - - if(gl_LocalInvocationIndex==0) { - const float hitMean = float(numPaths)/max(reduction[0], eps); //harmonic mean - surfels[surfelId].radiusMean = hitMean; - } } \ No newline at end of file From a45bd4436bf48e5f5362cbbe5c3c79a42fa77900 Mon Sep 17 00:00:00 2001 From: Try Date: Sat, 25 Jul 2026 16:08:07 +0200 Subject: [PATCH 25/29] update hit mean calculation --- shader/lighting/surfels/surf_alloc.comp | 9 +-------- shader/lighting/surfels/surf_apply.comp | 7 +++++-- shader/lighting/surfels/surf_common.glsl | 16 ++++++++++++++- shader/lighting/surfels/surf_dbg.vert | 5 +---- shader/lighting/surfels/surf_lighting.comp | 23 +++++++++++++++------- shader/lighting/surfels/surf_update.comp | 13 +++--------- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp index 7a9d40960..a05b927b9 100644 --- a/shader/lighting/surfels/surf_alloc.comp +++ b/shader/lighting/surfels/surf_alloc.comp @@ -40,14 +40,7 @@ vec3 worldPos(float z, ivec2 fragCoord) { } float pixelToWorld(float pixelRadius, float z) { - z = linearDepth(z, scene.clipInfo); - - float clipRadiusX = (2.0 * pixelRadius) * scene.screenResInv.x; - float clipRadiusY = (2.0 * pixelRadius) * scene.screenResInv.y; - - float worldRadiusX = (clipRadiusX * z) / scene.project[0][0]; - float worldRadiusY = (clipRadiusY * z) / scene.project[1][1]; - return min(worldRadiusX, worldRadiusY); + return pixelToWorld(scene, pixelRadius, z); } float calculteWeight(const Candidate s, const vec3 wpos, const vec3 norm) { diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index f7da8d3dd..0ded3f71b 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -47,8 +47,11 @@ vec4 unpackHalf4x16(uvec2 packedVal) { } TSurfel packSurfel(const Surfel s) { - float rMax = min(s.radius, 65000); - float rEff = min(s.radiusMean, rMax*0.5); + // float rMax = min(s.radius, 65000); + // float rEff = min(s.radiusMean, rMax * MeanCoverageScale); + + float rMax = s.radius; + float rEff = s.radiusMean; TSurfel sx; sx.pos = vec4(s.pos, rEff); diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 05e1e4e29..2d3a9b339 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -2,6 +2,7 @@ #define SURF_COMMON_GLSL #include "common.glsl" +#include "scene.glsl" const float SKY_DEPTH = 0.999995; const int MinCoverage = 8; // in pixels @@ -9,6 +10,8 @@ const int DefaultCoverage = 96; // in pixels const int LargeTile = 128; // in pixels const uint MaxInTile = 1024; // ~32px (~6x6) per surfel +const float MeanCoverageScale = 0.75; + struct SurfHeader { uint count; uint one1; @@ -112,6 +115,17 @@ uint surfHash(vec3 pos, float cellSize, uint inorm) { #endif } +float pixelToWorld(const SceneDesc scene, float pixelRadius, float z) { + z = linearDepth(z, scene.clipInfo); + + float clipRadiusX = (2.0 * pixelRadius) * scene.screenResInv.x; + float clipRadiusY = (2.0 * pixelRadius) * scene.screenResInv.y; + + float worldRadiusX = (clipRadiusX * z) / scene.project[0][0]; + float worldRadiusY = (clipRadiusY * z) / scene.project[1][1]; + return min(worldRadiusX, worldRadiusY); + } + float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float rDiff, const vec3 wpos, const vec3 wnorm) { // An Approximate Global Illumination System for Computer Generated Films // https://www.tabellion.org/et/paper/siggraph_2004_gi_for_films.pdf @@ -134,7 +148,7 @@ float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, const vec3 wpos, const vec3 wnorm) { rMax = min(rMax, 65000); - rEff = min(rEff, rMax*0.5); + rEff = min(rEff, rMax*MeanCoverageScale); return calculteWeight(spos, snorm, rEff, rMax, rMax-rEff, wpos, wnorm); } diff --git a/shader/lighting/surfels/surf_dbg.vert b/shader/lighting/surfels/surf_dbg.vert index 7e6aa1fd4..4f69d7e37 100644 --- a/shader/lighting/surfels/surf_dbg.vert +++ b/shader/lighting/surfels/surf_dbg.vert @@ -42,10 +42,7 @@ vec3 hasGridPos(vec3 wpos, float cellSize) { } float pixelToWorld(float pixelRadius, float z) { - z = linearDepth(z, scene.clipInfo); - float clipRadius = (2.0 * pixelRadius) * scene.screenResInv.y; - float worldRadius = (clipRadius * z) / scene.project[1][1]; - return worldRadius; + return pixelToWorld(scene, pixelRadius, z); } void main() { diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index 0c5127e33..944e7072c 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -37,6 +37,9 @@ struct GBuf { float rayW; }; +shared vec4 reduction[NumThreads]; +shared uint hitCnt; + float shadowResolve(in vec4 sh, float z) { z = clamp(z,0,0.99); sh = step(sh,vec4(z)); @@ -64,6 +67,10 @@ mat3 toTangent(vec3 norm) { return tangent; } +float pixelToWorld(float pixelRadius, float z) { + return pixelToWorld(scene, pixelRadius, z); + } + float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { float lamb = max(dot(norm, rayDirection), 0); if(lamb==0) @@ -125,9 +132,6 @@ GBuf loadGBuf(uint ptr) { return hit; } -shared vec4 reduction[NumThreads]; -shared uint hitCnt; - void reduceIrr() { uint laneID = gl_LocalInvocationIndex; @@ -155,9 +159,8 @@ void main() { const GBuf hit = loadGBuf(p.payload); const vec3 pt = evalLight(p.pos, ray, hit); - hitCnt = 0; - barrier(); - + //hitCnt = 0; + //barrier(); //atomicAdd(hitCnt, 1); float hitInv = 1.0/clamp(hit.rayT*0.4, 10.0, p.radius); @@ -167,8 +170,14 @@ void main() { reduceIrr(); if(gl_LocalInvocationIndex==0) { + const vec4 pos4 = scene.viewProject * vec4(p.pos, 1.0); + const float rMin = pixelToWorld(MinCoverage, pos4.z/pos4.w); + const float rMax = min(pixelToWorld(DefaultCoverage, pos4.z/pos4.w), 65000); + const float hitMean = float(numPaths)/max(reduction[0].w, eps); //harmonic mean + surfels[surfelId].radius = rMax; + surfels[surfelId].radiusMean = min(max(hitMean, rMin), rMax*MeanCoverageScale); + surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); - surfels[surfelId].radiusMean = hitMean; } } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_update.comp b/shader/lighting/surfels/surf_update.comp index 7f216b2f5..135f0c433 100644 --- a/shader/lighting/surfels/surf_update.comp +++ b/shader/lighting/surfels/surf_update.comp @@ -12,16 +12,8 @@ layout(binding = 0, std140) uniform UboScene { layout(binding = 1) uniform texture2D depth; layout(binding = 2, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; - float pixelToWorld(float pixelRadius, float z) { - z = linearDepth(z, scene.clipInfo); - - float clipRadiusX = (2.0 * pixelRadius) * scene.screenResInv.x; - float clipRadiusY = (2.0 * pixelRadius) * scene.screenResInv.y; - - float worldRadiusX = (clipRadiusX * z) / scene.project[0][0]; - float worldRadiusY = (clipRadiusY * z) / scene.project[1][1]; - return min(worldRadiusX, worldRadiusY); + return pixelToWorld(scene, pixelRadius, z); } void main() { @@ -34,9 +26,10 @@ void main() { const vec4 pos4 = scene.viewProject * vec4(s.pos, 1.0); const vec2 posNdc = ((pos4.xy/pos4.w)*0.5+0.5); const ivec2 fragCoord = ivec2(posNdc*scene.screenRes + 0.5); + const float rMin = pixelToWorld(MinCoverage, pos4.z/pos4.w); const float rMax = pixelToWorld(DefaultCoverage, pos4.z/pos4.w); - const float radiusMean = max(rMin, min(s.radiusMean, rMax)); + const float radiusMean = max(rMin, min(s.radiusMean, rMax * MeanCoverageScale)); surfels[surfelId].fragCoord = fragCoord; surfels[surfelId].radius = rMax; From 37e7d6627a6bdd7d19921744ef0f541476c7a33b Mon Sep 17 00:00:00 2001 From: Try Date: Sat, 25 Jul 2026 18:25:12 +0200 Subject: [PATCH 26/29] revert rErrScale: 0.75 -> 0.5 --- shader/lighting/surfels/surf_common.glsl | 4 ++-- shader/lighting/surfels/surf_lighting.comp | 7 +------ shader/lighting/surfels/surf_update.comp | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 2d3a9b339..69efb440d 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -10,7 +10,7 @@ const int DefaultCoverage = 96; // in pixels const int LargeTile = 128; // in pixels const uint MaxInTile = 1024; // ~32px (~6x6) per surfel -const float MeanCoverageScale = 0.75; +const float rErrScale = 0.5; struct SurfHeader { uint count; @@ -148,7 +148,7 @@ float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, const vec3 wpos, const vec3 wnorm) { rMax = min(rMax, 65000); - rEff = min(rEff, rMax*MeanCoverageScale); + rEff = min(rEff, rMax*rErrScale); return calculteWeight(spos, snorm, rEff, rMax, rMax-rEff, wpos, wnorm); } diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index 944e7072c..284ffe3da 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -38,7 +38,6 @@ struct GBuf { }; shared vec4 reduction[NumThreads]; -shared uint hitCnt; float shadowResolve(in vec4 sh, float z) { z = clamp(z,0,0.99); @@ -159,10 +158,6 @@ void main() { const GBuf hit = loadGBuf(p.payload); const vec3 pt = evalLight(p.pos, ray, hit); - //hitCnt = 0; - //barrier(); - //atomicAdd(hitCnt, 1); - float hitInv = 1.0/clamp(hit.rayT*0.4, 10.0, p.radius); reduction[i] = vec4(pt.rgb, hitInv); barrier(); @@ -176,7 +171,7 @@ void main() { const float hitMean = float(numPaths)/max(reduction[0].w, eps); //harmonic mean surfels[surfelId].radius = rMax; - surfels[surfelId].radiusMean = min(max(hitMean, rMin), rMax*MeanCoverageScale); + surfels[surfelId].radiusMean = min(max(hitMean, rMin), rMax * rErrScale); surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); } diff --git a/shader/lighting/surfels/surf_update.comp b/shader/lighting/surfels/surf_update.comp index 135f0c433..6ee0bc2d9 100644 --- a/shader/lighting/surfels/surf_update.comp +++ b/shader/lighting/surfels/surf_update.comp @@ -29,7 +29,7 @@ void main() { const float rMin = pixelToWorld(MinCoverage, pos4.z/pos4.w); const float rMax = pixelToWorld(DefaultCoverage, pos4.z/pos4.w); - const float radiusMean = max(rMin, min(s.radiusMean, rMax * MeanCoverageScale)); + const float radiusMean = max(rMin, min(s.radiusMean, rMax * rErrScale)); surfels[surfelId].fragCoord = fragCoord; surfels[surfelId].radius = rMax; From 3f96ee4ed099e00ce774347323ff427feeb5080f Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 26 Jul 2026 00:31:13 +0200 Subject: [PATCH 27/29] rework surfel GC --- game/graphics/renderer.cpp | 44 ++++++++++----------- shader/lighting/surfels/surf_alloc.comp | 3 -- shader/lighting/surfels/surf_bin_pass.comp | 2 +- shader/lighting/surfels/surf_common.glsl | 8 +++- shader/lighting/surfels/surf_compact.comp | 2 +- shader/lighting/surfels/surf_culling.comp | 30 +++----------- shader/lighting/surfels/surf_dbg.frag | 5 ++- shader/lighting/surfels/surf_decimate.comp | 6 +-- shader/lighting/surfels/surf_lighting.comp | 7 +++- shader/lighting/surfels/surf_pathtrace.comp | 12 +----- shader/lighting/surfels/surf_raycast.comp | 18 ++++++--- shader/lighting/surfels/surf_update.comp | 5 ++- 12 files changed, 65 insertions(+), 77 deletions(-) diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index 74915e386..de192382c 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -2002,37 +2002,20 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor cmd.setPipeline(shaders.surfUpdate); cmd.dispatchThreads(maxSurfels); - } - - static bool gc = true; - if(gc) { - surfelsBinning(cmd, wview, TileSize, false); cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(1, hiz.hiZ); cmd.setBinding(2, zbuffer); cmd.setBinding(3, surfels); cmd.setBinding(4, surf.gbuffFree); - // - cmd.setBinding(6, surfCnts); - cmd.setBinding(7, surfBins); - cmd.setBinding(8, surfList); - cmd.setPushData(scene.znear); - cmd.setPipeline(shaders.surfCulling); - cmd.dispatchThreads(maxSurfels); - - //cmd.setPushData(push); - //cmd.setPipeline(shaders.surfBinSort); // assist with stable GC - //cmd.dispatch(surfBins.size()); - cmd.setPushData(push); - cmd.setPipeline(shaders.surfDecimate); + cmd.setPipeline(shaders.surfCulling); cmd.dispatchThreads(maxSurfels); cmd.setPipeline(shaders.surfCompact); cmd.dispatch(1); - } + } static bool rays = true; if(rays) { @@ -2055,15 +2038,32 @@ void Renderer::prepareSurfels(Tempest::Encoder& cmd, Wor cmd.setBinding(3, zbuffer); cmd.setBinding(4, surfels); cmd.setBinding(5, surf.gbuffFree); - cmd.setBinding(6, surfCnts); - cmd.setBinding(7, surfBins); - cmd.setBinding(8, surfList); const auto tc = tileCount(zbuffer.size(), LargeTile); cmd.setPipeline(shaders.surfAlloc); cmd.dispatch(tc); } + static bool gc = true; + if(gc) { + surfelsBinning(cmd, wview, TileSize, false); + + cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(3, surfels); + // + cmd.setBinding(6, surfCnts); + cmd.setBinding(7, surfBins); + cmd.setBinding(8, surfList); + + //cmd.setPushData(push); + //cmd.setPipeline(shaders.surfBinSort); // assist with stable GC + //cmd.dispatch(surfBins.size()); + + cmd.setPushData(push); + cmd.setPipeline(shaders.surfDecimate); + cmd.dispatchThreads(maxSurfels); + } + if(rays) { surfelsTrace(cmd, wview, surfels, true); } diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp index a05b927b9..efc82cca3 100644 --- a/shader/lighting/surfels/surf_alloc.comp +++ b/shader/lighting/surfels/surf_alloc.comp @@ -23,9 +23,6 @@ layout(binding = 2) uniform utexture2D gbufNormal; layout(binding = 3) uniform texture2D depth; layout(binding = 4, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; } surf; layout(binding = 5, std430) buffer SB1 { uint count; uint ids[]; } freelist; -layout(binding = 6, r32ui) uniform uimage2D surfCnts; -layout(binding = 7, r32ui) uniform uimage2D surfBins; -layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; shared Candidate pointQueue[MaxQueue]; shared int pointQueueSize; diff --git a/shader/lighting/surfels/surf_bin_pass.comp b/shader/lighting/surfels/surf_bin_pass.comp index 5609cf938..e1f68c6c3 100644 --- a/shader/lighting/surfels/surf_bin_pass.comp +++ b/shader/lighting/surfels/surf_bin_pass.comp @@ -32,7 +32,7 @@ void main() { const ivec2 fragCoord = p.fragCoord; const int radiusPix = DefaultCoverage; - if(p.radius==0) + if(!isSurfelAlive(p)) return; ivec2 pos0 = ivec2(fragCoord-radiusPix)/tileSize; diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 69efb440d..c1944712c 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -10,7 +10,7 @@ const int DefaultCoverage = 96; // in pixels const int LargeTile = 128; // in pixels const uint MaxInTile = 1024; // ~32px (~6x6) per surfel -const float rErrScale = 0.5; +const float rEffScale = 0.5; struct SurfHeader { uint count; @@ -44,6 +44,10 @@ ivec2 unpackAtlassPos(uint ptr) { return ivec2(x, y); } +bool isSurfelAlive(const Surfel s) { + return s.radius >= 0; + } + bool isSurfelVisible(const Surfel s, ivec2 bboxMin, ivec2 bboxMax) { const ivec2 at = s.fragCoord; const int radius = DefaultCoverage; @@ -148,7 +152,7 @@ float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, const vec3 wpos, const vec3 wnorm) { rMax = min(rMax, 65000); - rEff = min(rEff, rMax*rErrScale); + rEff = min(rEff, rMax*rEffScale); return calculteWeight(spos, snorm, rEff, rMax, rMax-rEff, wpos, wnorm); } diff --git a/shader/lighting/surfels/surf_compact.comp b/shader/lighting/surfels/surf_compact.comp index d59ccd3f7..373cff50e 100644 --- a/shader/lighting/surfels/surf_compact.comp +++ b/shader/lighting/surfels/surf_compact.comp @@ -31,7 +31,7 @@ void main() { Surfel sx; for(uint ix=0; ix0; + const bool keep = i p.w) { - // TODO: two-sided HiZ - return false; - } - /* - const vec3 dp = unproject(fragCoord) - viewPos; - if(dot(dp,dp) > p.w*p.w) { - // TODO: two-sided HiZ - return false; - } - */ - } -#endif - #if 1 vec4 aabb = vec4(0); float depthMin=0, depthMax=0; - if(!projectSphere(viewPos, p.w, aabb, depthMin, depthMax)) + if(!projectSphere(viewPos, radius, aabb, depthMin, depthMax)) return false; + // TODO: two-sided HiZ if(!hiZTest(aabb, depthMin)) return false; #endif @@ -146,8 +128,8 @@ void main() { return; const Surfel s = surfels[surfelId]; - if(isSurfelVisible(vec4(s.pos, s.radius), s.radiusMean, s.fragCoord)) + if(isSurfelVisible(s.pos, s.radiusMean, s.radius, s.fragCoord)) return; - surfels[surfelId].radius = 0; + surfels[surfelId].radius = -1; } diff --git a/shader/lighting/surfels/surf_dbg.frag b/shader/lighting/surfels/surf_dbg.frag index 8c36473fd..5022b2dd2 100644 --- a/shader/lighting/surfels/surf_dbg.frag +++ b/shader/lighting/surfels/surf_dbg.frag @@ -44,10 +44,11 @@ void main(void) { ;//discard; //vec3 clr = surfDebugColor(p, instanceIndex) * (1.0-qDist); - //vec3 clr = 3.0 * p.irradiance * (1.0-qDist) * scene.exposure; - vec3 clr = p.irradiance * (1.0-qDist); + vec3 clr = 4.0 * p.irradiance * (1.0-qDist) * scene.exposure; if(instanceIndex >= header.count-header.added) clr = vec3(0,1,0); + if(!isSurfelAlive(p)) + clr = vec3(1,0,0); outColor = vec4(clr,1.0); // outColor = vec4(1,0,0,1.0); } diff --git a/shader/lighting/surfels/surf_decimate.comp b/shader/lighting/surfels/surf_decimate.comp index 7a2d88528..085310047 100644 --- a/shader/lighting/surfels/surf_decimate.comp +++ b/shader/lighting/surfels/surf_decimate.comp @@ -31,7 +31,7 @@ void main() { const Surfel p = surfels[surfelId]; const ivec2 sz = scene.screenRes; - if(p.radius==0) + if(!isSurfelAlive(p)) return; // disabled #if 0 @@ -53,7 +53,7 @@ void main() { /* if(cnt>1024) { // too many - surfels[surfelId].radius = 0; + surfels[surfelId].radius = -1; return; } */ @@ -77,7 +77,7 @@ void main() { if(weight>edge) { // too dense - can be removed - surfels[surfelId].radius = 0; + surfels[surfelId].radius = -1; return; } } diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index 284ffe3da..5fd974f6c 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -148,6 +148,11 @@ void main() { const uint surfelId = pass==0 ? gl_WorkGroupID.x : (header.count-gl_WorkGroupID.z-1); const Surfel p = surfels[surfelId]; + if(!isSurfelAlive(p)) { + surfels[surfelId].irradiance = vec3(1,1,0) / scene.exposure; + return; + } + const mat3 tbn = toTangent(decodeNormal(p.norm)); Random rngState = Random(pcgHash(gl_LocalInvocationIndex)); @@ -171,7 +176,7 @@ void main() { const float hitMean = float(numPaths)/max(reduction[0].w, eps); //harmonic mean surfels[surfelId].radius = rMax; - surfels[surfelId].radiusMean = min(max(hitMean, rMin), rMax * rErrScale); + surfels[surfelId].radiusMean = min(max(hitMean, rMin), rMax * rEffScale); surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); } diff --git a/shader/lighting/surfels/surf_pathtrace.comp b/shader/lighting/surfels/surf_pathtrace.comp index 52b668f36..a0db0e041 100644 --- a/shader/lighting/surfels/surf_pathtrace.comp +++ b/shader/lighting/surfels/surf_pathtrace.comp @@ -136,7 +136,6 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { } shared vec4 reduction[NumThreads]; -shared uint hitCnt; void reduceIrr() { uint laneID = gl_LocalInvocationIndex; @@ -171,16 +170,10 @@ void main() { // const vec3 ray = randCosWeightedHemisphereDirection(decodeNormal(p.norm), rngState); const vec4 pt = pathtrace(p.pos, ray, rngState); - hitCnt = 0; - barrier(); - - if(pt.a!=TMax) - atomicAdd(hitCnt, 1); - #if defined(USE_MIN_HIT_DIST) reduction[i] = vec4(pt.rgb, pt.a); #else - reduction[i] = vec4(pt.rgb, 1.0/clamp(pt.a*0.4, 10.0, 1*p.radius)); + reduction[i] = vec4(pt.rgb, 1.0/clamp(pt.a*0.4, 10.0, p.radius)); #endif barrier(); @@ -190,9 +183,6 @@ void main() { #if defined(USE_MIN_HIT_DIST) const float hitMin = max(4, reduction[0].a); surfels[surfelId].radiusMean = hitMin; -#elif 0 - const float hitMean = float(hitCnt)/max(reduction[0].a, eps); //harmonic mean - surfels[surfelId].radiusMean = hitCnt > 0 ? hitMean : p.radius; #else const float hitMean = float(numPaths)/max(reduction[0].a, eps); //harmonic mean surfels[surfelId].radiusMean = hitMean; diff --git a/shader/lighting/surfels/surf_raycast.comp b/shader/lighting/surfels/surf_raycast.comp index 2cab7cd9e..47d49383a 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -14,7 +14,8 @@ layout(local_size_x = 8, local_size_y = 8) in; -const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; +const float NormalBias = 0.0015; layout(std140, push_constant) uniform Push { uint pass; @@ -77,13 +78,18 @@ void main() { // if(surfelId>=header.count) // return; - const Surfel p = surfels[surfelId]; - const mat3 tbn = toTangent(decodeNormal(p.norm)); + const Surfel p = surfels[surfelId]; + if(!isSurfelAlive(p)) + return; - const uint i = gl_LocalInvocationIndex; - const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); + const vec3 norm = decodeNormal(p.norm); + const mat3 tbn = toTangent(norm); + const vec3 pos = p.pos; + + const uint i = gl_LocalInvocationIndex; + const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); float wHit = 0; - const HitResolve hit = raycast(p.pos, ray, wHit); + const HitResolve hit = raycast(pos, ray, wHit); storeGBuf(p.payload, hit, wHit); } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_update.comp b/shader/lighting/surfels/surf_update.comp index 6ee0bc2d9..fc1eb32d1 100644 --- a/shader/lighting/surfels/surf_update.comp +++ b/shader/lighting/surfels/surf_update.comp @@ -23,13 +23,16 @@ void main() { const Surfel s = surfels[surfelId]; + if(!isSurfelAlive(s)) + return; // disabled + const vec4 pos4 = scene.viewProject * vec4(s.pos, 1.0); const vec2 posNdc = ((pos4.xy/pos4.w)*0.5+0.5); const ivec2 fragCoord = ivec2(posNdc*scene.screenRes + 0.5); const float rMin = pixelToWorld(MinCoverage, pos4.z/pos4.w); const float rMax = pixelToWorld(DefaultCoverage, pos4.z/pos4.w); - const float radiusMean = max(rMin, min(s.radiusMean, rMax * rErrScale)); + const float radiusMean = max(rMin, min(s.radiusMean, rMax * rEffScale)); surfels[surfelId].fragCoord = fragCoord; surfels[surfelId].radius = rMax; From cc5c976c39c335bc21fb277a02106b7778cdd01e Mon Sep 17 00:00:00 2001 From: Try Date: Mon, 27 Jul 2026 00:44:51 +0200 Subject: [PATCH 28/29] experimenting with binning pass --- shader/lighting/surfels/surf_alloc.comp | 2 +- shader/lighting/surfels/surf_bin_pass.comp | 49 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp index efc82cca3..73f8ef3db 100644 --- a/shader/lighting/surfels/surf_alloc.comp +++ b/shader/lighting/surfels/surf_alloc.comp @@ -102,7 +102,7 @@ void storeSurfels() { sx.norm = encodeNormal(p.norm.xyz); sx.fragCoord = fragCoord; sx.radius = p.pos.w; - sx.radiusMean = p.pos.w * 2; + sx.radiusMean = p.pos.w * rEffScale; sx.irradiance = debugColors[(gl_WorkGroupID.x*32+gl_WorkGroupID.y)%debugColors.length()] / scene.exposure; sx.payload = allocPayload(); surf.surfels[ptr] = sx; diff --git a/shader/lighting/surfels/surf_bin_pass.comp b/shader/lighting/surfels/surf_bin_pass.comp index e1f68c6c3..d67c645df 100644 --- a/shader/lighting/surfels/surf_bin_pass.comp +++ b/shader/lighting/surfels/surf_bin_pass.comp @@ -21,6 +21,49 @@ layout(binding = 3, r32ui) uniform uimage2D surfCnts; layout(binding = 4, r32ui) uniform readonly uimage2D surfBins; layout(binding = 5, std430) buffer SB2 { uint surfelIds[]; }; +vec3 mkTangent(vec3 norm) { + // Pick axis least parallel to n + vec3 up = abs(norm.z) < 0.999f ? vec3(0.0f, 0.0f, 1.0f) : vec3(1.0f, 0.0f, 0.0f); + return normalize(cross(norm, up)); + } + +ivec4 projectSurfel(const Surfel s) { + vec3 n = decodeNormal(s.norm); + vec3 c = s.pos; + float r = s.radius; + + vec3 t = mkTangent(n); + vec3 bt = cross(n, t); + + vec2 mn = vec2( 100); + vec2 mx = vec2(-100); + + // center + ellipse axes + vec3 points[5]; + points[0] = c; + points[1] = c + t * r; + points[2] = c - t * r; + points[3] = c + bt * r; + points[4] = c - bt * r; + + for(int i=0; i pixel coordinates + vec2 screen = scene.screenRes; + ivec2 minXY = ivec2((mn * 0.5 + 0.5) * screen); + ivec2 maxXY = ivec2((mx * 0.5 + 0.5) * screen); + + return ivec4(minXY, maxXY); + } + void main() { const uint surfelId = gl_GlobalInvocationID.x; if(surfelId>=surf.header.count) @@ -35,8 +78,14 @@ void main() { if(!isSurfelAlive(p)) return; +#if 0 ivec2 pos0 = ivec2(fragCoord-radiusPix)/tileSize; ivec2 pos1 = ivec2(fragCoord+radiusPix)/tileSize + 1; +#else + ivec4 aabb = projectSurfel(p); + ivec2 pos0 = ivec2(aabb.xy)/tileSize; + ivec2 pos1 = ivec2(aabb.zw)/tileSize + 1; +#endif ivec2 sz = imageSize(surfCnts); pos0 = max(ivec2(0), pos0); From c24fc445562ecf48c12eed2949e4866b8077e18b Mon Sep 17 00:00:00 2001 From: Try Date: Mon, 27 Jul 2026 01:09:03 +0200 Subject: [PATCH 29/29] optimize weight calculation --- shader/lighting/surfels/surf_apply.comp | 2 +- shader/lighting/surfels/surf_common.glsl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 0ded3f71b..6d0f6851a 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -56,7 +56,7 @@ TSurfel packSurfel(const Surfel s) { TSurfel sx; sx.pos = vec4(s.pos, rEff); sx.norm = packHalf4x16(vec4(decodeNormal(s.norm), rMax)); - sx.irr = packHalf4x16(vec4(s.irradiance, rMax-rEff)); + sx.irr = packHalf4x16(vec4(s.irradiance, 1.0/(rMax-rEff))); return sx; } diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index c1944712c..86ae10e9e 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -130,7 +130,7 @@ float pixelToWorld(const SceneDesc scene, float pixelRadius, float z) { return min(worldRadiusX, worldRadiusY); } -float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float rDiff, const vec3 wpos, const vec3 wnorm) { +float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float rDiffInv, const vec3 wpos, const vec3 wnorm) { // An Approximate Global Illumination System for Computer Generated Films // https://www.tabellion.org/et/paper/siggraph_2004_gi_for_films.pdf // https://cgg.mff.cuni.cz/~jaroslav/papers/2008-irradiance_caching_class/03-greg-ic.pdf @@ -143,9 +143,9 @@ float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, dist = max(dist, 0.0001); // Wendland C2 inspired falloff - float q = max(min(dist,rMax)-rEff, 0)/rDiff; + float q = max(min(dist,rMax)-rEff, 0)*rDiffInv; float wPos = pow(1-q, 4.0)*(4.0*q + 1.0); - float wNorm = pow(max(dotN, 0.0), 2.0); + float wNorm = dotN * dotN; float wOccl = 1.0 - max(dot(ldir, snorm)/dist, 0.0); return wPos * wNorm * wOccl; } @@ -153,7 +153,7 @@ float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, const vec3 wpos, const vec3 wnorm) { rMax = min(rMax, 65000); rEff = min(rEff, rMax*rEffScale); - return calculteWeight(spos, snorm, rEff, rMax, rMax-rEff, wpos, wnorm); + return calculteWeight(spos, snorm, rEff, rMax, 1.0/(rMax-rEff), wpos, wnorm); } vec3 surfDebugColor(Surfel s, uint sId) {