diff --git a/game/commandline.cpp b/game/commandline.cpp index eda2039ba..c59b2ed79 100644 --- a/game/commandline.cpp +++ b/game/commandline.cpp @@ -40,6 +40,12 @@ static bool boolArg(std::string_view v) { return std::string_view(v)!="0" && std::string_view(v)!="false"; } +static int intArg(std::string_view v) { + if(v=="false") + return 0; + return (std::stoi(std::string(v))); + } + CommandLine::CommandLine(int argc, const char** argv) { instance = this; if(argc<1) @@ -114,8 +120,8 @@ CommandLine::CommandLine(int argc, const char** argv) { ++i; if(i0; - settings.swrtEnabled = Gothic::options().doSoftwareRT; + settings.giMethod = Gothic::options().doGi; + settings.vsmEnabled = Gothic::options().doVirtualShadow; + settings.rtsmEnabled = Gothic::options().doSoftwareShadow; + settings.swrEnabled = Gothic::options().swRenderingPreset>0; + settings.swrtEnabled = Gothic::options().doSoftwareRT; sky.cloudsLut = device.image2d (sky.lutRGBAFormat, 2, 1); sky.transLut = device.attachment(sky.lutRGBFormat, 256, 64); @@ -193,7 +193,7 @@ void Renderer::setupSettings() { auto prevVidResIndex = settings.vidResIndex; settings.vidResIndex = Gothic::inst().settingsGetF("INTERNAL","vidResIndex"); - settings.aaEnabled = (Gothic::options().aaPreset>0) && (settings.vidResIndex==0); + settings.aaEnabled = (Gothic::options().aaPreset>0) && (settings.vidResIndex==0); // direct lighting if(settings.rtsmEnabled) @@ -216,9 +216,24 @@ void Renderer::setupSettings() { else lights.directLightPso = &shaders.lights; + const auto gi = settings.giMethod; + if(gi==GiMethod::Probes && Shaders::isGi1Supported() && Gothic::options().doRayQuery) { + settings.giMethod = GiMethod::Probes; + } + else if(gi==GiMethod::IrrC && Shaders::isGi2Supported() && Gothic::options().doRayQuery) { + settings.giMethod = GiMethod::IrrC; + } + else { + settings.giMethod = GiMethod::None; + } + if(prevVidResIndex!=settings.vidResIndex) { resetSwapchain(); } + if(settings.giMethod!=GiMethod::None) { + // need a projective shadow, for gi + resetShadowmap(); + } resetSkyFog(); resetShadowmap(); @@ -230,21 +245,15 @@ void Renderer::toggleGi() { if(!Gothic::options().doRayQuery) return; - auto prop = device.properties(); - if(prop.tex2d.maxSize<4096 || !prop.hasStorageFormat(R11G11B10UF) || !prop.hasStorageFormat(R16)) - return; - - settings.giEnabled = !settings.giEnabled; + if(settings.giMethod==GiMethod::None && Gothic::options().doGi!=GiMethod::None) + settings.giMethod = Gothic::options().doGi; + else if(settings.giMethod==GiMethod::None && Shaders::isGi1Supported()) + settings.giMethod = GiMethod::Probes; // default for now + else + settings.giMethod = GiMethod::None; device.waitIdle(); - if(auto wview = Gothic::inst().worldView()) { - wview->resetRendering(); - } - if(settings.giEnabled) { - // need a projective shadow, for gi - resetShadowmap(); - } - prepareUniforms(); + setupSettings(); } void Renderer::toggleVsm() { @@ -325,7 +334,7 @@ bool Renderer::requiresTlas() const { if(!Gothic::options().doRayQuery) return false; - if(settings.giEnabled || settings.pathTraceEnabled) + if(settings.giMethod!=GiMethod::None || settings.pathTraceEnabled) return true; if(!(settings.rtsmEnabled || settings.vsmEnabled)) return true; @@ -372,6 +381,14 @@ StorageBuffer& Renderer::usesSsbo(Tempest::StorageBuffer& ret, size_t size) { return ret; } +StorageBuffer& Renderer::usesSsboInit(Tempest::StorageBuffer& ret, size_t size) { + if(ret.byteSize()==size) + return ret; + Resources::recycle(std::move(ret)); + ret = Resources::device().ssbo(nullptr, size); + return ret; + } + StorageBuffer& Renderer::usesScratch(Tempest::StorageBuffer& ret, size_t size) { if(ret.byteSize()>=size) return ret; @@ -404,7 +421,7 @@ void Renderer::resetShadowmap() { for(int i=0; i(shadowMap[0])); //tex.push_back(&textureCast(vsm.pageData)); - tex.push_back(&textureCast(swrt.outputImage)); + //tex.push_back(&textureCast(swrt.outputImage)); + tex.push_back(&textureCast(surf.dbgImage)); + //tex.push_back(&textureCast(surf.irrImage)); static int size = 400; int left = 10; @@ -653,6 +672,7 @@ void Renderer::draw(Tempest::Attachment& result, Encoder& cmd, ui prepareSSAO(cmd,*wview); prepareFog (cmd,*wview); prepareGi (cmd,*wview); + prepareSurfels(cmd,*wview); cmd.setFramebuffer({{sceneLinear, Tempest::Discard, Tempest::Preserve}}, {zbuffer, Tempest::Readonly}); drawShadowResolve(cmd,*wview); @@ -670,8 +690,10 @@ void Renderer::draw(Tempest::Attachment& result, Encoder& cmd, ui cmd.setDebugMarker("Translucent"); wview->drawTranslucent(cmd, fId); + //drawHashDbg(sceneLinear, cmd, *wview); drawProbesDbg(cmd, *wview); drawProbesHitDbg(cmd); + drawSurfelsDbg(cmd, *wview); drawVsmDbg(cmd, *wview); drawSwrDbg(cmd, *wview); drawRtsmDbg(cmd, *wview); @@ -696,6 +718,7 @@ void Renderer::draw(Tempest::Attachment& result, Encoder& cmd, ui } //drawRayQueryDbg(cmd, *wview); + //drawHashDbg(result, cmd, *wview); wview->postFrameupdate(); } @@ -989,6 +1012,29 @@ void Renderer::drawRtsmDbg(Tempest::Encoder& cmd, const cmd.draw(nullptr, 0, 3); } +void Renderer::drawHashDbg(Attachment& result, Tempest::Encoder& cmd, const WorldView& wview) { + static bool enable = true; + if(!enable) + return; + + auto& scene = wview.sceneGlobals(); + + struct Push { + Vec3 originLwc; + } push; + push.originLwc = scene.originLwc; + + cmd.setDebugMarker("Hash-dbg"); + cmd.setPushData(push); + cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(2, gbufNormal); + cmd.setBinding(3, zbuffer); + + cmd.setFramebuffer({{result, Tempest::Preserve, Tempest::Preserve}}); + cmd.setPipeline(shaders.hashDbg); + cmd.draw(nullptr, 0, 3); + } + void Renderer::drawHiZ(Encoder& cmd, WorldView& view) { cmd.setDebugMarker("HiZ-occluders"); cmd.setFramebuffer({}, {zbuffer, 1.f, Tempest::Preserve}); @@ -1899,6 +1945,215 @@ void Renderer::prepareEpipolar(Tempest::Encoder& cmd, Wo cmd.dispatch(uint32_t(epTrace.h())); } +void Renderer::prepareSurfels(Tempest::Encoder& cmd, WorldView& wview) { + static bool enable = true; + if(!enable) + return; + + 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); + + 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& 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; + uint32_t tileSize; + uint32_t pass; + } push = {}; + push.originLwc = scene.originLwc; + push.tileSize = uint32_t(tileSize); + push.pass = 0; + + cmd.setDebugMarker("Surfels"); + + // prev-frame + { + cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(1, zbuffer); + cmd.setBinding(2, surfels); + + 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(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.setPipeline(shaders.surfDecimate); + cmd.dispatchThreads(maxSurfels); + + cmd.setPipeline(shaders.surfCompact); + cmd.dispatch(1); + } + + static bool rays = true; + if(rays) { + surfelsTrace(cmd, wview, surfels, false); + } + + 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()); + } + + // current-frame + static bool alloc = true; + if(alloc) { + 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); + + const auto tc = tileCount(zbuffer.size(), 128); + cmd.setPipeline(shaders.surfAlloc); + cmd.dispatch(tc); + } + + if(rays) { + surfelsTrace(cmd, wview, surfels, true); + } + + if(apply) { + surfelsBinning(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); + + cmd.setPipeline(shaders.surfApply); + cmd.dispatchThreads(zbuffer.size()); + } + } + +void Renderer::surfelsBinning(Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass) { + const uint32_t maxSurfels = surf.maxSurfels; + + auto& scene = wview.sceneGlobals(); + auto& surfels = surf.surfels; + auto& binCtrl = usesSsboInit(surf.surfBinsCtrl, sizeof(uint32_t)); + auto& surfCnts = surf.surfCnts; + auto& surfBins = surf.surfBins; + auto& surfList = surf.surfList; + + struct Push { + int32_t tileSize; + int32_t allocPass; + int32_t postPass; + } push = {}; + push.tileSize = tileSize; + push.postPass = postPass ? 1 : 0; + + cmd.setPushData(push); + cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(1, binCtrl); + cmd.setBinding(2, surfels); + cmd.setBinding(3, surfCnts); + cmd.setBinding(4, surfBins); + cmd.setBinding(5, surfList); + + cmd.setPipeline(shaders.surfBinClear); + cmd.dispatchThreads(surfBins.size()); + + push.allocPass = 0; + cmd.setPushData(push); + cmd.setPipeline(shaders.surfBinPass); + cmd.dispatchThreads(maxSurfels); //TODO: indirect + + cmd.setPipeline(shaders.surfBinAlloc); + cmd.dispatchThreads(surfBins.size()); + + push.allocPass = 1; + cmd.setPushData(push); + cmd.setPipeline(shaders.surfBinPass); + cmd.dispatchThreads(maxSurfels); + } + +void Renderer::surfelsTrace(Tempest::Encoder& cmd, WorldView& wview, StorageBuffer& surfels, bool postPass) { + auto& scene = wview.sceneGlobals(); + + 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(6, scene.rtScene.tlas); + cmd.setBinding(7, Sampler::trillinear()); + cmd.setBinding(8, scene.rtScene.tex); + cmd.setBinding(9, scene.rtScene.vbo); + cmd.setBinding(10,scene.rtScene.ibo); + cmd.setBinding(11,scene.rtScene.rtDesc); + + cmd.setPipeline(shaders.surfPathtrace); + //cmd.dispatchThreads(maxSurfels); + const uint32_t offset = postPass ? sizeof(uint32_t) : 0; + cmd.dispatchIndirect(surfels, offset); + } + void Renderer::prepareIrradiance(Encoder& cmd, WorldView& wview) { auto& scene = wview.sceneGlobals(); @@ -1912,7 +2167,7 @@ void Renderer::prepareIrradiance(Encoder& cmd, WorldView& wview) } void Renderer::prepareGi(Encoder& cmd, WorldView& wview) { - if(!settings.giEnabled || !settings.zCloudShadowScale) { + if(settings.giMethod!=GiMethod::Probes || !settings.zCloudShadowScale) { return; } @@ -2179,8 +2434,25 @@ void Renderer::drawRayQueryDbg(Tempest::Encoder& cmd, co cmd.draw(nullptr, 0, 3); } +void Renderer::drawSurfelsDbg(Encoder& cmd, const WorldView& wview) { + if(settings.giMethod!=GiMethod::IrrC) + return; + + static bool enable = false; + if(!enable) + return; + + cmd.setDebugMarker("GI-dbg"); + cmd.setBinding(0, wview.sceneGlobals().uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(1, surf.surfels); + cmd.setBinding(2, gbufNormal, Sampler::nearest()); + cmd.setBinding(3, sceneDepth, Sampler::nearest()); + cmd.setPipeline(shaders.surfDbg); + cmd.draw(nullptr, 0, 36, 0, surf.maxSurfels); + } + void Renderer::drawProbesDbg(Encoder& cmd, const WorldView& wview) { - if(!settings.giEnabled) + if(settings.giMethod!=GiMethod::Probes) return; static bool enable = false; @@ -2197,7 +2469,7 @@ void Renderer::drawProbesDbg(Encoder& cmd, const WorldView& wview } void Renderer::drawProbesHitDbg(Encoder& cmd) { - if(!settings.giEnabled) + if(settings.giMethod!=GiMethod::Probes) return; static bool enable = false; @@ -2219,30 +2491,31 @@ void Renderer::drawAmbient(Encoder& cmd, const WorldView& view) { if(!enable) return; - if(settings.giEnabled && settings.zCloudShadowScale) { - cmd.setDebugMarker("AmbientLight"); - cmd.setBinding(0, view.sceneGlobals().uboGlobal[SceneGlobals::V_Main]); - cmd.setBinding(1, gi.probesLighting); - cmd.setBinding(2, gbufDiffuse, Sampler::nearest()); - cmd.setBinding(3, gbufNormal, Sampler::nearest()); - cmd.setBinding(4, zbuffer, Sampler::nearest()); - cmd.setBinding(5, ssao.ssaoBlur, Sampler::nearest()); - cmd.setBinding(6, gi.hashTable); - cmd.setBinding(7, gi.probes); - cmd.setPipeline(shaders.probeAmbient); - cmd.draw(nullptr, 0, 3); - return; - } - cmd.setDebugMarker("AmbientLight"); cmd.setBinding(0, view.sceneGlobals().uboGlobal[SceneGlobals::V_Main]); cmd.setBinding(1, gbufDiffuse, Sampler::nearest()); cmd.setBinding(2, gbufNormal, Sampler::nearest()); - cmd.setBinding(3, sky.irradianceLut); - if(settings.zCloudShadowScale) { + if(settings.giMethod==GiMethod::IrrC && settings.zCloudShadowScale) { + cmd.setBinding(3, surf.irrImage, Sampler::nearest(ClampMode::ClampToEdge)); + cmd.setBinding(4, ssao.ssaoBlur, Sampler::nearest(ClampMode::ClampToEdge)); //TODO: remove once splatting is working + cmd.setBinding(5, surf.surfels); + cmd.setPipeline(shaders.ambientLightSurf); + } + else if(settings.giMethod==GiMethod::Probes && settings.zCloudShadowScale) { + cmd.setBinding(3, ssao.ssaoBlur, Sampler::nearest()); + cmd.setBinding(4, zbuffer, Sampler::nearest()); + cmd.setBinding(5, gi.hashTable); + cmd.setBinding(6, gi.probes); + cmd.setBinding(7, gi.probesLighting); + cmd.setPipeline(shaders.probeAmbient); + } + else if(settings.zCloudShadowScale) { + cmd.setBinding(3, sky.irradianceLut); cmd.setBinding(4, ssao.ssaoBlur, Sampler::nearest(ClampMode::ClampToEdge)); cmd.setPipeline(shaders.ambientLightSsao); - } else { + } + else { + cmd.setBinding(3, sky.irradianceLut); cmd.setPipeline(shaders.ambientLight); } cmd.draw(nullptr, 0, 3); diff --git a/game/graphics/renderer.h b/game/graphics/renderer.h index 9a4b38e04..576b74f8b 100644 --- a/game/graphics/renderer.h +++ b/game/graphics/renderer.h @@ -41,8 +41,8 @@ class Renderer final { }; Tempest::Size internalResolution() const; float internalResolutionScale() const; - void updateCamera(const Camera &camera); + void updateCamera(const Camera &camera); bool requiresTlas() const; Tempest::StorageImage& usesImage2d(Tempest::StorageImage& ret, Tempest::TextureFormat frm, uint32_t w, uint32_t h, bool mips = false); @@ -50,6 +50,7 @@ class Renderer final { 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::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); void prepareUniforms(); @@ -64,6 +65,10 @@ class Renderer final { void prepareExposure (Tempest::Encoder& cmd, WorldView& wview); void prepareEpipolar (Tempest::Encoder& cmd, WorldView& wview); + void prepareSurfels (Tempest::Encoder& cmd, WorldView& wview); + void surfelsBinning (Tempest::Encoder& cmd, WorldView& wview, int32_t tileSize, bool postPass); + void surfelsTrace (Tempest::Encoder& cmd, WorldView& wview, Tempest::StorageBuffer& surfels, bool postPass); + void drawHiZ (Tempest::Encoder& cmd, WorldView& view); void buildHiZ (Tempest::Encoder& cmd); void drawVsm (Tempest::Encoder& cmd, WorldView& view); @@ -93,10 +98,12 @@ class Renderer final { void drawRayQueryDbg (Tempest::Encoder& cmd, const WorldView& wview); void drawProbesDbg (Tempest::Encoder& cmd, const WorldView& wview); + void drawSurfelsDbg (Tempest::Encoder& cmd, const WorldView& wview); void drawProbesHitDbg (Tempest::Encoder& cmd); void drawVsmDbg (Tempest::Encoder& cmd, const WorldView& wview); void drawSwrDbg (Tempest::Encoder& cmd, const WorldView& wview); void drawRtsmDbg (Tempest::Encoder& cmd, const WorldView& wview); + void drawHashDbg (Tempest::Attachment& result, Tempest::Encoder& cmd, const WorldView& wview); void setupSettings(); void toggleGi(); @@ -116,7 +123,7 @@ class Renderer final { bool zCloudShadowScale = false; bool zFogRadial = false; - bool giEnabled = false; + GiMethod giMethod = GiMethod::None; bool aaEnabled = false; float zVidBrightness = 0.5; @@ -205,6 +212,17 @@ class Renderer final { Tempest::StorageImage probesLightingPrev; } gi; + struct { + const uint32_t maxSurfels = 16*1024; + Tempest::StorageBuffer surfels; + + Tempest::StorageImage irrImage; + Tempest::StorageImage surfCnts, surfBins; + Tempest::StorageBuffer surfBinsCtrl, surfList; + + Tempest::StorageImage dbgImage; + } surf; + struct { Tempest::Attachment frame; uint32_t numFrames = 0; diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 63958d473..38062e8ee 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -75,6 +75,7 @@ void Shaders::compileShaders() { ambientLight = ambientLightShader("ambient_light"); ambientLightSsao = ambientLightShader("ambient_light_ssao"); + ambientLightSurf = ambientLightShader("ambient_light_surf"); irradiance = computeShader("irradiance.comp.sprv"); cloudsLut = computeShader("clouds_lut.comp.sprv"); @@ -181,6 +182,7 @@ void Shaders::compileShaders() { if(Gothic::options().doRayQuery) { rtDbg = postEffect("triangle_uv", "rt_dbg", RenderState::ZTestMode::NoEqual); } + hashDbg = postEffect("triangle_uv", "hash_dbg"); if(Gothic::options().doRayQuery) { RenderState state; @@ -197,7 +199,7 @@ void Shaders::compileShaders() { rtPathtrace = device.pipeline(Triangles,state,vs,fs); } - if(Gothic::options().doRayQuery) { + if(isGi1Supported()) { RenderState state; state.setCullFaceMode(RenderState::CullMode::NoCull); state.setZTestMode (RenderState::ZTestMode::Less); @@ -235,6 +237,34 @@ void Shaders::compileShaders() { probeAmbient = device.pipeline(Triangles,state,vs,fs); } + if(isGi2Supported()) { + RenderState state; + state.setCullFaceMode(RenderState::CullMode::NoCull); + state.setZTestMode (RenderState::ZTestMode::Less); + state.setZWriteEnabled(false); + + auto sh = GothicShader::get("surf_dbg.vert.sprv"); + auto vs = device.shader(sh.data,sh.len); + sh = GothicShader::get("surf_dbg.frag.sprv"); + auto fs = device.shader(sh.data,sh.len); + surfDbg = device.pipeline(Triangles,state,vs,fs); + + surfBinPass = computeShader("surf_bin_pass.comp.sprv"); + surfBinClear = computeShader("surf_bin_clear.comp.sprv"); + surfBinAlloc = computeShader("surf_bin_alloc.comp.sprv"); + surfBinSort = computeShader("surf_bin_sort.comp.sprv"); + + surfUpdate = computeShader("surf_update.comp.sprv"); + surfCulling = computeShader("surf_culling.comp.sprv"); + surfDecimate = computeShader("surf_decimate.comp.sprv"); + surfCompact = computeShader("surf_compact.comp.sprv"); + + surfAlloc = computeShader("surf_alloc.comp.sprv"); + surfApply = computeShader("surf_apply.comp.sprv"); + + surfPathtrace = computeShader("surf_pathtrace.comp.sprv"); + } + if(Shaders::isVsmSupported()) { fogEpipolarOcclusion = computeShader("fog_epipolar_occlusion.comp.sprv"); fogEpipolarVsm = computeShader("fog_epipolar_vsm.comp.sprv"); @@ -355,6 +385,28 @@ bool Shaders::isRtsmSupported() { return true; } +bool Shaders::isGi1Supported() { + auto& gpu = Resources::device().properties(); + if(!gpu.raytracing.rayQuery) + return false; + if(!gpu.hasStorageFormat(R11G11B10UF) || !gpu.hasStorageFormat(R16)) + return false; + if(gpu.tex2d.maxSize<4096 || gpu.compute.maxInvocations<256 || !gpu.descriptors.nonUniformIndexing) + return false; + return true; + } + +bool Shaders::isGi2Supported() { + auto& gpu = Resources::device().properties(); + if(!gpu.raytracing.rayQuery) + return false; //TODO: splatting + if(gpu.compute.maxInvocations<256 || !gpu.descriptors.nonUniformIndexing) + return false; + if(!gpu.hasStorageFormat(RGBA16F)) + return false; + return true; + } + const RenderPipeline* Shaders::materialPipeline(const Material& mat, DrawCommands::Type t, PipelineType pt, bool bl) const { if(t==DrawCommands::Static) { // same shader diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index ecba2b9ac..1d2f50e79 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -27,10 +27,12 @@ class Shaders { static Shaders& inst(); static bool isVsmSupported(); static bool isRtsmSupported(); + static bool isGi1Supported(); + static bool isGi2Supported(); Tempest::RenderPipeline lights, lightsRq, lightsVsm; Tempest::RenderPipeline directLight, directLightSh, directLightRq; - Tempest::RenderPipeline ambientLight, ambientLightSsao; + Tempest::RenderPipeline ambientLight, ambientLightSsao, ambientLightSurf; Tempest::ComputePipeline copyBuf; Tempest::ComputePipeline copyImg; @@ -75,6 +77,7 @@ class Shaders { // RT/RQ Tempest::RenderPipeline rtDbg; Tempest::RenderPipeline rtPathtrace; + Tempest::RenderPipeline hashDbg; // GI Tempest::RenderPipeline probeDbg, probeHitDbg; @@ -83,6 +86,15 @@ class Shaders { Tempest::ComputePipeline probeTrace, probeLighting; Tempest::RenderPipeline probeAmbient; + // GI-Surf + Tempest::RenderPipeline surfDbg; + Tempest::ComputePipeline surfBinClear, surfBinPass, surfBinAlloc, surfBinSort; + + Tempest::ComputePipeline surfAlloc, surfApply; + Tempest::ComputePipeline surfUpdate, surfCulling, surfDecimate, surfCompact; + + Tempest::ComputePipeline surfPathtrace; + // Epipolar Tempest::ComputePipeline fogEpipolarVsm; Tempest::ComputePipeline fogEpipolarOcclusion; diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index d87c2f35d..ec2f3736a 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -217,6 +217,7 @@ add_shader(direct_light_vsm.frag virtual_shadow/vsm_direct_light.frag) add_shader(ambient_light.frag lighting/ambient_light.frag) add_shader(ambient_light_ssao.frag lighting/ambient_light.frag -DSSAO) +add_shader(ambient_light_surf.frag lighting/ambient_light.frag -DSSAO -DSURFEL_GI) add_shader(light.vert lighting/light.vert) add_shader(light.frag lighting/light.frag) @@ -285,6 +286,7 @@ add_shader(hiz_pot.comp hiz/hiz_pot.comp) # RayQuery add_shader(rt_dbg.frag lighting/rt_dbg/rt_dbg.frag -DRAY_QUERY -DRAY_QUERY_AT) +add_shader(hash_dbg.frag lighting/rt_dbg/hash_dbg.frag) # Global illumination add_shader(probe_dbg.vert lighting/rt/probe_dbg.vert) @@ -306,6 +308,25 @@ add_shader(probe_lighting.comp lighting/rt/probe_lighting.comp) add_shader(probe_ambient.vert triangle.vert) add_shader(probe_ambient.frag lighting/rt/probe_ambient.frag) +# Surfels +add_shader(surf_dbg.vert lighting/surfels/surf_dbg.vert) +add_shader(surf_dbg.frag lighting/surfels/surf_dbg.frag) + +add_shader(surf_update.comp lighting/surfels/surf_update.comp) +add_shader(surf_culling.comp lighting/surfels/surf_culling.comp) +add_shader(surf_decimate.comp lighting/surfels/surf_decimate.comp) +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_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) +add_shader(surf_bin_sort.comp lighting/surfels/surf_bin_sort.comp) + +add_shader(surf_pathtrace.comp lighting/surfels/surf_pathtrace.comp) + # Pathtracing add_shader(pathtrace.frag lighting/pt/pathtrace.frag -DRAY_QUERY -DRAY_QUERY_AT) diff --git a/shader/common.glsl b/shader/common.glsl index 512b656f1..27b2ca928 100644 --- a/shader/common.glsl +++ b/shader/common.glsl @@ -188,7 +188,7 @@ vec3 sampleHemisphereCos(uint i, uint numSamples, float offsetAng) { const float u = sqrt(1 - xi.x); const float u1p = sqrt(1 - u*u); const float a = M_PI*2.0*xi.y + offsetAng; - return vec3(cos(a) * u1p, xi.x, sin(a) * u1p); + return vec3(cos(a) * u1p, sin(a) * u1p, xi.x); } vec3 sampleSphere(uint i, uint numSamples, float offsetAng) { @@ -335,6 +335,21 @@ vec3 normalFetch(in utexture2D gbufNormal, ivec2 p) { return decodeNormal(n); } +uint wangHash(inout uint seed) { + seed = uint(seed ^ uint(61)) ^ uint(seed >> uint(16)); + seed *= uint(9); + seed = seed ^ (seed >> 4); + seed *= uint(0x27d4eb2d); + seed = seed ^ (seed >> 15); + return seed; + } + +uint pcgHash(uint v) { + uint state = v * 747796405u + 2891336453u; + uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; + } + // PCG3D // https://www.jcgt.org/published/0009/03/02/ // https://www.shadertoy.com/view/XlGcRh @@ -379,4 +394,28 @@ vec3 dither(vec2 fragCoords, uvec3 targetBits) { return nrnd / divisionSteps; } +float drawInt(in vec2 where, in int n) { + const float RESOLUTION = 0.5; + int i=int((where*=RESOLUTION).y); + if(00 || n>0; n/=10) + if ((j+=4)<3 && j>=0) { + int x = 0; + if(i>4) + x = 972980223; + else if(i>3) + x = 690407533; + else if(i>2) + x = 704642687; + else if(i>1) + x = 696556137; + else + x = 972881535; + return float(x >> (29-j-(n%10)*3)&1); + } + } + return 0; + } + #endif diff --git a/shader/lighting/ambient_light.frag b/shader/lighting/ambient_light.frag index f9630e477..1ad95cbf9 100644 --- a/shader/lighting/ambient_light.frag +++ b/shader/lighting/ambient_light.frag @@ -13,9 +13,15 @@ layout(binding = 0, std140) uniform UboScene { }; layout(binding = 1) uniform texture2D gbufDiffuse; 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; +#elif defined(SSAO) +layout(binding = 3) uniform texture2D irradiance; +layout(binding = 4) uniform sampler2D ssao; +#else layout(binding = 3) uniform texture2D irradiance; -#if defined(SSAO) -layout(binding = 4) uniform sampler2D ssao; #endif layout(location = 0) out vec4 outColor; @@ -28,10 +34,8 @@ float textureSsao() { return textureLod(ssao, uv, 0).r; } float textureSsao() { return 1; } #endif +#if !defined(SURFEL_GI) vec3 skyIrradiance(vec3 n) { -#if 0 - return scene.ambient * Fd_LambertInv; -#else ivec3 d; d.x = n.x>=0 ? 1 : 0; d.y = n.y>=0 ? 1 : 0; @@ -45,13 +49,29 @@ vec3 skyIrradiance(vec3 n) { ret += texelFetch(irradiance, ivec2(2,d.z), 0).rgb * n.z; return ret; -#endif } +#endif float grayscale(vec3 color) { return dot(color, vec3(0.2125, 0.7154, 0.0721)); } +vec3 luminance(vec3 norm) { +#if defined(SURFEL_GI) + vec3 ambient = textureLod(surfGi, uv, 0).rgb; + return ambient; +#else + vec3 ambient = scene.ambient; + vec3 sky = skyIrradiance(norm); + + vec3 ret = vec3(0); + ret += ambient; + ret += sky*0.8; + ret += (norm.y*0.25+0.75) * NightAmbient * Fd_Lambert; + return ret; +#endif + } + void main() { const ivec2 fragCoord = ivec2(gl_FragCoord.xy); @@ -62,18 +82,22 @@ void main() { const vec3 linear = textureAlbedo(diff); const float ao = textureSsao(); - vec3 ambient = scene.ambient; - vec3 sky = skyIrradiance(norm); - - vec3 luminance = vec3(0); - luminance += ambient; - luminance += sky*0.8; - luminance += (norm.y*0.25+0.75) * NightAmbient * Fd_Lambert; - vec3 color = linear; - color *= luminance; + color *= luminance(norm); +#if defined(SURFEL_GI) + //NOTE: pre-exposed already + color *= ao; +#else color *= ao; color *= scene.exposure; +#endif + +#if defined(SURFEL_GI) && 1 + if(drawInt(fragCoord.xy-ivec2(100,100), int(surf.count))>0) { + outColor = vec4(1); + return; + } +#endif outColor = vec4(color, 1); // outColor = vec4(vec3(ao), 0); diff --git a/shader/lighting/pt/pathtrace.frag b/shader/lighting/pt/pathtrace.frag index 10d868cb3..ce0220662 100644 --- a/shader/lighting/pt/pathtrace.frag +++ b/shader/lighting/pt/pathtrace.frag @@ -5,14 +5,14 @@ #define RAY_QUERY_AT #define SOFT_SHADOW +// #define RESCALE #include "lighting/rt/rt_common.glsl" +#include "lighting/pt/pathtrace_common.glsl" #include "lighting/tonemapping.glsl" #include "scene.glsl" #include "common.glsl" -//const float probeRayDistance = 200*100; // Lumen rt-probe uses 200-meters range -const float TMax = 1e30f; const vec3 groundAlbedo = vec3(0.3f); // testing layout(location = 0) in vec2 inUV; @@ -28,47 +28,6 @@ layout(binding = 2) uniform texture2D irradiance; layout(binding = 3) uniform sampler2D skyLUT; layout(binding = 4) uniform sampler2D textureSm1; -uint wangHash(inout uint seed) { - seed = uint(seed ^ uint(61)) ^ uint(seed >> uint(16)); - seed *= uint(9); - seed = seed ^ (seed >> 4); - seed *= uint(0x27d4eb2d); - seed = seed ^ (seed >> 15); - return seed; - } - -uint srand(uvec2 fragCoord, uint seed) { - return uint(uint(fragCoord.x) * uint(1973) + uint(fragCoord.y) * uint(9277) + uint(seed) * uint(26699)) | uint(1); - } - -float randf(inout uint state) { - return float(wangHash(state)) / 4294967296.0; - } - -vec3 randVec3(inout uint state) { - float z = randf(state) * 2.0 - 1.0; - float a = randf(state) * 2.0 * M_PI; - float r = sqrt(1.0f - z * z); - float x = r * cos(a); - float y = r * sin(a); - return vec3(x, y, z); - } - -vec3 randCosWeightedHemisphereDirection(const vec3 n, inout uint rngState) { - vec2 rv2 = vec2(randf(rngState), randf(rngState)); - - vec3 uu = normalize( cross( n, vec3(0.0,1.0,1.0) ) ); - vec3 vv = normalize( cross( uu, n ) ); - - float ra = sqrt(rv2.y); - float rx = ra*cos(6.2831*rv2.x); - float ry = ra*sin(6.2831*rv2.x); - float rz = sqrt( 1.0-rv2.y ); - vec3 rr = vec3( rx*uu + ry*vv + rz*n ); - - return normalize(rr); - } - vec3 skyIrradiance(vec3 n) { ivec3 d; d.x = n.x>=0 ? 1 : 0; @@ -103,50 +62,8 @@ float shadowFactor(vec3 pos) { return calcShadow(shPos.xyz/shPos.w); } -void rayQueryProceedAlphaTest(in rayQueryEXT rayQuery, inout uint rngState) { - while(rayQueryProceedEXT(rayQuery)) { - const uint type = rayQueryGetIntersectionTypeEXT(rayQuery,false); - if(type==gl_RayQueryCandidateIntersectionTriangleEXT) { - const vec4 d = resolveHit(rayQuery); - // const bool opaqueHit = (d.a>0.5); - const bool opaqueHit = (d.a > randf(rngState)); - if(opaqueHit) - rayQueryConfirmIntersectionEXT(rayQuery); - } - } - } - -float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, inout uint rngState) { - // CullBack due to vegetation - uint flags = gl_RayFlagsSkipAABBEXT | gl_RayFlagsCullBackFacingTrianglesEXT; - float tMin = 1; - - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, topLevelAS, flags, CM_ShadowCaster, - rayOrigin, tMin, rayDirection, 500*100); - rayQueryProceedAlphaTest(rayQuery); - // rayQueryProceedAlphaTest(rayQuery, rngState); - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) - return 1; - return 0; - } -float computeTextureLOD(float dist, vec3 dir, vec3 norm) { - dist /= 500.0; - float mip = 0; - mip += log2(dist); - mip -= log2(abs(dot(dir, norm))); - return mip; - } - -struct HitResolve { - vec4 diff; - vec3 norm; - float rayT; - bool water; - }; - -vec3 randomizeRay(vec3 ray, float angle, inout uint rngState) { +vec3 randomizeRay(vec3 ray, float angle, inout Random rngState) { // https://www.shadertoy.com/view/3sfBWs const vec2 blueNoiseInDisk[64] = vec2[64]( vec2(0.478712,0.875764), @@ -216,7 +133,7 @@ vec3 randomizeRay(vec3 ray, float angle, inout uint rngState) { ); // get a blue noise sample position - vec2 samplePos = blueNoiseInDisk[wangHash(rngState)%blueNoiseInDisk.length()]; + vec2 samplePos = blueNoiseInDisk[wangHash(rngState.state)%blueNoiseInDisk.length()]; samplePos *= tan(angle); vec3 r = normalize(vec3(0,0,1) + vec3(samplePos,0)); @@ -227,57 +144,7 @@ vec3 randomizeRay(vec3 ray, float angle, inout uint rngState) { return vec3( r.x*uu + r.y*vv + r.z*ray ); } -HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, float mipOverride, inout uint rngState) { - // CullBack due to vegetation - uint flags = gl_RayFlagsSkipAABBEXT | gl_RayFlagsCullBackFacingTrianglesEXT; - float tMin = 2; - - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, topLevelAS, flags, 0xFF, - rayOrigin, tMin, rayDirection, 500*100); - rayQueryProceedAlphaTest(rayQuery); - // rayQueryProceedAlphaTest(rayQuery, rngState); - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) { - HitResolve ret; - ret.rayT = TMax; - return ret; - } - - const HitDesc hit = pullCommitedHitDesc(rayQuery); - - const float rayT = rayQueryGetIntersectionTEXT(rayQuery, true); - const bool face = !(rayQueryGetIntersectionFrontFaceEXT(rayQuery, true)); //NOTE: not working on vegetation - - const uint id = hit.instanceId; - const uvec3 index = pullTrinagleIds(id,hit.primitiveId); - - const vec2 uv0 = pullTexcoord(id,index.x); - const vec2 uv1 = pullTexcoord(id,index.y); - const vec2 uv2 = pullTexcoord(id,index.z); - - const vec3 nr0 = pullNormal(id,index.x); - const vec3 nr1 = pullNormal(id,index.y); - const vec3 nr2 = pullNormal(id,index.z); - - const vec3 b = hit.baryCoord; - const vec2 uv = (b.x*uv0 + b.y*uv1 + b.z*uv2); - vec3 norm = (b.x*nr0 + b.y*nr1 + b.z*nr2); - - const mat3x3 matrix = mat3x3(rayQueryGetIntersectionObjectToWorldEXT(rayQuery, true)); - norm = normalize(matrix*norm); - - const float mip = mipOverride>=0 ? mipOverride : computeTextureLOD(rayT,rayDirection,norm); - const vec4 diff = textureLod(sampler2D(textures[nonuniformEXT(id)], smp),uv,mip); - - HitResolve ret; - ret.diff = diff; - ret.norm = norm; - ret.rayT = face ? -rayT : rayT; - ret.water = hit.water; - return ret; - } - -float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection, bool shadowed, float softAngle, inout uint rngState) { +float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection, bool shadowed, float softAngle, inout Random rngState) { #if defined(SOFT_SHADOW) vec3 shRay = randomizeRay(rayDirection, 0.5*M_PI/180.0, rngState); #else @@ -296,11 +163,11 @@ float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection, bool shado vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection) { const int numBounces = 5; - uint rngState = srand(uvec2(gl_FragCoord.xy), scene.tickCount32); - vec3 thruput = vec3(1); - vec3 color = vec3(0); - bool underWater = (scene.underWater!=0); - float depth = 0; + Random rngState = srand(uvec2(gl_FragCoord.xy), scene.tickCount32); + vec3 thruput = vec3(1); + vec3 color = vec3(0); + bool underWater = (scene.underWater!=0); + float depth = 0; for(int bounce=0;; ++bounce) { if(dot(thruput*scene.GSunIntensity*scene.exposure, vec3(0.2126, 0.7152, 0.0722)) < 0.01) @@ -308,10 +175,12 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection) { if(bounce>=numBounces) { // return 0.5*vec4(1,0,1,depth)*scene.GSunIntensity; +#if defined(RESCALE) // rescale to 100% thruput vec3 scale = 1.0-thruput; if(scale.r>0.01 && scale.g>0.01 && scale.b>0.01) return vec4(color/scale, depth); +#endif return vec4(color, depth); } @@ -352,7 +221,10 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection) { rayOrigin = (rayOrigin + rayDirection * hit.rayT); rayDirection = randCosWeightedHemisphereDirection(hit.norm, rngState); - thruput *= textureAlbedo(hit.diff.rgb); + vec3 albedo = textureAlbedo(hit.diff.rgb); + if(bounce>0) + albedo = min(albedo, 0.95); + thruput *= albedo; //thruput *= vec3(0.85); //snow //thruput *= vec3(0.04); // asphalt //if(bounce>0) @@ -397,7 +269,7 @@ vec2 halton(uint index) { } void main() { - const vec2 subpixel = (halton(srand(uvec2(gl_FragCoord.xy),frameId))-0.5) * scene.screenResInv; + const vec2 subpixel = (halton(srand(uvec2(gl_FragCoord.xy),frameId).state)-0.5) * scene.screenResInv; const mat4 inv = scene.viewProjectInv; const vec2 uv = inUV + subpixel; diff --git a/shader/lighting/pt/pathtrace_common.glsl b/shader/lighting/pt/pathtrace_common.glsl new file mode 100644 index 000000000..7a537a4bd --- /dev/null +++ b/shader/lighting/pt/pathtrace_common.glsl @@ -0,0 +1,101 @@ +#ifndef PATHTRACE_COMMON_GLSL +#define PATHTRACE_COMMON_GLSL + +#include "random.glsl" + +const float TMax = 1e30f; + +struct HitResolve { + vec4 diff; + vec3 norm; + float rayT; + bool water; + }; + +float rayPathTextureLOD(float dist, vec3 dir, vec3 norm) { + dist /= 500.0; + float mip = 0; + mip += log2(dist); + mip -= log2(abs(dot(dir, norm))); + return mip; + } + +void rayQueryProceedAlphaTest(in rayQueryEXT rayQuery, inout Random rng) { + while(rayQueryProceedEXT(rayQuery)) { + const uint type = rayQueryGetIntersectionTypeEXT(rayQuery,false); + if(type==gl_RayQueryCandidateIntersectionTriangleEXT) { + const vec4 d = resolveHit(rayQuery); + // const bool opaqueHit = (d.a>0.5); + const bool opaqueHit = (d.a > randf(rng)); + if(opaqueHit) + rayQueryConfirmIntersectionEXT(rayQuery); + } + } + } + +float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, inout Random rngState) { + // CullBack due to vegetation + uint flags = gl_RayFlagsSkipAABBEXT | gl_RayFlagsCullBackFacingTrianglesEXT; + float tMin = 1; + + rayQueryEXT rayQuery; + rayQueryInitializeEXT(rayQuery, topLevelAS, flags, CM_ShadowCaster, + rayOrigin, tMin, rayDirection, 500*100); + rayQueryProceedAlphaTest(rayQuery); + // rayQueryProceedAlphaTest(rayQuery, rngState); + if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) + return 1; + return 0; + } + +HitResolve rayQueryProceedPrimary(const vec3 rayOrigin, const vec3 rayDirection, float mipOverride, inout Random rngState) { + // CullBack due to vegetation + uint flags = gl_RayFlagsSkipAABBEXT | gl_RayFlagsCullBackFacingTrianglesEXT; + float tMin = 2; + + rayQueryEXT rayQuery; + rayQueryInitializeEXT(rayQuery, topLevelAS, flags, 0xFF, + rayOrigin, tMin, rayDirection, 500*100); + rayQueryProceedAlphaTest(rayQuery); + // rayQueryProceedAlphaTest(rayQuery, rngState); + if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) { + HitResolve ret; + ret.rayT = TMax; + return ret; + } + + const HitDesc hit = pullCommitedHitDesc(rayQuery); + + const float rayT = rayQueryGetIntersectionTEXT(rayQuery, true); + const bool face = !(rayQueryGetIntersectionFrontFaceEXT(rayQuery, true)); //NOTE: not working on vegetation + + const uint id = hit.instanceId; + const uvec3 index = pullTrinagleIds(id,hit.primitiveId); + + const vec2 uv0 = pullTexcoord(id,index.x); + const vec2 uv1 = pullTexcoord(id,index.y); + const vec2 uv2 = pullTexcoord(id,index.z); + + const vec3 nr0 = pullNormal(id,index.x); + const vec3 nr1 = pullNormal(id,index.y); + const vec3 nr2 = pullNormal(id,index.z); + + const vec3 b = hit.baryCoord; + const vec2 uv = (b.x*uv0 + b.y*uv1 + b.z*uv2); + vec3 norm = (b.x*nr0 + b.y*nr1 + b.z*nr2); + + const mat3x3 matrix = mat3x3(rayQueryGetIntersectionObjectToWorldEXT(rayQuery, true)); + norm = normalize(matrix*norm); + + const float mip = mipOverride>=0 ? mipOverride : rayPathTextureLOD(rayT,rayDirection,norm); + const vec4 diff = textureLod(sampler2D(textures[nonuniformEXT(id)], smp),uv,mip); + + HitResolve ret; + ret.diff = diff; + ret.norm = norm; + ret.rayT = face ? -rayT : rayT; + ret.water = hit.water; + return ret; + } + +#endif \ No newline at end of file diff --git a/shader/lighting/rt/probe_ambient.frag b/shader/lighting/rt/probe_ambient.frag index 1bd00d783..33271911c 100644 --- a/shader/lighting/rt/probe_ambient.frag +++ b/shader/lighting/rt/probe_ambient.frag @@ -17,14 +17,13 @@ layout(binding = 0, std140) uniform UboScene { SceneDesc scene; }; -layout(binding = 1) uniform sampler2D probesLighting; -layout(binding = 2) uniform sampler2D gbufDiffuse; -layout(binding = 3) uniform usampler2D gbufNormal; +layout(binding = 1) uniform sampler2D gbufDiffuse; +layout(binding = 2) uniform usampler2D gbufNormal; +layout(binding = 3) uniform sampler2D ssao; layout(binding = 4) uniform sampler2D depth; -layout(binding = 5) uniform sampler2D ssao; - -layout(binding = 6, std430) readonly buffer Hbo0 { uint hashTable[]; }; -layout(binding = 7, std430) readonly buffer Pbo { ProbesHeader probeHeader; Probe probe[]; }; +layout(binding = 5, std430) readonly buffer Hbo0 { uint hashTable[]; }; +layout(binding = 6, std430) readonly buffer Pbo { ProbesHeader probeHeader; Probe probe[]; }; +layout(binding = 7) uniform sampler2D probesLighting; layout(location = 0) out vec4 outColor; diff --git a/shader/lighting/rt_dbg/hash_dbg.frag b/shader/lighting/rt_dbg/hash_dbg.frag new file mode 100644 index 000000000..18e6ea211 --- /dev/null +++ b/shader/lighting/rt_dbg/hash_dbg.frag @@ -0,0 +1,74 @@ +#version 460 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(location = 0) in vec2 inUV; +layout(location = 0) out vec4 outColor; + +layout(std140, push_constant) uniform Push { + vec3 originLwc; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +//layout(binding = 1) uniform texture2D depth; +layout(binding = 2) uniform usampler2D gbufNormal; +layout(binding = 3) uniform texture2D depth; + +uint computeDepthSlice(float linDepth, float near, float far, uint numSlices) { + float z = linDepth; + // Compute logarithmic slice index + float slice_float = log(z / near) * (float(numSlices) / log(far / near)); + // Clamp to ensure it doesn't exceed the grid bounds + return clamp(uint(slice_float), 0u, numSlices - 1u); + } + +ivec3 hasGridPos(vec3 wpos, float cellSize) { + wpos = wpos / cellSize; + return ivec3(round(wpos)); + } + +float surfDist(ivec3 hpos, vec3 pos, float cellSize) { + vec3 p = (pos/cellSize - hpos); + p *= 2/sqrt(2.0); + return dot(p,p); //quad distance + } + +uint surfHash(ivec3 hpos) { + ivec3 p = hpos; + uint hashKey = pcgHash(p.x + pcgHash(p.y + pcgHash(p.z))); + return hashKey; + } + +void main(void) { + const float d = texelFetch(depth, ivec2(gl_FragCoord.xy), 0).r; + if(d>=1.0) + discard; + + const vec3 norm = normalFetch(gbufNormal, ivec2(gl_FragCoord.xy)); + + const float lD = max(linearDepth(d, scene.clipInfo), 10); + const mat4 inv = scene.viewProjectLwcInv; + const vec4 pos = inv*vec4(inUV * 2.0 - 1.0, d, 1); + + float fov = 67.5f*M_PI/180.0; + float cellSize = 25; + + // float sz = computeCellSize(lD, fov, textureSize(depth,0), 16, cellSize); + // float sz = computeCellSize(lD, fov, textureSize(depth,0), 2, 1); + float sz = computeCellSize(lD, fov, textureSize(depth,0), 64); + vec3 wpos = pos.xyz/pos.w + originLwc; + + ivec3 ipos = hasGridPos(wpos, sz); + uint h = surfHash(ipos); + float dx = surfDist(ipos, wpos, sz); + + //uint sx = computeDepthSlice(lD, 10, 100000, 32); + + // outColor = vec4(debugColors[h%debugColors.length()], 1.0); + // outColor = vec4(dx * debugColors[h%debugColors.length()], 1.0); + outColor = vec4(dx); + // outColor = vec4(debugColors[sx%debugColors.length()], 1.0); + } diff --git a/shader/lighting/surfels/surf_alloc.comp b/shader/lighting/surfels/surf_alloc.comp new file mode 100644 index 000000000..8a680cdbb --- /dev/null +++ b/shader/lighting/surfels/surf_alloc.comp @@ -0,0 +1,208 @@ +#version 450 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +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; + +layout(std140, push_constant) uniform Push { + vec3 originLwc; + uint tileSize; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +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 = 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; + +shared uint fragVote; + +vec3 worldPos(float z, vec3 normal, 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 + normal*NormalBias)/pos.w + originLwc; + } + +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); + } + +float calculteWeight(const Candidate s, const vec3 wpos, const vec3 norm) { + float rMean = s.pos.w * 2.0; + float rMax = s.pos.w; + return calculteWeight(s.pos.xyz, s.norm.xyz, rMean, rMax, wpos, norm); + } + +float amendCoverage(const ivec2 fragCoord, const vec3 wpos, const vec3 norm) { + const int i = pointQueueSize-1; + return calculteWeight(pointQueue[i], wpos, norm); + } + +float estimateCoverage(const ivec2 fragCoord, const vec3 wpos, const vec3 norm, const uint accounted) { + float wSum = 0.001 + texelFetch(irradiance, fragCoord, 0).a; + if(wSum>1.0) + return 1.0; + + for(int i=pointQueueSize; i>accounted;) { + --i; + wSum += calculteWeight(pointQueue[i], wpos, norm); + if(wSum > 1.0) + return 1.0; + } + return wSum; + } + +void fragCoverageVote(float wSum) { + uint w = floatBitsToUint(wSum) & 0xFFFF0000; + uint b = uint(w) | uint(gl_LocalInvocationIndex) ^ Salt; + atomicMin(fragVote, b); + } + +void storeSurfels() { + const uint laneID = gl_LocalInvocationIndex; + + if(laneID==0) { + pointQueueSize = min(pointQueueSize, pointQueue.length()); + fragVote = atomicAdd(surf.header.count, pointQueueSize); + atomicAdd(surf.header.added, pointQueueSize); + } + barrier(); + + if(laneID= SKY_DEPTH); + + const vec3 norm = normalFetch(gbufNormal, fragCoord); + const vec3 wpos = worldPos(z, norm, fragCoord, screenSize); + + float coverage = (sky ? 1.0 : estimateCoverage(fragCoord, wpos, norm, 0)); + + for(int i=0; i=pointQueue.length()) + break; + + if(coverage<1.0) + fragCoverageVote(coverage); + barrier(); + + const uint vote = fragVote; + barrier(); + + fragVote = 0xFFFFFFFF; + if((vote & 0xFFFF0000)==0xFFFF0000) + break; + + allocSurfel(at, screenSize, vote); + barrier(); + + coverage += amendCoverage(fragCoord, wpos, norm); + } + + imageStore(dbgImage, fragCoord, vec4(coverage)); + } + +ivec2 remap(ivec2 subtile, ivec2 tileCnt) { + // shuffle sub-tiles a little + uint k = uint(subtile.y * tileCnt.x + subtile.x); + k ^= (3*8+4); + k &= uint(NumThreads-1); + return ivec2(int(k % tileCnt.x), int(k / tileCnt.x)); + } + +void main() { +#if 0 + if(gl_WorkGroupID.xy!=uvec2(10,4)) + return; +#endif + + if(!(7 < gl_WorkGroupID.x && gl_WorkGroupID.x<=9 && + 3 < gl_WorkGroupID.y && gl_WorkGroupID.y<=6)) + ;//return; + + pointQueueSize = 0; + fragVote = 0xFFFFFFFF; + barrier(); + + const ivec2 screenSize = ivec2(textureSize(depth,0)); + const ivec2 tileCount = ivec2(tileSize)/ivec2(gl_WorkGroupSize.xy); + for(int y=0; y= cnt) + return; + + const uint sId = surfelList[ptr+i]; + const Surfel s = surfels[sId]; + + if(!isSurfelVisible(s, bboxMin, bboxMax)) + return; + + const uint id = atomicAdd(surfelCount, 1); + surfelsPos [id] = vec4(s.pos, uintBitsToFloat(sId)); + surfelsNorm[id] = vec4(decodeNormal(s.norm), s.radius); + surfelsIrr [id] = vec4(s.irradiance, s.radiusMean); + } + +void main() { +#if 0 + if(gl_WorkGroupID.xy/8!=uvec2(10,6)) + return; +#endif + const uint laneID = gl_LocalInvocationIndex; + + const ivec2 fragCoord = ivec2(gl_GlobalInvocationID.xy); + const ivec2 screenSize = ivec2(textureSize(depth,0)); + const bool valid = all(lessThan(fragCoord, screenSize)); + + const ivec2 tileId = ivec2(gl_WorkGroupID.xy*gl_WorkGroupSize.xy)/int(tileSize); + const uint cnt = imageLoad(surfCnts, tileId).x; + const uint ptr = imageLoad(surfBins, tileId).x; + + if(pass>0 && cnt==0) + return; + + const float z = valid ? texelFetch(depth,fragCoord,0).x : 1; + const vec3 wpos = worldPos(z, fragCoord, screenSize); + const vec3 norm = normalFetch(gbufNormal, fragCoord); + const bool sky = (z >= SKY_DEPTH); + + ivec2 bboxMin = ivec2(gl_WorkGroupID.xy*gl_WorkGroupSize.xy); + ivec2 bboxMax = bboxMin + ivec2(gl_WorkGroupSize.xy); + + surfelCount = 0; + + vec4 clr = vec4(0); + for(uint i=0; i0) + clr.rgb /= clr.a; + imageStore(irradiance, fragCoord, vec4(clr)); + } else { + // post pass + const vec4 pre = imageLoad(irradiance, fragCoord); + clr += vec4(pre.rgb*pre.a, pre.a); + if(clr.a>0) + clr.rgb /= clr.a; + imageStore(irradiance, fragCoord, vec4(clr.rgb, clr.a)); + } + + imageStore(dbgImage, fragCoord, vec4(clr.a)); + } diff --git a/shader/lighting/surfels/surf_bin_alloc.comp b/shader/lighting/surfels/surf_bin_alloc.comp new file mode 100644 index 000000000..503e7ef28 --- /dev/null +++ b/shader/lighting/surfels/surf_bin_alloc.comp @@ -0,0 +1,21 @@ +#version 450 + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(binding = 1, std430) buffer SB0 { uint binAlloc; }; +layout(binding = 3, r32ui) uniform uimage2D surfCnts; +layout(binding = 4, r32ui) uniform uimage2D surfBins; +layout(binding = 5, std430) readonly buffer SB1 { uint surfelIds[]; }; + +void main() { + const ivec2 tile = ivec2(gl_GlobalInvocationID.xy); + const ivec2 sz = imageSize(surfBins); + if(tile.x>=sz.x || tile.y>=sz.y) + return; + + const uint v = imageLoad(surfCnts, tile).x; + const uint ptr = v>0 ? atomicAdd(binAlloc, v) : 0; + + imageStore(surfCnts, tile, uvec4(0)); + imageStore(surfBins, tile, uvec4(ptr)); + } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_bin_clear.comp b/shader/lighting/surfels/surf_bin_clear.comp new file mode 100644 index 000000000..aa711dfa9 --- /dev/null +++ b/shader/lighting/surfels/surf_bin_clear.comp @@ -0,0 +1,20 @@ +#version 450 + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(binding = 1, std430) buffer SB0 { uint binAlloc; }; +layout(binding = 3, r32ui) uniform uimage2D surfCnts; +layout(binding = 4, r32ui) uniform uimage2D surfBins; + +void main() { + if(gl_GlobalInvocationID.xy==uvec2(0)) + binAlloc = 0; + + const ivec2 tile = ivec2(gl_GlobalInvocationID.xy); + const ivec2 sz = imageSize(surfBins); + if(tile.x>=sz.x || tile.y>=sz.y) + return; + + imageStore(surfCnts, tile, uvec4(0)); + imageStore(surfBins, tile, uvec4(0)); + } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_bin_pass.comp b/shader/lighting/surfels/surf_bin_pass.comp new file mode 100644 index 000000000..5609cf938 --- /dev/null +++ b/shader/lighting/surfels/surf_bin_pass.comp @@ -0,0 +1,56 @@ +#version 450 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(local_size_x = 64) in; + +layout(std140, push_constant) uniform Push { + int tileSize; + int allocPass; + int postPass; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; + +layout(binding = 1, std430) buffer SB0 { uint binAlloc; }; +layout(binding = 2, std430) readonly buffer SB1 { SurfHeader header; Surfel surfels[]; } surf; +layout(binding = 3, r32ui) uniform uimage2D surfCnts; +layout(binding = 4, r32ui) uniform readonly uimage2D surfBins; +layout(binding = 5, std430) buffer SB2 { uint surfelIds[]; }; + +void main() { + const uint surfelId = gl_GlobalInvocationID.x; + if(surfelId>=surf.header.count) + return; + if(surfelId=surfelIds.length()) + return; // error! Must be too little memmory for the binning + surfelIds[ptr] = surfelId; + } + } diff --git a/shader/lighting/surfels/surf_bin_sort.comp b/shader/lighting/surfels/surf_bin_sort.comp new file mode 100644 index 000000000..ab472f90d --- /dev/null +++ b/shader/lighting/surfels/surf_bin_sort.comp @@ -0,0 +1,81 @@ +#version 450 + +#include "lighting/surfels/surf_common.glsl" + +layout(local_size_x = 256) in; + +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; + +layout(binding = 6, r32ui) uniform uimage2D surfCnts; +layout(binding = 7, r32ui) uniform uimage2D surfBins; +layout(binding = 8, std430) buffer SB2 { uint surfelList[]; }; + +shared uint data[MaxInTile]; + +uint nextPowerOfTwo(uint x) { + if(x <= 1u) + return 1u; + return 1u << (findMSB(x - 1u) + 1); + } + +void swap(inout uint a, inout uint b) { + uint t = a; + a = b; + b = t; + } + +void bitonicSort(uint count) { + const uint laneID = gl_LocalInvocationIndex; + + //count = nextPowerOfTwo(count); + + for(uint k = 2; k <= count; k <<= 1) { + for(uint j = k >> 1; j > 0; j >>= 1) { + for (uint i = laneID; i < count; i += NumThreads) { + uint ixj = i ^ j; + + if(ixj > i) { + bool ascending = ((i & k) == 0); + + uint a = data[i ]; + uint b = data[ixj]; + + const bool swapNeeded = ascending ? (a > b) : (a < b); + if(swapNeeded) + swap(data[i], data[ixj]); + } + } + barrier(); + } + } + } + +void main() { + const uint laneID = gl_LocalInvocationIndex; + + const ivec2 tileId = ivec2(gl_WorkGroupID.xy); + const uint ptr = imageLoad(surfBins, tileId).x; + const uint cnt = imageLoad(surfCnts, tileId).x; + const uint cntM = min(cnt, data.length()); + const uint cntPow2 = nextPowerOfTwo(cntM); + + for(uint i=laneID; icntM) + imageStore(surfCnts, tileId, uvec4(cntM)); + } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl new file mode 100644 index 000000000..60430db83 --- /dev/null +++ b/shader/lighting/surfels/surf_common.glsl @@ -0,0 +1,160 @@ +#ifndef SURF_COMMON_GLSL +#define SURF_COMMON_GLSL + +#include "common.glsl" + +const float SKY_DEPTH = 0.999995; +const int MinCoverage = 8; // in pixels +const int DefaultCoverage = 128; // in pixels +const uint MaxInTile = 512; // ~32px (~6x6) per surfel + +struct SurfHeader { + uint count; + uint one1; + uint one2; + uint added; + }; + +struct Surfel { + vec3 pos; + uint norm; + ivec2 fragCoord; + float radius; + float radiusMean; + vec3 irradiance; + int radiusPix; + }; + +struct Candidate { + vec4 pos; // pos, size + vec4 norm; // norm, padd + }; + +bool isSurfelVisible(const Surfel s, ivec2 bboxMin, ivec2 bboxMax) { + const ivec2 at = s.fragCoord; + const int radius = s.radiusPix; + + if(bboxMax.x < at.x-radius || bboxMax.y < at.y-radius) + return false; + if(at.x+radius < bboxMin.x || at.y+radius < bboxMin.y) + return false; + return true; + } + +float computeTargetCellSize(float d, float aperture, vec2 resolution, float pixelFeatureSize) { + // Equation 2: Evaluate the angular factor based on resolution aspect ratio + float term1 = aperture / resolution.x; + float term2 = (aperture * resolution.x) / (resolution.y * resolution.y); + float maxFactor = max(term1, term2); + + // Compute target feature size in world space + float sw = d * tan(maxFactor * pixelFeatureSize); + return sw; + } + +// Computes the discretized cell size (s_wd) rounding to the nearest power-of-two level +float computeAdaptiveCellSize(float sw, float sMin) { + // Avoid log2 of zero or negative numbers if sw is too small + if(sw <= sMin) + return sMin; + + // Equation 3: Discretize to power-of-two bands to create discrete levels of detail + float logScale = floor(log2(sw / sMin)); + float swd = pow(2.0, logScale) * sMin; + return swd; + } + +float computeCellSize(float d, float fov, vec2 resolution, float pixelFeatureSize) { + const float sMin = 1; // min 1 centimeter + float sw = computeTargetCellSize(d, fov, resolution, pixelFeatureSize); + return computeAdaptiveCellSize(sw, sMin); + } + +uint surfHash(vec3 pos, float cellSize, uint inorm) { +#if 1 + ivec3 p = ivec3(round(pos * cellSize)); + uint hashKey = pcgHash(inorm + pcgHash(p.x + pcgHash(p.y + pcgHash(p.z)))); + return hashKey; +#else + ivec3 p = ivec3(round(pos * cellSize)); + + uint hash = 0x811C9DC5; + hash ^= p.x; + hash *= 0x01000193; // FNV-1a prime + hash = (hash << 13) | (hash >> 19); + + hash ^= p.y; + hash *= 0x01000193; + hash = (hash << 17) | (hash >> 15); + + hash ^= p.z; + hash *= 0x01000193; + + // 5. Avalanche step to ensure every input bit affects every output bit + hash ^= hash >> 16; + hash *= 0x7feb352d; + hash ^= hash >> 15; + hash *= 0x846ca68b; + hash ^= hash >> 16; + + return hash; +#endif + } + +float calculteWeight(const vec3 spos, const vec3 snorm, float rEff, float rMax, 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 + vec3 ldir = wpos - spos; + float dist = length(ldir); + 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 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 + } + +vec3 surfDebugColor(Surfel s, uint sId) { + ivec3 p = ivec3(s.pos/10); + uint h = pcgHash(p.x + pcgHash(p.y + pcgHash(p.z))); + return debugColors[h%debugColors.length()]; + } + +vec3 surfDebugColor(vec3 pos, uint sId) { + ivec3 p = ivec3(pos/10); + uint h = pcgHash(p.x + pcgHash(p.y + pcgHash(p.z))); + return debugColors[h%debugColors.length()]; + } + +#endif \ No newline at end of file diff --git a/shader/lighting/surfels/surf_compact.comp b/shader/lighting/surfels/surf_compact.comp new file mode 100644 index 000000000..e7b06d16f --- /dev/null +++ b/shader/lighting/surfels/surf_compact.comp @@ -0,0 +1,43 @@ +#version 450 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(local_size_x = 256) in; + +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; + +layout(binding = 3, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; } surf; + +shared uint wPtr; + +void main() { + const uint laneID = gl_LocalInvocationIndex; + + wPtr = 0; + barrier(); + + Surfel sx; + for(uint ix=0; ix0; + if(keep) { + sx = surf.surfels[i]; + } + barrier(); + + if(keep) { + const uint w = atomicAdd(wPtr, 1); + surf.surfels[w] = sx; + } + barrier(); + } + + if(laneID==0) { + surf.header.count = wPtr; + surf.header.one1 = 1; + surf.header.one2 = 1; + surf.header.added = 0; + } + } diff --git a/shader/lighting/surfels/surf_culling.comp b/shader/lighting/surfels/surf_culling.comp new file mode 100644 index 000000000..fdd0c4222 --- /dev/null +++ b/shader/lighting/surfels/surf_culling.comp @@ -0,0 +1,140 @@ +#version 460 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(local_size_x = 64) in; + +layout(push_constant, std140) uniform Push { + float znear; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +layout(binding = 1) uniform texture2D hiZ; +layout(binding = 2) uniform texture2D depth; +layout(binding = 3, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; + +float hiZFetch(in texture2D hiZ, in ivec2 ibbox[2]) { + if(ibbox[0]==ibbox[1]) + return texelFetch(hiZ, ibbox[0], 0).x; + + const uint sz = max(ibbox[1].x-ibbox[0].x, ibbox[1].y-ibbox[0].y); + const int mip = findMSB(sz - 1) + 1; // log2 of next pot + + ibbox[0].x = (ibbox[0].x >> mip); + ibbox[0].y = (ibbox[0].y >> mip); + + ivec2 hiZsize = textureSize(hiZ,mip); + float z = texelFetchOffset(hiZ, ibbox[0], mip, ivec2(0,0)).x; + if(ibbox[0].x+1 z) + return false; + return true; + } + +bool projectSphere(const vec3 c, float R, out vec4 aabb, out float depthMin, out float depthMax) { + // https://jcgt.org/published/0002/02/05/paper.pdf + depthMin = scene.project[3][2]/max(c.z - R, znear) + scene.project[2][2]; + depthMax = scene.project[3][2]/max(c.z + R, znear) + scene.project[2][2]; + + if (c.z + R < 0.0) { + // aabb = vec4(0,0,1,1); + return false; + } + + const float P00 = scene.project[0][0]; // fx + const float P11 = scene.project[1][1]; // fy + + vec3 cr = c * R; + float czr2 = c.z * c.z - R * R; + + float vx = sqrt(c.x * c.x + czr2); + float minx = (vx * c.x - cr.z) / (vx * c.z + cr.x); + float maxx = (vx * c.x + cr.z) / (vx * c.z - cr.x); + + float vy = sqrt(c.y * c.y + czr2); + float miny = (vy * c.y - cr.z) / (vy * c.z + cr.y); + float maxy = (vy * c.y + cr.z) / (vy * c.z - cr.y); + + aabb = vec4(minx * P00, miny * P11, maxx * P00, maxy * P11); + aabb = aabb*0.5 + vec4(0.5); + return true; + } + +bool frustrumTest(const vec4 sphere) { + if(sphere.w<=0.f) + return false; + for(int i=0; i<6; ++i) { + if(dot(vec4(sphere.xyz,1.0), scene.frustrum[i]) <= -sphere.w) { + return false; + } + } + return true; + } + +bool isSurfelVisible(const vec4 p, float radiusMean, ivec2 fragCoord) { + if(!frustrumTest(p)) + return false; + + const vec3 viewPos = (scene.view * vec4(p.xyz, 1)).xyz; + +#if 1 + if(0<=fragCoord.x && fragCoord.x radiusMean) { + // 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)) + return false; + + if(!hiZTest(aabb, depthMin)) + return false; +#endif + + return true; + } + +void main() { + const uint surfelId = gl_GlobalInvocationID.x; + if(surfelId >= header.count) + return; + + const Surfel s = surfels[surfelId]; + if(isSurfelVisible(vec4(s.pos, s.radius), s.radiusMean, s.fragCoord)) + return; + + surfels[surfelId].radius = 0; + } diff --git a/shader/lighting/surfels/surf_dbg.frag b/shader/lighting/surfels/surf_dbg.frag new file mode 100644 index 000000000..7e2ba6364 --- /dev/null +++ b/shader/lighting/surfels/surf_dbg.frag @@ -0,0 +1,51 @@ +#version 460 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +layout(binding = 1, std430) readonly buffer SB0 { uvec4 count; Surfel surfels[]; }; +layout(binding = 2) uniform usampler2D gbufNormal; +layout(binding = 3) uniform sampler2D depth; + +layout(location = 0) in vec3 center; +layout(location = 1) in vec3 normal; +layout(location = 2) in float radius; +layout(location = 3) in flat uint instanceIndex; + +layout(location = 0) out vec4 outColor; + +vec3 unprojectDepth(float z) { + const vec2 fragCoord = (gl_FragCoord.xy*scene.screenResInv)*2.0-vec2(1.0); + const vec4 pos = vec4(fragCoord.xy, z, 1.0);///gl_FragCoord.w); + const vec4 ret = scene.viewProjectInv*pos; + return (ret.xyz/ret.w); + } + +void main(void) { + //vec3 norm = normalFetch(gbufNormal, ivec2(gl_FragCoord.xy)); + float z = texelFetch(depth, ivec2(gl_FragCoord.xy), 0).x; + vec3 wpos = unprojectDepth(z); + vec3 view = normalize(wpos-scene.camPos); + + const Surfel p = surfels[instanceIndex]; + const vec3 delta = wpos - center; + const float distToPlane = dot(delta, normal); + const vec3 projectedPoint = (delta - distToPlane * normal) / radius; + const float qDist = dot(projectedPoint, projectedPoint); + + if(qDist > 1) + discard; + + if(length(delta) > radius) + ;//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); + 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 new file mode 100644 index 000000000..81f0c72ea --- /dev/null +++ b/shader/lighting/surfels/surf_dbg.vert @@ -0,0 +1,76 @@ +#version 450 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +layout(binding = 1, std430) readonly buffer SB0 { uvec4 count; Surfel surfels[]; }; +layout(binding = 3) uniform sampler2D depth; + +layout(location = 0) out vec3 center; +layout(location = 1) out vec3 normal; +layout(location = 2) out float radius; +layout(location = 3) out flat uint instanceIndex; + +const vec3 v[8] = { + {-1,-1,-1}, + { 1,-1,-1}, + { 1, 1,-1}, + {-1, 1,-1}, + + {-1,-1, 1}, + { 1,-1, 1}, + { 1, 1, 1}, + {-1, 1, 1}, + }; + +const uint index[36] = { + 0, 1, 3, 3, 1, 2, + 1, 5, 2, 2, 5, 6, + 5, 4, 6, 6, 4, 7, + 4, 0, 7, 7, 0, 3, + 3, 2, 7, 7, 2, 6, + 4, 5, 0, 0, 5, 1 + }; + +vec3 hasGridPos(vec3 wpos, float cellSize) { + wpos = wpos / cellSize; + return round(wpos); + } + +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; + } + +void main() { + const uint surfelId = gl_InstanceIndex; + if(surfelId>=count.x) { + gl_Position = vec4(0); + return; + } + + Surfel p = surfels[surfelId]; + + const vec3 vert = v[index[gl_VertexIndex]]; + const vec4 pp = scene.viewProject * vec4(p.pos, 1.0); + + float pixelRadius = 5.0; + float worldRadius = pixelToWorld(pixelRadius, pp.z/pp.w); + + //radius = 5; + radius = worldRadius; + gl_Position = scene.viewProject * vec4(p.pos + vert * radius, 1.0); + center = p.pos; + normal = decodeNormal(p.norm); + + const float fov = 67.5f*M_PI/180.0; + const float ld = max(linearDepth(pp.z/pp.w, scene.clipInfo), 10); + + instanceIndex = surfelId; + } diff --git a/shader/lighting/surfels/surf_decimate.comp b/shader/lighting/surfels/surf_decimate.comp new file mode 100644 index 000000000..1c6707f18 --- /dev/null +++ b/shader/lighting/surfels/surf_decimate.comp @@ -0,0 +1,73 @@ +#version 460 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(local_size_x = 64) in; + +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; + +layout(std140, push_constant) uniform Push { + vec3 originLwc; + uint tileSize; + }; +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +// +layout(binding = 3, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; +// +layout(binding = 6, r32ui) uniform uimage2D surfCnts; +layout(binding = 7, r32ui) uniform uimage2D surfBins; +layout(binding = 8, std430) readonly buffer SB2 { uint surfelList[]; }; + +void main() { + const uint surfelId = gl_GlobalInvocationID.x; + if(surfelId >= header.count) + return; + + const float edge = 1.57; // .57 is prefference driven + const Surfel p = surfels[surfelId]; + const ivec2 sz = scene.screenRes; + + if(p.radius==0) + return; // disabled + + if(!(-DefaultCoverage <= p.fragCoord.x && p.fragCoord.x < sz.x+DefaultCoverage)) + return; + if(!(-DefaultCoverage <= p.fragCoord.y && p.fragCoord.y < sz.y+DefaultCoverage)) + return; + + const vec3 wpos = p.pos; + const vec3 norm = decodeNormal(p.norm); + + const ivec2 tileId = (p.fragCoord/int(tileSize)); + + const ivec2 cTileId = clamp(tileId, ivec2(0), imageSize(surfCnts)-1); + const uint cnt = imageLoad(surfCnts, cTileId).x; + const uint ptr = imageLoad(surfBins, cTileId).x; + + float weight = 0; + for(uint i=0; i=surfelId) + break; // surfels are sorted + + 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) + continue; + + const float w = calculteWeight(s.pos, decodeNormal(s.norm), s.radiusMean, s.radius, wpos, norm); + weight += w; + + if(weight>edge) { + // too dense - can be removed + surfels[surfelId].radius = 0; + return; + } + } + } diff --git a/shader/lighting/surfels/surf_pathtrace.comp b/shader/lighting/surfels/surf_pathtrace.comp new file mode 100644 index 000000000..4ba6596d0 --- /dev/null +++ b/shader/lighting/surfels/surf_pathtrace.comp @@ -0,0 +1,204 @@ +#version 460 + +#define LIGHTING 1 +#define RAY_QUERY +#define RAY_QUERY_AT + +// #define USE_MIN_HIT_DIST +// #define RTAO + +#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 = 64) in; + +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; +const int numBounces = 5; +// const int numBounces = 1; + +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; + +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, bool shadowed, float softAngle, inout Random rngState) { +#if defined(SOFT_SHADOW) + vec3 shRay = randomizeRay(rayDirection, 0.5*M_PI/180.0, rngState); +#else + vec3 shRay = rayDirection; +#endif + + float lamb = max(dot(norm, rayDirection), 0); + if(!shadowed || lamb==0) + return lamb; + + float shadow = rayQueryProceedShadow(rayOrigin, shRay, rngState); + // float shadow = shadowFactor(rayOrigin); + return (lamb * shadow); + } + +vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { + vec3 thruput = vec3(1); + vec3 color = vec3(0); + bool underWater = (scene.underWater!=0); + float depth = 0; + + for(int bounce=0;; ++bounce) { + if(dot(thruput*scene.GSunIntensity*scene.exposure, vec3(0.2126, 0.7152, 0.0722)) < 0.01) + break; + + if(bounce>=numBounces) { + // return 0.5*vec4(1,0,1,depth)*scene.GSunIntensity; + // rescale to 100% thruput + /* + vec3 scale = 1.0-thruput; + if(scale.r>0.01 && scale.g>0.01 && scale.b>0.01) + return vec4(color/scale, depth); + */ + return vec4(color, depth); + } + + HitResolve hit = rayQueryProceedPrimary(rayOrigin, rayDirection, (bounce==0 ? 0 : -1), rngState); + if(bounce==0) + depth = abs(hit.rayT); + + if(hit.rayT<0) { + hit.rayT = abs(hit.rayT); // backfaces are fine for PT + hit.norm = -hit.norm; + } + + if(hit.rayT==TMax) { + vec3 sky = textureSkyLUT(skyLUT, vec3(0,RPlanet+max(rayOrigin.y*0.01,0),0), rayDirection, scene.sunDir) * scene.GSunIntensity; + color += thruput*sky; + color += thruput*(vec3(0.3, 0.26, 1)*0.15); //HACK for the night sky + break; + } + +#if defined(RTAO) + return vec4(color, depth); +#endif + + /* + if(underWater) { + const float depth = hit.rayT / 5000.0; // 50 meters + const vec3 transmittance = exp(-depth * vec3(4,2,1) * 1.25); + thruput *= transmittance; + } + + if(hit.water) { + const float ior = (underWater ? IorAir : IorWater); + const vec3 refl = reflect(rayDirection, hit.norm); + const float f = fresnel(refl,hit.norm,ior); + const bool path = (f>randf(rngState)); + + rayOrigin = (rayOrigin + rayDirection * hit.rayT); + rayDirection = path ? refl : refract(rayDirection, hit.norm, ior); + underWater = path ? underWater : !underWater; + thruput *= WaterAlbedo; + continue; + } + */ + + rayOrigin = (rayOrigin + rayDirection * hit.rayT); + rayDirection = randCosWeightedHemisphereDirection(hit.norm, rngState); + thruput *= min(textureAlbedo(hit.diff.rgb), 0.95); + //thruput *= vec3(0.85); //snow + //thruput *= vec3(0.04); // asphalt + //if(bounce>0) + // thruput *= groundAlbedo; + //if(bounce>0) + // thruput *= textureAlbedo(hit.diff.rgb); + + vec3 direct = vec3(0); + direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir, true, 0.54*M_PI/180.0, rngState) * scene.sunColor; + color += thruput*direct*Fd_Lambert; + } + + return vec4(color, depth); + } + +shared vec4 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; +#if defined(USE_MIN_HIT_DIST) + reduction[laneID].a = min(reduction[laneID].a, reduction[laneID + stride].a); +#else + reduction[laneID].a += reduction[laneID + stride].a; +#endif + } + 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); + // 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 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)); +#endif + barrier(); + + reduceIrr(); + + if(gl_LocalInvocationIndex==0) { +#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; +#endif + 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 new file mode 100644 index 000000000..bdfefd2e7 --- /dev/null +++ b/shader/lighting/surfels/surf_update.comp @@ -0,0 +1,46 @@ +#version 460 + +#include "lighting/surfels/surf_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(local_size_x = 64) in; + +layout(binding = 0, std140) uniform UboScene { + SceneDesc scene; + }; +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); + } + +void main() { + const uint surfelId = gl_GlobalInvocationID.x; + if(surfelId >= header.count) + return; + + const Surfel s = surfels[surfelId]; + + 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 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; + } diff --git a/shader/random.glsl b/shader/random.glsl new file mode 100644 index 000000000..3597a8bf5 --- /dev/null +++ b/shader/random.glsl @@ -0,0 +1,56 @@ +#ifndef RANDOM_GLSL +#define RANDOM_GLSL + +#include "common.glsl" + +struct Random { + uint state; + }; + +Random srand(uvec2 fragCoord, uint seed) { + Random r; + r.state = uint(uint(fragCoord.x) * uint(1973) + uint(fragCoord.y) * uint(9277) + uint(seed) * uint(26699)) | uint(1); + return r; + } + +float randf(inout Random r) { + return float(wangHash(r.state)) / 4294967296.0; + } + +vec3 randVec3(inout Random rng) { + float z = randf(rng) * 2.0 - 1.0; + float a = randf(rng) * 2.0 * M_PI; + float r = sqrt(1.0f - z * z); + float x = r * cos(a); + float y = r * sin(a); + return vec3(x, y, z); + } + +vec3 randCosWeightedHemisphereDirection(const vec3 n, inout Random rng) { + vec2 rv2 = vec2(randf(rng), randf(rng)); + + vec3 uu = normalize( cross( n, vec3(0.0,1.0,1.0) ) ); + vec3 vv = normalize( cross( uu, n ) ); + + float ra = sqrt(rv2.y); + float rx = ra*cos(6.2831*rv2.x); + float ry = ra*sin(6.2831*rv2.x); + float rz = sqrt( 1.0-rv2.y ); + vec3 rr = vec3( rx*uu + ry*vv + rz*n ); + + return normalize(rr); + } + +vec3 randCosWeightedHemisphereDirection(const mat3 tbn, inout Random rng) { + vec2 rv2 = vec2(randf(rng), randf(rng)); + + float ra = sqrt(rv2.y); + float rx = ra*cos(6.2831*rv2.x); + float ry = ra*sin(6.2831*rv2.x); + float rz = sqrt( 1.0-rv2.y ); + vec3 rr = vec3( rx*tbn[0] + ry*tbn[1] + rz*tbn[2] ); + + return normalize(rr); + } + +#endif \ No newline at end of file diff --git a/shader/rtsm/rtsm_dbg.frag b/shader/rtsm/rtsm_dbg.frag index 8d8adb529..fec371f81 100644 --- a/shader/rtsm/rtsm_dbg.frag +++ b/shader/rtsm/rtsm_dbg.frag @@ -14,30 +14,6 @@ layout(binding = 0) uniform utexture2D dbgImage; layout(location = 0) out vec4 outColor; -float drawInt(in vec2 where, in int n) { - const float RESOLUTION = 0.75; - int i=int((where*=RESOLUTION).y); - if(00 || n>0; n/=10) - if ((j+=4)<3 && j>=0) { - int x = 0; - if(i>4) - x = 972980223; - else if(i>3) - x = 690407533; - else if(i>2) - x = 704642687; - else if(i>1) - x = 696556137; - else - x = 972881535; - return float(x >> (29-j-(n%10)*3)&1); - } - } - return 0; - } - void main() { ivec2 fragCoord = ivec2(gl_FragCoord.xy); ivec2 tileSz = ivec2(32); diff --git a/shader/ssao/ssao.comp b/shader/ssao/ssao.comp index 1c0b66120..811263f63 100644 --- a/shader/ssao/ssao.comp +++ b/shader/ssao/ssao.comp @@ -97,16 +97,10 @@ bool isSolved(float diff, int samples) { // return d < 0.1 + samples*0.005; } -float whangHashNoise(uvec2 uv, uint s) { +float wangHashNoise(uvec2 uv, uint s) { uint seed = (uv.x*1664525u + uv.y) + s; - - seed = (seed ^ 61u) ^(seed >> 16u); - seed *= 9u; - seed = seed ^(seed >> 4u); - seed *= uint(0x27d4eb2d); - seed = seed ^(seed >> 15u); - - float value = float(seed) / (4294967296.0); + uint hash = wangHash(seed); + float value = float(hash) / (4294967296.0); return value; } @@ -144,7 +138,7 @@ void calcOcclussion(const vec3 at0, const vec3 pos0, const float f0, uint seed, for(uint i0=0; i00 || n>0; n/=10) - if ((j+=4)<3 && j>=0) { - int x = 0; - if(i>4) - x = 972980223; - else if(i>3) - x = 690407533; - else if(i>2) - x = 704642687; - else if(i>1) - x = 696556137; - else - x = 972881535; - return float(x >> (29-j-(n%10)*3)&1); - } - } - return 0; - } - uint hash(uvec3 src) { const uint M = 0x5bd1e995u; uint h = 1190494759u;