diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index 8d73c4f5d..5c1a38aac 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -339,6 +339,14 @@ bool Renderer::requiresTlas() const { return false; } +bool Renderer::requiresLightsTree() const { + if(!Shaders::isLightsTreeSupported()) + return false; + if(settings.giMethod==GiMethod::IrrC || settings.pathTraceEnabled) + return true; + return true; + } + StorageImage& Renderer::usesImage3d(Tempest::StorageImage& ret, Tempest::TextureFormat frm, uint32_t w, uint32_t h, uint32_t d, bool mips) { if(ret.format()==frm && uint32_t(ret.w())==w && uint32_t(ret.h())==h && uint32_t(ret.d())==d && bool(ret.mipCount()>1)==mips) return ret; @@ -634,6 +642,9 @@ void Renderer::draw(Tempest::Attachment& result, Encoder& cmd, ui wview->updateRtScene(); wview->updateLights(); + if(requiresLightsTree()) + prepareLightsBvh(cmd, *wview); + updateCamera(*camera); static bool updFr = true; @@ -693,6 +704,7 @@ void Renderer::draw(Tempest::Attachment& result, Encoder& cmd, ui drawAmbient(cmd,*wview); drawLights(cmd,*wview); drawSky(cmd,*wview); + drawLightTreeDbg(sceneLinear, cmd, *wview); stashSceneAux(cmd); @@ -1049,6 +1061,30 @@ void Renderer::drawHashDbg(Attachment& result, Tempest::Encoder& cmd, const WorldView& wview) { + static bool enable = false; + if(!enable) + return; + + auto& scene = wview.sceneGlobals(); + + struct Push { + Vec3 originLwc; + } push; + push.originLwc = scene.originLwc; + + cmd.setDebugMarker("LightTree-dbg"); + cmd.setPushData(push); + cmd.setBinding(0, scene.uboGlobal[SceneGlobals::V_Main]); + cmd.setBinding(1, gbufNormal); + cmd.setBinding(2, zbuffer); + cmd.setBinding(3, lightsTree.bvh); + + cmd.setFramebuffer({{result, Tempest::Preserve, Tempest::Preserve}}); + cmd.setPipeline(shaders.lightsTreeDbg); + cmd.draw(nullptr, 0, 3); + } + void Renderer::drawHiZ(Encoder& cmd, WorldView& view) { cmd.setDebugMarker("HiZ-occluders"); cmd.setFramebuffer({}, {zbuffer, 1.f, Tempest::Preserve}); @@ -1954,6 +1990,59 @@ void Renderer::prepareEpipolar(Tempest::Encoder& cmd, Wo cmd.dispatch(uint32_t(epTrace.h())); } +void Renderer::prepareLightsBvh(Tempest::Encoder& cmd, WorldView& wview) { + if(!Shaders::isLightsTreeSupported()) + return; + + struct Push { + uint32_t numLightsPot; + uint32_t numLights; + } push = {}; + const uint32_t numLights = uint32_t(wview.lights().size()); + push.numLightsPot = nextPot(numLights); + push.numLights = numLights; + + auto& lightsSsbo = wview.lights().lightsSsbo(); + auto& bvhMorton = usesSsbo(lightsTree.bvhMorton, shaders.lightsBvh.sizeofBuffer(1, push.numLightsPot)); + auto& bvhAlux = usesSsbo(lightsTree.bvhAlux, shaders.lightsBvh.sizeofBuffer(3, numLights * 2)); + auto& ctrl = usesSsboInit(lightsTree.bvhCtrl, shaders.lightsBvh.sizeofBuffer(4, (numLights + 31 )/32)); + + const bool ltree = settings.pathTraceEnabled; + const bool lbvh = (settings.giMethod==GiMethod::IrrC); + + cmd.setDebugMarker("LightsTree"); + cmd.setFramebuffer({}); + cmd.setPushData(push); + cmd.setBinding(0, lightsSsbo); + cmd.setBinding(1, bvhMorton); + // + cmd.setBinding(3, bvhAlux); + cmd.setBinding(4, ctrl); + + cmd.setPipeline(shaders.lightsMorton); + cmd.dispatch(1); // single pass, for simplicity + + if(ltree) { + auto& tree = usesSsbo(lightsTree.tree, shaders.lightsBvh.sizeofBuffer(2, numLights * 2)); + cmd.setBinding(2, tree); + cmd.setPipeline(shaders.lightsTopology); + cmd.dispatchThreads(numLights); + + cmd.setPipeline(shaders.lightsTree); + cmd.dispatchThreads(numLights); + } + + if(lbvh) { + auto& bvh = usesSsbo(lightsTree.bvh, shaders.lightsBvh.sizeofBuffer(2, numLights * 2)); + cmd.setBinding(2, bvh); + cmd.setPipeline(shaders.lightsTopology); + cmd.dispatchThreads(numLights); + + cmd.setPipeline(shaders.lightsBvh); + cmd.dispatchThreads(numLights); + } + } + void Renderer::prepareSurfels(Tempest::Encoder& cmd, WorldView& wview) { static bool enable = true; if(!enable) @@ -2184,6 +2273,7 @@ void Renderer::surfelsTrace(Tempest::Encoder& cmd, World cmd.setBinding(10,scene.rtScene.ibo); cmd.setBinding(11,scene.rtScene.rtDesc); cmd.setBinding(12,shadowMap[1]); + cmd.setBinding(13,lightsTree.bvh); cmd.setPipeline(*pso); const uint32_t offset = postPass ? sizeof(uint32_t) : 0; @@ -2423,7 +2513,7 @@ void Renderer::drawPathtrace(Tempest::Encoder& cmd, Worl cmd.setBinding(3, sky.viewCldLut, sky.sampler); //cmd.setBinding(4, shadowMap[1], Sampler::bilinear()); cmd.setBinding(4, Resources::fallbackBlack(), Sampler::bilinear()); - // + cmd.setBinding(5, lightsTree.tree); cmd.setBinding(6, scene.rtScene.tlas); cmd.setBinding(7, Sampler::trillinear()); cmd.setBinding(8, scene.rtScene.tex); diff --git a/game/graphics/renderer.h b/game/graphics/renderer.h index 64f0bc189..5e873f027 100644 --- a/game/graphics/renderer.h +++ b/game/graphics/renderer.h @@ -44,6 +44,7 @@ class Renderer final { void updateCamera(const Camera &camera); bool requiresTlas() const; + bool requiresLightsTree() const; Tempest::StorageImage& usesImage2d(Tempest::StorageImage& ret, Tempest::TextureFormat frm, uint32_t w, uint32_t h, bool mips = false); Tempest::StorageImage& usesImage2d(Tempest::StorageImage& ret, Tempest::TextureFormat frm, Tempest::Size sz, bool mips = false); @@ -67,6 +68,8 @@ class Renderer final { void prepareExposure (Tempest::Encoder& cmd, WorldView& wview); void prepareEpipolar (Tempest::Encoder& cmd, WorldView& wview); + void prepareLightsBvh (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); @@ -107,6 +110,7 @@ class Renderer final { 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 drawLightTreeDbg (Tempest::Attachment& result, Tempest::Encoder& cmd, const WorldView& wview); void setupSettings(); void toggleGi(); @@ -166,6 +170,11 @@ class Renderer final { Tempest::RenderPipeline* directLightPso = nullptr; } lights; + struct { + Tempest::StorageBuffer bvh, tree; + Tempest::StorageBuffer bvhMorton, bvhAlux, bvhCtrl; + } lightsTree; + struct Sky { Quality quality = Quality::None; @@ -215,7 +224,7 @@ class Renderer final { } gi; struct { - const Tempest::IVec2 gbufTile = {8, 8}; + const Tempest::IVec2 gbufTile = {8}; const uint32_t gbufTilesX = 256; const uint32_t gbufTilesY = 256; const uint32_t maxSurfels = gbufTilesX*gbufTilesY; diff --git a/game/graphics/rtscene.cpp b/game/graphics/rtscene.cpp index 2fbc4edaf..e43981103 100644 --- a/game/graphics/rtscene.cpp +++ b/game/graphics/rtscene.cpp @@ -69,8 +69,10 @@ void RtScene::addInstance(const Matrix4x4& pos, const AccelerationStructure& bla ix.flags = RtInstanceFlags::Opaque; ix.flags = ix.flags | RtInstanceFlags::CullFlip; +#if 1 if(mat.alpha==Material::Solid) ix.flags = ix.flags | RtInstanceFlags::CullDisable; +#endif if(mat.alpha==Material::Solid) ix.mask = CullMask::CM_Opaque; diff --git a/game/graphics/shaders.cpp b/game/graphics/shaders.cpp index 76fc88d2e..cd56ab98a 100644 --- a/game/graphics/shaders.cpp +++ b/game/graphics/shaders.cpp @@ -268,6 +268,15 @@ void Shaders::compileShaders() { surLighting = computeShader("surf_lighting.comp.sprv"); } + if(Shaders::isLightsTreeSupported()) { + lightsMorton = computeShader("lights_morton.comp.sprv"); + lightsTopology = computeShader("lights_topology.comp.sprv"); + lightsTree = computeShader("lights_tree.comp.sprv"); + lightsBvh = computeShader("lights_bvh.comp.sprv"); + + lightsTreeDbg = postEffect("triangle_uv", "lightstree_dbg"); + } + if(Shaders::isVsmSupported()) { fogEpipolarOcclusion = computeShader("fog_epipolar_occlusion.comp.sprv"); fogEpipolarVsm = computeShader("fog_epipolar_vsm.comp.sprv"); @@ -388,6 +397,13 @@ bool Shaders::isRtsmSupported() { return true; } +bool Shaders::isLightsTreeSupported() { + auto& gpu = Resources::device().properties(); + if(gpu.compute.maxInvocations<512 || gpu.compute.maxSharedMemory<16*1024) + return false; + return true; + } + bool Shaders::isGi1Supported() { auto& gpu = Resources::device().properties(); if(!gpu.raytracing.rayQuery) diff --git a/game/graphics/shaders.h b/game/graphics/shaders.h index d6e774c68..97e29b58d 100644 --- a/game/graphics/shaders.h +++ b/game/graphics/shaders.h @@ -27,6 +27,7 @@ class Shaders { static Shaders& inst(); static bool isVsmSupported(); static bool isRtsmSupported(); + static bool isLightsTreeSupported(); static bool isGi1Supported(); static bool isGi2Supported(); @@ -95,6 +96,10 @@ class Shaders { Tempest::ComputePipeline surfPathtrace, surfRaycast, surLighting; + // Lights tree + Tempest::ComputePipeline lightsMorton, lightsTopology, lightsTree, lightsBvh; + Tempest::RenderPipeline lightsTreeDbg; + // Epipolar Tempest::ComputePipeline fogEpipolarVsm; Tempest::ComputePipeline fogEpipolarOcclusion; diff --git a/game/world/objects/item.cpp b/game/world/objects/item.cpp index 244fe64f7..560243bf7 100644 --- a/game/world/objects/item.cpp +++ b/game/world/objects/item.cpp @@ -189,11 +189,11 @@ void Item::setPhysicsEnable(const MeshObjects::Mesh& view) { } void Item::setPhysicsEnable(const ProtoMesh* mesh) { - if(bBox()==nullptr) + if(mesh==nullptr) return; auto& p = *world.physic(); Bounds b; - b.assign(bBox()); + b.assign(mesh->bboxMesh); physic = p.dynamicObj(transform(),b,zenkit::MaterialGroup(hitem->material)); physic.setItem(this); } diff --git a/game/world/worldlight.cpp b/game/world/worldlight.cpp index 7263c69d5..0fb954498 100644 --- a/game/world/worldlight.cpp +++ b/game/world/worldlight.cpp @@ -4,6 +4,11 @@ WorldLight::WorldLight(Vob* parent, World& world, const zenkit::VLight& vob, Flags flags) : Vob(parent,world,vob,flags) { +#if 0 + if(!(flags&Startup)) + return; +#endif + light = world.addLight(vob); if(!vob.is_static && !vob.on) { light.setEnabled(false); diff --git a/shader/CMakeLists.txt b/shader/CMakeLists.txt index be5fc299e..d4dfc8575 100644 --- a/shader/CMakeLists.txt +++ b/shader/CMakeLists.txt @@ -331,7 +331,14 @@ 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) +add_shader(pathtrace.frag lighting/pt/pathtrace.frag -DRAY_QUERY -DRAY_QUERY_AT) + +add_shader(lights_morton.comp lighting/lightstree/lights_morton.comp) +add_shader(lights_topology.comp lighting/lightstree/lights_topology.comp) +add_shader(lights_tree.comp lighting/lightstree/lights_tree.comp) +add_shader(lights_bvh.comp lighting/lightstree/lights_bvh.comp) + +add_shader(lightstree_dbg.frag lighting/lightstree/lightstree_dbg.frag) # CMAA2 add_shader(cmaa2_edges_color2x2_quality_0.comp antialiasing/cmaa2/edge_color2x2.comp -DCMAA2_STATIC_QUALITY_PRESET=0) diff --git a/shader/common.glsl b/shader/common.glsl index f5624a51c..d8286da81 100644 --- a/shader/common.glsl +++ b/shader/common.glsl @@ -246,6 +246,43 @@ vec3 i_octahedral_32( uint data ) { return normalize(nor); } +uint floatToOrderedUint(float value) { + uint uvalue = floatBitsToUint(value); + uint mask = -int(uvalue >> 31) | 0x80000000; + return uvalue ^ mask; + } + +uvec4 floatToOrderedUint(vec4 value) { + uvec4 r; + r.x = floatToOrderedUint(value.x); + r.y = floatToOrderedUint(value.y); + r.z = floatToOrderedUint(value.z); + r.w = floatToOrderedUint(value.w); + return r; + } + +float orderedUintToFloat(uint value) { + uint mask = ((value >> 31) - 1) | 0x80000000; + return uintBitsToFloat(value ^ mask); + } + +vec3 orderedUintToFloat(uvec3 value) { + vec3 r; + r.x = orderedUintToFloat(value.x); + r.y = orderedUintToFloat(value.y); + r.z = orderedUintToFloat(value.z); + return r; + } + +vec4 orderedUintToFloat(uvec4 value) { + vec4 r; + r.x = orderedUintToFloat(value.x); + r.y = orderedUintToFloat(value.y); + r.z = orderedUintToFloat(value.z); + r.w = orderedUintToFloat(value.w); + return r; + } + // the next packing/unpacking methods are taken from // https://github.com/Microsoft/DirectX-Graphics-Samples/blob/master/MiniEngine/Core/Shaders/PixelPacking_R11G11B10.hlsli uint packR11G11B10F(vec3 rgb) { diff --git a/shader/lighting/light.vert b/shader/lighting/light.vert index eb7bef77f..0fb903f22 100644 --- a/shader/lighting/light.vert +++ b/shader/lighting/light.vert @@ -17,7 +17,7 @@ layout(binding = 0, std140) uniform UboScene { SceneDesc scene; }; -layout(binding = 4, std140) readonly buffer SsboLighting { +layout(binding = 4, std430) readonly buffer SsboLighting { LightSource lights[]; }; diff --git a/shader/lighting/lightstree/bvh_build_common.glsl b/shader/lighting/lightstree/bvh_build_common.glsl new file mode 100644 index 000000000..576cb4d6e --- /dev/null +++ b/shader/lighting/lightstree/bvh_build_common.glsl @@ -0,0 +1,18 @@ +#ifndef BVH_BUILD_COMMON_GLSL +#define BVH_BUILD_COMMON_GLSL + +struct MortonHeader { + uint numActiveLights; + uint padd0, padd1, padd2; + }; + +struct MortonPair { + uint key; + uint id; + }; + +struct BVHAlux { + uint parent; + }; + +#endif \ No newline at end of file diff --git a/shader/lighting/lightstree/lights_bvh.comp b/shader/lighting/lightstree/lights_bvh.comp new file mode 100644 index 000000000..bc0f25c4e --- /dev/null +++ b/shader/lighting/lightstree/lights_bvh.comp @@ -0,0 +1,93 @@ +#version 450 + +#include "lighting/lightstree/lights_common.glsl" +#include "lighting/lightstree/bvh_build_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(binding = 0, std430) readonly buffer LS { + LightSource lights[]; + }; + +layout(binding = 1, std430) readonly buffer B1 { + MortonHeader header; MortonPair morton[]; + }; + +layout(binding = 2, std430) buffer B2 { + BVHNode nodes[]; + }; + +layout(binding = 3, std430) buffer B3 { + BVHAlux alux[]; + }; + +layout(binding = 4, std430) buffer B4 { + uint parentbits[]; + }; + +void computeHeirarchy(const LightSource light, uint nodeId) { + vec3 bboxMin = light.pos - light.range; + vec3 bboxMax = light.pos + light.range; + + nodes[nodeId].lmin = light.pos; + nodes[nodeId].lmax = light.color; + nodes[nodeId].ptrL = floatBitsToUint(light.range); + + while(nodeId>0) { + const uint parent = alux[nodeId].parent; + const uint parentL = nodes[parent].ptrL & 0x0FFFFFFF; + if(parentL==nodeId) { + nodes[parent].lmin = bboxMin; + nodes[parent].lmax = bboxMax; + } else { + nodes[parent].rmin = bboxMin; + nodes[parent].rmax = bboxMax; + } + + const uint bit = 1u << (parent%32); + memoryBarrierBuffer(); + if((atomicXor(parentbits[parent/32], bit) & bit)==0) { + break; + } + + nodeId = parent; + + BVHNode node = nodes[nodeId]; + bboxMin = min(node.lmin, node.rmin); + bboxMax = max(node.lmax, node.rmax); + } + } + +void emitTrivialBVH(const LightSource light) { + vec3 bboxMin = light.pos - light.range; + vec3 bboxMax = light.pos + light.range; + + nodes[1].lmin = light.pos; + nodes[1].lmax = light.color; + nodes[1].ptrL = floatBitsToUint(light.range); + + nodes[0].lmin = bboxMin; + nodes[0].lmax = bboxMax; + nodes[0].ptrL = 1 | BVH_LightNode; + nodes[0].rmin = vec3(0); + nodes[0].rmax = vec3(0); + nodes[0].ptrR = BVH_NullNode; + } + +void main() { + const uint i = gl_GlobalInvocationID.x; + if(i >= header.numActiveLights) + return; + + if(header.numActiveLights==1) { + emitTrivialBVH(lights[morton[0].id]); + return; + } + + const uint internalCount = header.numActiveLights - 1; + computeHeirarchy(lights[morton[i].id], internalCount + i); + } diff --git a/shader/lighting/lightstree/lights_common.glsl b/shader/lighting/lightstree/lights_common.glsl new file mode 100644 index 000000000..e1ac3f23c --- /dev/null +++ b/shader/lighting/lightstree/lights_common.glsl @@ -0,0 +1,61 @@ +#ifndef LIGHTSTREE_COMMON_GLSL +#define LIGHTSTREE_COMMON_GLSL + +#include "common.glsl" + +const uint BVH_NullNode = 0x00000000; +const uint BVH_BoxNode = 0x10000000; +const uint BVH_LightNode = 0x40000000; + +struct PTreeNode { + vec3 centerL; float weightL; + uvec3 padd0; uint ptrL; + vec3 centerR; float weightR; + uvec3 padd1; uint ptrR; + }; + +struct BVHNode { + vec3 lmin; uint padd0; + vec3 lmax; uint ptrL; + vec3 rmin; uint padd1; + vec3 rmax; uint ptrR; + }; + +uint bvhGetNodeType(uint ptr) { + return ptr & 0xF0000000; + } + +bool bvhIntersectBox(vec3 point, vec3 lo, vec3 hi){ + return all(lessThanEqual(lo, point)) && all(lessThanEqual(point, hi)); + } + +float grayscale(vec3 color) { + return dot(color, vec3(0.2125, 0.7154, 0.0721)); + } + +float bvhLightsNodeWeight(const vec3 wpos, const vec3 norm, const vec3 cen, float wFlux) { + vec3 dlen = wpos - cen; + float distSq = max(dot(dlen, dlen), 0.0001f); + float distanceAttenuation = 1.0 / distSq; + return distanceAttenuation * wFlux; + } + +float squareFalloffAttenuation(const float distance, const float lightInvRadius) { + float distanceSquare = (distance * distance); + float factor = distanceSquare * lightInvRadius * lightInvRadius; + float smoothFactor = max(1.0 - factor * factor, 0.0); + return (smoothFactor * smoothFactor) / max(factor, 0.005); + } + +//FIXME: copypaste of RTSM; duplicate if lights.frag logic +float lightIntensity(const vec3 normal, const float distance, const vec3 ldir, const float lightRange) { + if(distance>lightRange) + return 0; + + float falloff = squareFalloffAttenuation(distance, 1.0/lightRange); + float lambert = max(0.0,-dot(ldir,normal)); + + return lambert * falloff * 0.25; + } + +#endif diff --git a/shader/lighting/lightstree/lights_morton.comp b/shader/lighting/lightstree/lights_morton.comp new file mode 100644 index 000000000..987a09eaf --- /dev/null +++ b/shader/lighting/lightstree/lights_morton.comp @@ -0,0 +1,154 @@ +#version 450 + +#include "lighting/lightstree/bvh_build_common.glsl" +#include "lighting/lightstree/lights_common.glsl" +#include "scene.glsl" +#include "common.glsl" + +layout(local_size_x = 512) in; + +const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; + +layout(push_constant, std140) uniform Push { + uint numLightsPot; + uint numLights; + }; + +layout(binding = 0, std430) readonly buffer LS { + LightSource lights[]; + }; + +layout(binding = 1, std430) buffer B1 { + MortonHeader header; MortonPair morton[]; + }; + +layout(binding = 2, std430) buffer B2 { + mat4 nodes[]; + }; + +shared uvec3 bboxMinU, bboxMaxU; +shared vec3 bboxMin, bboxMax; +shared uint numActiveLights; + +void bboxMinMax() { + const uint laneID = gl_LocalInvocationIndex; + + if(laneID == 0) { + bboxMinU.x = 0xFFFFFFFFu; + bboxMinU.y = 0xFFFFFFFFu; + bboxMinU.z = 0xFFFFFFFFu; + + bboxMaxU.x = 0u; + bboxMaxU.y = 0u; + bboxMaxU.z = 0u; + + numActiveLights = 0; + } + barrier(); + + for(uint i=laneID; i B.key) : (A.key < B.key); + if(swap) { + morton[a] = B; + morton[b] = A; + } + } + +void computeMorton() { + const uint laneID = gl_LocalInvocationIndex; + + for(uint i=laneID; i0 ? morton3D(lights[i].pos) : 0xFFFFFFFF; + morton[i].id = i; + } + for(uint i=numLights+laneID; i> 1; j > 0; j >>= 1) { + for(uint i = laneID; i < numLightsPot; i += NumThreads) { + uint ixj = i ^ j; + + if(ixj > i) { + bool ascending = ((i & k) == 0); + compareSwap(i, ixj, ascending); + } + } + memoryBarrierBuffer(); + barrier(); + } + } + } + +void emitTrivialBVH() { + if(gl_LocalInvocationIndex!=0) + return; + nodes[0] = mat4(0); + } + +void main() { + bboxMinMax(); + barrier(); + + computeMorton(); + barrier(); + + if(header.numActiveLights==0) { + emitTrivialBVH(); + return; + } + + bitonicSort(); + } diff --git a/shader/lighting/lightstree/lights_topology.comp b/shader/lighting/lightstree/lights_topology.comp new file mode 100644 index 000000000..95860de58 --- /dev/null +++ b/shader/lighting/lightstree/lights_topology.comp @@ -0,0 +1,121 @@ +#version 450 + +#include "lighting/lightstree/lights_common.glsl" +#include "lighting/lightstree/bvh_build_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(binding = 0, std430) readonly buffer LS { + LightSource lights[]; + }; + +layout(binding = 1, std430) readonly buffer B1 { + MortonHeader header; MortonPair morton[]; + }; + +layout(binding = 2, std430) buffer B2 { + PTreeNode nodes[]; + }; + +layout(binding = 3, std430) buffer B3 { + BVHAlux alux[]; + }; + +int delta(int i, uint codeI, int j) { + if(j < 0 || j >= header.numActiveLights) + return -1; + + uint codeJ = morton[j].key; + if(codeI == codeJ) { + // handle duplicate morton codes + uint elementIdxI = morton[i].id; + uint elementIdxJ = morton[j].id; + // add 32 for common prefix of codeI ^ codeJ + return 32 + 31 - findMSB(elementIdxI ^ elementIdxJ); + } + return 31 - findMSB(codeI ^ codeJ); + } + +int findSplit(int first, int last) { + uint firstCode = morton[first].key; + + // Calculate the number of highest bits that are the same + // for all objects, using the count-leading-zeros intrinsic. + int commonPrefix = delta(first, firstCode, last); + int split = first; // initial guess + int stride = last - first; + + do { + stride = (stride + 1) >> 1;// exponential decrease + int newSplit = split + stride;// proposed new position + if (newSplit < last) { + int splitPrefix = delta(first, firstCode, newSplit); + if(splitPrefix > commonPrefix) { + split = newSplit; // accept proposal + } + } + } while (stride > 1); + + return split; + } + +void determineRange(int idx, out int lower, out int upper) { + // determine direction of the range (+1 or -1) + const uint code = morton[idx].key; + const int deltaL = delta(idx, code, idx - 1); + const int deltaR = delta(idx, code, idx + 1); + const int d = (deltaR >= deltaL) ? 1 : -1; + + // compute upper bound for the length of the range + const int deltaMin = min(deltaL, deltaR); // delta(idx, code, idx - d); + int lMax = 2; + while (delta(idx, code, idx + lMax * d) > deltaMin) { + lMax = lMax << 1; + } + + // find the other end using binary search + int l = 0; + for(int t = lMax >> 1; t > 0; t >>= 1) { + if(delta(idx, code, idx + (l + t) * d) > deltaMin) { + l += t; + } + } + int jdx = idx + l * d; + + // ensure idx < jdx + lower = min(idx, jdx); + upper = max(idx, jdx); + } + +uint bvhAnnotate(uint ptr) { + uint internalCount = header.numActiveLights - 1; + uint nodeType = (ptr < internalCount ? BVH_BoxNode : BVH_LightNode); + return ptr | nodeType; + } + +void main() { + + const uint i = gl_GlobalInvocationID.x; + if(i > header.numActiveLights) + return; + + const uint internalCount = header.numActiveLights - 1; + + int first = 0; + int last = 0; + determineRange(int(i), first, last); + + int split = findSplit(first,last); + uint left = (split == first) ? uint(internalCount + split) : uint(split); + uint right = (split+1 == last ) ? uint(internalCount + split + 1) : uint(split + 1); + + nodes[i].ptrL = bvhAnnotate(left); + nodes[i].ptrR = bvhAnnotate(right); + + alux[left ].parent = i; + alux[right].parent = i; + } diff --git a/shader/lighting/lightstree/lights_tree.comp b/shader/lighting/lightstree/lights_tree.comp new file mode 100644 index 000000000..2178a7eb6 --- /dev/null +++ b/shader/lighting/lightstree/lights_tree.comp @@ -0,0 +1,100 @@ +#version 450 + +#include "lighting/lightstree/lights_common.glsl" +#include "lighting/lightstree/bvh_build_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(binding = 0, std430) readonly buffer LS { + LightSource lights[]; + }; + +layout(binding = 1, std430) readonly buffer B1 { + MortonHeader header; MortonPair morton[]; + }; + +layout(binding = 2, std430) buffer B2 { + PTreeNode nodes[]; + }; + +layout(binding = 3, std430) buffer B3 { + BVHAlux alux[]; + }; + +layout(binding = 4, std430) buffer B4 { + uint parentbits[]; + }; + +void computeHeirarchy(const LightSource light, uint nodeId) { + float weight = light.range * grayscale(light.color) + 1; + vec3 center = light.pos; + + nodes[nodeId].centerL = center; + nodes[nodeId].weightL = weight; + nodes[nodeId].centerR = light.color; + nodes[nodeId].weightR = light.range; + //TODO: ball-tree? + + while(nodeId>0) { + const uint parent = alux[nodeId].parent; + const uint parentL = nodes[parent].ptrL & 0x0FFFFFFF; + if(parentL==nodeId) { + nodes[parent].centerL = center; + nodes[parent].weightL = weight; + } else { + nodes[parent].centerR = center; + nodes[parent].weightR = weight; + } + + const uint bit = 1u << (parent%32); + memoryBarrierBuffer(); + if((atomicXor(parentbits[parent/32], bit) & bit)==0) { + break; + } + + nodeId = parent; + + PTreeNode node = nodes[nodeId]; + weight = node.weightL + node.weightR; + center = node.centerL*node.weightL + node.centerR*node.weightR; + if(weight>0) { + center /= weight; + } + } + } + +void emitTrivialBVH(const LightSource light) { + float weight = light.range * grayscale(light.color) + 1; + vec3 center = light.pos; + + nodes[1].centerL = center; + nodes[1].weightL = weight; + nodes[1].centerR = light.color; + nodes[1].weightR = light.range; + + nodes[0].centerL = center; + nodes[0].weightL = weight; + nodes[0].ptrL = 1 | BVH_LightNode; + nodes[0].centerR = vec3(0); + nodes[0].weightR = 0; + nodes[0].ptrR = BVH_NullNode; + } + + +void main() { + const uint i = gl_GlobalInvocationID.x; + if(i >= header.numActiveLights) + return; + + if(header.numActiveLights==1) { + emitTrivialBVH(lights[morton[0].id]); + return; + } + + const uint internalCount = header.numActiveLights - 1; + computeHeirarchy(lights[morton[i].id], internalCount + i); + } diff --git a/shader/lighting/lightstree/lightstree_dbg.frag b/shader/lighting/lightstree/lightstree_dbg.frag new file mode 100644 index 000000000..aa8c30079 --- /dev/null +++ b/shader/lighting/lightstree/lightstree_dbg.frag @@ -0,0 +1,134 @@ +#version 460 + +#define LIGHTS_BVH + +#include "lighting/lightstree/lights_common.glsl" +#include "scene.glsl" +#include "common.glsl" +#include "random.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 usampler2D gbufNormal; +layout(binding = 2) uniform texture2D depth; +layout(binding = 3, std430) readonly buffer BVH { +#if defined(LIGHTS_BVH) + BVHNode node[]; +#else + PTreeNode node[]; +#endif + } bvhData; + +#if !defined(LIGHTS_BVH) +vec3 traverseLightTree(const vec3 wpos, const vec3 norm, inout Random rng) { + float pdf = 1.0; + float key = randf(rng); + uint node = 0 | BVH_BoxNode; + + while(node!=0) { + const uint type = bvhGetNodeType(node); + const PTreeNode n = bvhData.node[node & 0x0FFFFFFF]; + + if(type==BVH_LightNode) { + // light + const vec3 distance = wpos - n.centerL; + const float tMax = length(distance); + const vec3 ldir = distance/tMax; + const float intensity = lightIntensity(norm, tMax, ldir, n.weightR); + return intensity * n.centerR; + } + + const float wLeft = bvhLightsNodeWeight(wpos, norm, n.centerL, n.weightL); + const float wRight = bvhLightsNodeWeight(wpos, norm, n.centerR, n.weightR); + const float pLeft = wLeft /(wLeft + wRight); + const float pRight = wRight/(wLeft + wRight); + if(key < pLeft) { + pdf *= pLeft; + node = n.ptrL; + key = key/pLeft; + } else { + pdf *= pRight; + node = n.ptrR; + key = (key-pLeft)/pRight; + } + } + + return vec3(0); + } +#endif + +#if defined(LIGHTS_BVH) +vec3 traverseLightBvh(const vec3 wpos, const vec3 norm) { + uint stack[32]; + uint ptr = 0; + + vec3 ret = vec3(0); + uint node = 0 | BVH_BoxNode; + while(node!=0) { + const uint type = bvhGetNodeType(node); + const BVHNode n = bvhData.node[node & 0x0FFFFFFF]; + + if(ptr==stack.length()) { + // stack overflow + return vec3(1,0,0); + } + + if(type==BVH_LightNode) { + // light + const vec3 src = n.lmin; + const vec3 distance = wpos - src; + const float tMax = length(distance); + const vec3 ldir = distance/tMax; + const float intensity = lightIntensity(norm, tMax, ldir, uintBitsToFloat(n.ptrL)); + ret += intensity * n.lmax.rgb; + } + else { + const bool left = bvhIntersectBox(wpos, n.lmin, n.lmax); + const bool right = bvhIntersectBox(wpos, n.rmin, n.rmax); + if(left && right) { + node = n.ptrL; + stack[ptr++] = n.ptrR; + continue; + } + else if(left) { + node = n.ptrL; + continue; + } + else if(right) { + node = n.ptrR; + continue; + } + } + if(ptr == 0) + break; + node = stack[--ptr]; + } + + return ret; + } +#endif + +void main() { + 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 vec4 pos4 = scene.viewProjectLwcInv*vec4(inUV * 2.0 - 1.0, d, 1); + + vec3 wpos = pos4.xyz/pos4.w + originLwc; +#if defined(LIGHTS_BVH) + vec3 clr = traverseLightBvh(wpos, norm); +#else + Random rng = srand(uvec2(gl_FragCoord.xy), 0); + vec3 clr = traverseLightTree(wpos, norm, rng); +#endif + outColor = vec4(clr * max(1.0, scene.exposure) * Fd_Lambert, 1); + } diff --git a/shader/lighting/pt/pathtrace.frag b/shader/lighting/pt/pathtrace.frag index 279a2197c..7b50d34ba 100644 --- a/shader/lighting/pt/pathtrace.frag +++ b/shader/lighting/pt/pathtrace.frag @@ -5,9 +5,11 @@ #define SOFT_SHADOW // #define RESCALE +#define LOCAL_LIGHTS #include "lighting/rt/rt_common.glsl" #include "lighting/pt/pathtrace_common.glsl" +#include "lighting/lightstree/lights_common.glsl" #include "lighting/tonemapping.glsl" #include "scene.glsl" #include "common.glsl" @@ -26,6 +28,9 @@ layout(binding = 0, std140) uniform UboScene { layout(binding = 2) uniform texture2D irradiance; layout(binding = 3) uniform sampler2D skyLUT; layout(binding = 4) uniform sampler2D textureSm1; +layout(binding = 5, std430) readonly buffer BVH { + PTreeNode node[]; + } bvhData; vec3 skyIrradiance(vec3 n) { ivec3 d; @@ -62,7 +67,10 @@ float shadowFactor(vec3 pos) { } -vec3 randomizeRay(vec3 ray, float angle, inout Random rngState) { +vec3 randomizeRay(vec3 ray, float off, inout Random rngState) { +#if !defined(SOFT_SHADOW) + return ray; +#else // https://www.shadertoy.com/view/3sfBWs const vec2 blueNoiseInDisk[64] = vec2[64]( vec2(0.478712,0.875764), @@ -133,7 +141,7 @@ vec3 randomizeRay(vec3 ray, float angle, inout Random rngState) { // get a blue noise sample position vec2 samplePos = blueNoiseInDisk[wangHash(rngState.state)%blueNoiseInDisk.length()]; - samplePos *= tan(angle); + samplePos *= off; //tan(angle); vec3 r = normalize(vec3(0,0,1) + vec3(samplePos,0)); @@ -141,16 +149,12 @@ vec3 randomizeRay(vec3 ray, float angle, inout Random rngState) { vec3 vv = normalize( cross( uu, ray ) ); return vec3( r.x*uu + r.y*vv + r.z*ray ); +#endif } 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); + vec3 shRay = randomizeRay(rayDirection, tan(0.5*M_PI/180.0), rngState); + float lamb = max(dot(norm, rayDirection), 0); if(!shadowed || lamb==0) return lamb; @@ -159,6 +163,65 @@ float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection, bool shado return (lamb * shadow); } +float samplePointLight(vec3 norm, vec3 rayOrigin, vec3 lightPos, float range, bool shadowed, float softAngle, inout Random rngState) { + const vec3 ldir = lightPos - rayOrigin; + const float dirLength = length(ldir); + const vec3 rayDirection = ldir/dirLength; + const vec3 shRay = randomizeRay(rayDirection, 0.0375, rngState); + + const float intensity = lightIntensity(norm, dirLength, -shRay, range); + if(!shadowed || intensity<=0) + return 0; + + float rayDistance = dirLength - range*0.0375; //NOTE: padding of ~3%, in case if light inside wall + if(rayDistance<=0) + return intensity; + + float shadow = rayQueryProceedShadow(rayOrigin, shRay, rayDistance, rngState); + return (intensity * shadow); + } + +vec3 sampleLocalLight(vec3 norm, vec3 rayOrigin, inout Random rngState) { + float pdf = 1.0; + float key = randf(rngState); + uint node = 0 | BVH_BoxNode; + + while(true) { + const uint type = bvhGetNodeType(node); + if(type==BVH_LightNode) { + // light + break; + } + if(type!=BVH_BoxNode) { + // something else + return vec3(0); + } + + const PTreeNode n = bvhData.node[node & 0x0FFFFFFF]; + const float wLeft = bvhLightsNodeWeight(rayOrigin, norm, n.centerL, n.weightL); + const float wRight = bvhLightsNodeWeight(rayOrigin, norm, n.centerR, n.weightR); + const float pLeft = wLeft /(wLeft + wRight); + const float pRight = wRight/(wLeft + wRight); + if(key < pLeft) { + pdf *= pLeft; + node = n.ptrL; + key = key/pLeft; + } else { + pdf *= pRight; + node = n.ptrR; + key = (key-pLeft)/pRight; + } + } + + // light + PTreeNode n = bvhData.node[node & 0x0FFFFFFF]; + vec3 color = n.centerR; + float intensity = samplePointLight(norm, rayOrigin, n.centerL, n.weightR, true, 0, rngState); + if(pdf < 0.001) + return vec3(0); // numerically unstable + fireflys + return (intensity * color) / pdf; + } + vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection) { const int numBounces = 5; @@ -232,6 +295,9 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection) { vec3 direct = vec3(0); direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir, true, 0.54*M_PI/180.0, rngState) * scene.sunColor; //direct += sampleDirectLight(hit.norm, rayOrigin, normalize(vec3(-1,1,0)), false, 0.56*M_PI/180.0, rngState) * (vec3(0.3, 0.26, 1)*GMoonIntensity); +#if defined(LOCAL_LIGHTS) + direct += sampleLocalLight(hit.norm, rayOrigin, rngState) * (max(1.0, scene.exposure) / scene.exposure); +#endif //if(bounce==0) color += thruput*direct*Fd_Lambert; diff --git a/shader/lighting/pt/pathtrace_common.glsl b/shader/lighting/pt/pathtrace_common.glsl index e338578eb..c7e501beb 100644 --- a/shader/lighting/pt/pathtrace_common.glsl +++ b/shader/lighting/pt/pathtrace_common.glsl @@ -33,14 +33,14 @@ void rayQueryProceedAlphaTest(in rayQueryEXT rayQuery, inout Random rng) { } } -float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, inout Random rngState) { +float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, float tMax, 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, TMax); + rayOrigin, tMin, rayDirection, tMax); rayQueryProceedAlphaTest(rayQuery); // rayQueryProceedAlphaTest(rayQuery, rngState); if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) @@ -48,6 +48,10 @@ float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, inout return 0; } +float rayQueryProceedShadow(const vec3 rayOrigin, const vec3 rayDirection, inout Random rngState) { + return rayQueryProceedShadow(rayOrigin, rayDirection, TMax, 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; diff --git a/shader/lighting/surfels/surf_apply.comp b/shader/lighting/surfels/surf_apply.comp index 6d0f6851a..bf2e84c76 100644 --- a/shader/lighting/surfels/surf_apply.comp +++ b/shader/lighting/surfels/surf_apply.comp @@ -153,7 +153,7 @@ void main() { return; } - clr.rgb *= scene.exposure; + //clr.rgb *= scene.exposure; if(pass==0) { // first pass diff --git a/shader/lighting/surfels/surf_common.glsl b/shader/lighting/surfels/surf_common.glsl index 86ae10e9e..f995147cf 100644 --- a/shader/lighting/surfels/surf_common.glsl +++ b/shader/lighting/surfels/surf_common.glsl @@ -5,10 +5,11 @@ #include "scene.glsl" const float SKY_DEPTH = 0.999995; -const int MinCoverage = 8; // in pixels +const int MinCoverage = 4; // in pixels const int DefaultCoverage = 96; // in pixels const int LargeTile = 128; // in pixels const uint MaxInTile = 1024; // ~32px (~6x6) per surfel +const ivec2 GBufTile = ivec2(8); const float rEffScale = 0.5; @@ -59,6 +60,13 @@ bool isSurfelVisible(const Surfel s, ivec2 bboxMin, ivec2 bboxMax) { return true; } +bool planetOcclusion(float viewPos, vec3 sunDir) { + const float y = RPlanet + max(viewPos*0.1, 0); + if(rayIntersect(vec3(0,y,0), sunDir, RPlanet)>=0) + return true; + return false; + } + 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; diff --git a/shader/lighting/surfels/surf_dbg.frag b/shader/lighting/surfels/surf_dbg.frag index 5022b2dd2..98ddc90da 100644 --- a/shader/lighting/surfels/surf_dbg.frag +++ b/shader/lighting/surfels/surf_dbg.frag @@ -43,8 +43,10 @@ void main(void) { if(length(delta) > radius) ;//discard; - //vec3 clr = surfDebugColor(p, instanceIndex) * (1.0-qDist); - vec3 clr = 4.0 * p.irradiance * (1.0-qDist) * scene.exposure; + //vec3 clr = 4.0 * p.irradiance * (1.0-qDist) * scene.exposure; + float k = p.radiusMean/p.radius; + + vec3 clr = mix(vec3(1,0,0), vec3(0,1,0), 2*k) * (1.0-qDist); if(instanceIndex >= header.count-header.added) clr = vec3(0,1,0); if(!isSurfelAlive(p)) diff --git a/shader/lighting/surfels/surf_lighting.comp b/shader/lighting/surfels/surf_lighting.comp index 5fd974f6c..93583be01 100644 --- a/shader/lighting/surfels/surf_lighting.comp +++ b/shader/lighting/surfels/surf_lighting.comp @@ -7,13 +7,14 @@ #include "lighting/rt/rt_common.glsl" #include "lighting/pt/pathtrace_common.glsl" #include "lighting/surfels/surf_common.glsl" +#include "lighting/lightstree/lights_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; +layout(local_size_x = GBufTile.x, local_size_y = GBufTile.y) in; const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; @@ -28,7 +29,11 @@ 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; +layout(binding = 13, std430) readonly buffer BVH { + BVHNode node[]; + } bvhData; struct GBuf { vec3 diff; @@ -38,6 +43,7 @@ struct GBuf { }; shared vec4 reduction[NumThreads]; +shared vec3 reductionSq[NumThreads]; float shadowResolve(in vec4 sh, float z) { z = clamp(z,0,0.99); @@ -75,6 +81,9 @@ float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { if(lamb==0) return lamb; + if(planetOcclusion(rayOrigin.y, rayDirection)) + return 0; + vec4 shCoord = scene.viewShadow[1]*vec4(rayOrigin,1); if(abs(shCoord.x)>=shCoord.w && abs(shCoord.y)>=shCoord.w) { #if 1 @@ -89,6 +98,55 @@ float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection) { return (lamb * shadow); } +vec3 traverseLightTree(const vec3 wpos, const vec3 norm) { + uint stack[32]; + uint ptr = 0; + + vec3 ret = vec3(0); + uint node = 0 | BVH_BoxNode; + while(node!=0) { + const uint type = bvhGetNodeType(node); + const BVHNode n = bvhData.node[node & 0x0FFFFFFF]; + + if(ptr==stack.length()) { + // stack overflow + return vec3(1,0,0); + } + + if(type==BVH_LightNode) { + // light + const vec3 src = n.lmin; + const vec3 distance = wpos - src; + const float tMax = length(distance); + const vec3 ldir = distance/tMax; + const float intensity = lightIntensity(norm, tMax, ldir, uintBitsToFloat(n.ptrL)); + ret += intensity * n.lmax.rgb; + } + else { + const bool left = bvhIntersectBox(wpos, n.lmin, n.lmax); + const bool right = bvhIntersectBox(wpos, n.rmin, n.rmax); + if(left && right) { + node = n.ptrL; + stack[ptr++] = n.ptrR; + continue; + } + else if(left) { + node = n.ptrL; + continue; + } + else if(right) { + node = n.ptrR; + continue; + } + } + if(ptr == 0) + break; + node = stack[--ptr]; + } + + return ret; + } + vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, GBuf hit) { vec3 thruput = vec3(1.0); vec3 color = vec3(0); @@ -101,7 +159,7 @@ vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, GBuf hit) { 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 thruput*color; + return thruput*color;//*scene.exposure; } thruput *= min(textureAlbedo(hit.diff.rgb), 0.95); @@ -109,16 +167,14 @@ vec3 evalLight(vec3 rayOrigin, vec3 rayDirection, GBuf hit) { vec3 direct = vec3(0); direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir) * scene.sunColor; - color += thruput*direct*Fd_Lambert; + direct += traverseLightTree(rayOrigin, hit.norm) * (max(1.0, scene.exposure) / scene.exposure); + color += thruput * direct * Fd_Lambert; return color; } GBuf loadGBuf(uint ptr) { - uint x = ((ptr >> 0) & 0xFFF); - uint y = ((ptr >> 12) & 0xFFF); - - ivec2 at = ivec2(x,y)*8 + ivec2(gl_LocalInvocationID.xy); + ivec2 at = unpackAtlassPos(ptr)*GBufTile + ivec2(gl_LocalInvocationID.xy); vec4 d = texelFetch(gbufDiff, at, 0); vec4 n = texelFetch(gbufNorm, at, 0); @@ -136,12 +192,39 @@ void reduceIrr() { for(uint stride = NumThreads/2; stride > 0; stride >>= 1) { if(laneID < stride) { - reduction[laneID] += reduction[laneID + stride]; + reduction [laneID] += reduction [laneID + stride]; + reductionSq[laneID] += reductionSq[laneID + stride]; } barrier(); } } +float estimateSurfelScale(vec3 mean, vec3 meanSq) { +#if 0 + const float k = 1.0; + + vec3 variance = max(meanSq - mean * mean, 0.0); + float mu = grayscale(mean); + float sigma = sqrt(grayscale(variance)); + + float cv = sigma / max(mu, 1e-3); + // float radiusScale = 1.0 / (1.0 + k * cv); + float radiusScale = exp(-k * cv); + + return radiusScale; +#else + const float k = 0.2; + // return k*grayscale(meanSq - mean*mean); + // return 1.0 - k * max(0, grayscale(meanSq - mean*mean)); + // return 1.0 - k*max(0, grayscale(meanSq - mean*mean))/max(grayscale(meanSq), 0.0001); + vec3 mean2 = max(mean*mean, 0.0001); + meanSq = max(meanSq, 0.0001); + + float cv = max(grayscale(meanSq/mean2) - 1.0, 0.0); + return 4.0 - k*cv*cv; +#endif + } + void main() { const float eps = 0.0001; const uint numPaths = NumThreads; @@ -153,18 +236,19 @@ void main() { return; } - const mat3 tbn = toTangent(decodeNormal(p.norm)); - - Random rngState = Random(pcgHash(gl_LocalInvocationIndex)); + // Random rngState = srand(uvec3(p.pos)); - const uint i = gl_LocalInvocationIndex; - const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); + const mat3 tbn = toTangent(decodeNormal(p.norm)); + const uint i = gl_LocalInvocationIndex; + const float f0 = 0; //randf(rngState)*2.0*M_PI; + const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,f0); const GBuf hit = loadGBuf(p.payload); - const vec3 pt = evalLight(p.pos, ray, hit); + const vec3 pt = evalLight(p.pos, ray, hit) * scene.exposure; float hitInv = 1.0/clamp(hit.rayT*0.4, 10.0, p.radius); - reduction[i] = vec4(pt.rgb, hitInv); + reduction [i] = vec4(pt.rgb, hitInv); + reductionSq[i] = pt.rgb * pt.rgb; barrier(); reduceIrr(); @@ -175,9 +259,16 @@ void main() { const float rMax = min(pixelToWorld(DefaultCoverage, pos4.z/pos4.w), 65000); const float hitMean = float(numPaths)/max(reduction[0].w, eps); //harmonic mean + + const vec3 irradiance = (reduction[0].rgb /float(numPaths)); + const vec3 irradianceSq = (reductionSq[0].rgb/float(numPaths)); + const float scale = estimateSurfelScale(irradiance, irradianceSq); + surfels[surfelId].radius = rMax; - surfels[surfelId].radiusMean = min(max(hitMean, rMin), rMax * rEffScale); + //surfels[surfelId].radiusMean = min(max(hitMean*scale, rMin), rMax * rEffScale); + surfels[surfelId].radiusMean = min(max(rMax*scale, rMin), rMax * rEffScale); - surfels[surfelId].irradiance = (reduction[0].rgb/float(numPaths)); + surfels[surfelId].irradiance = irradiance; + // surfels[surfelId].irradiance = vec3(scale * scene.exposure); } } \ No newline at end of file diff --git a/shader/lighting/surfels/surf_pathtrace.comp b/shader/lighting/surfels/surf_pathtrace.comp index a0db0e041..6dba72847 100644 --- a/shader/lighting/surfels/surf_pathtrace.comp +++ b/shader/lighting/surfels/surf_pathtrace.comp @@ -4,12 +4,12 @@ #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/lightstree/lights_common.glsl" #include "lighting/tonemapping.glsl" #include "scene.glsl" @@ -31,6 +31,10 @@ layout(binding = 0, std140) uniform UboScene { layout(binding = 1, std430) buffer SB0 { SurfHeader header; Surfel surfels[]; }; layout(binding = 2) uniform sampler2D skyLUT; +layout(binding = 13, std430) readonly buffer BVH { + BVHNode node[]; + } bvhData; + mat3 toTangent(vec3 norm) { mat3 tangent; // Compute a tangent frame and rotate the half vector to world space @@ -41,6 +45,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, bool shadowed, float softAngle, inout Random rngState) { #if defined(SOFT_SHADOW) vec3 shRay = randomizeRay(rayDirection, 0.5*M_PI/180.0, rngState); @@ -57,6 +65,55 @@ float sampleDirectLight(vec3 norm, vec3 rayOrigin, vec3 rayDirection, bool shado return (lamb * shadow); } +vec3 traverseLightTree(const vec3 wpos, const vec3 norm) { + uint stack[32]; + uint ptr = 0; + + vec3 ret = vec3(0); + uint node = 0 | BVH_BoxNode; + while(node!=0) { + const uint type = bvhGetNodeType(node); + const BVHNode n = bvhData.node[node & 0x0FFFFFFF]; + + if(ptr==stack.length()) { + // stack overflow + return vec3(1,0,0); + } + + if(type==BVH_LightNode) { + // light + const vec3 src = n.lmin; + const vec3 distance = wpos - src; + const float tMax = length(distance); + const vec3 ldir = distance/tMax; + const float intensity = lightIntensity(norm, tMax, ldir, uintBitsToFloat(n.ptrL)); + ret += intensity * n.lmax.rgb; + } + else { + const bool left = bvhIntersectBox(wpos, n.lmin, n.lmax); + const bool right = bvhIntersectBox(wpos, n.rmin, n.rmax); + if(left && right) { + node = n.ptrL; + stack[ptr++] = n.ptrR; + continue; + } + else if(left) { + node = n.ptrL; + continue; + } + else if(right) { + node = n.ptrR; + continue; + } + } + if(ptr == 0) + break; + node = stack[--ptr]; + } + + return ret; + } + vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { vec3 thruput = vec3(1); vec3 color = vec3(0); @@ -98,25 +155,6 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { return vec4(color, depth); #endif - /* - if(underWater) { - thruput *= waterTransmittance(hit.rayT); - } - - 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); @@ -128,7 +166,8 @@ vec4 pathtrace(vec3 rayOrigin, vec3 rayDirection, inout Random rngState) { // 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; + direct += sampleDirectLight(hit.norm, rayOrigin, scene.sunDir, true, 0.54*M_PI/180.0, rngState); + direct += traverseLightTree(rayOrigin, hit.norm) * (max(1.0, scene.exposure) / scene.exposure); color += thruput*direct*Fd_Lambert; } @@ -142,12 +181,7 @@ void reduceIrr() { 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 + reduction[laneID] += reduction[laneID + stride]; } barrier(); } @@ -170,23 +204,21 @@ void main() { // const vec3 ray = randCosWeightedHemisphereDirection(decodeNormal(p.norm), rngState); const vec4 pt = pathtrace(p.pos, ray, rngState); -#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, p.radius)); -#endif + float hitInv = 1.0/clamp(pt.a*0.4, 10.0, p.radius); + reduction[i] = vec4(pt.rgb*scene.exposure, hitInv); barrier(); reduceIrr(); if(gl_LocalInvocationIndex==0) { -#if defined(USE_MIN_HIT_DIST) - const float hitMin = max(4, reduction[0].a); - surfels[surfelId].radiusMean = hitMin; -#else - const float hitMean = float(numPaths)/max(reduction[0].a, eps); //harmonic mean - surfels[surfelId].radiusMean = hitMean; -#endif + 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 * rEffScale); + 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 47d49383a..a04d2a77b 100644 --- a/shader/lighting/surfels/surf_raycast.comp +++ b/shader/lighting/surfels/surf_raycast.comp @@ -12,10 +12,10 @@ #include "common.glsl" #include "random.glsl" -layout(local_size_x = 8, local_size_y = 8) in; +layout(local_size_x = GBufTile.x, local_size_y = GBufTile.y) in; const uint NumThreads = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z; -const float NormalBias = 0.0015; +// const float NormalBias = 0.0015; layout(std140, push_constant) uniform Push { uint pass; @@ -65,7 +65,7 @@ void storeGBuf(uint ptr, const HitResolve hit, const float wHit) { float wHitUNorm = (hit.rayT>0) ? (wHit / hit.rayT) : 0; - ivec2 at = unpackAtlassPos(ptr)*8 + ivec2(gl_LocalInvocationID.xy); + ivec2 at = unpackAtlassPos(ptr)*GBufTile + ivec2(gl_LocalInvocationID.xy); imageStore(gbufDiff, at, vec4(hit.diff.rgb, 1)); imageStore(gbufNorm, at, vec4(hit.norm.xyz*0.5+0.5, wHitUNorm)); imageStore(gbufHit, at, vec4(hit.rayT/TMax)); @@ -82,12 +82,15 @@ void main() { if(!isSurfelAlive(p)) return; + //Random rngState = srand(uvec3(p.pos)); + const vec3 norm = decodeNormal(p.norm); - const mat3 tbn = toTangent(norm); const vec3 pos = p.pos; + const mat3 tbn = toTangent(norm); const uint i = gl_LocalInvocationIndex; - const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,0); + const float f0 = 0; //randf(rngState)*2.0*M_PI; + const vec3 ray = tbn * sampleHemisphereCos(i,numPaths,f0); float wHit = 0; const HitResolve hit = raycast(pos, ray, wHit); diff --git a/shader/random.glsl b/shader/random.glsl index 3597a8bf5..52a2c1d2b 100644 --- a/shader/random.glsl +++ b/shader/random.glsl @@ -7,6 +7,18 @@ struct Random { uint state; }; +Random srand(uint seed) { + Random r; + r.state = uint(uint(seed) * uint(26699)) | uint(1); + return r; + } + +Random srand(uvec3 v3) { + Random r; + r.state = uint(v3.x * uint(1973) + v3.y * uint(9277) + v3.z * uint(26699)) | uint(1); + return r; + } + 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); @@ -14,7 +26,10 @@ Random srand(uvec2 fragCoord, uint seed) { } float randf(inout Random r) { - return float(wangHash(r.state)) / 4294967296.0; + // return float(wangHash(r.state)) / 4294967296.0; + // return float(wangHash(r.state) >> 8) * (1.0 / 16777216.0); // 2^-24 + uint x = wangHash(r.state); + return uintBitsToFloat(0x3F800000u | (x >> 9)) - 1.0; } vec3 randVec3(inout Random rng) { diff --git a/shader/rtsm/rtsm_common.glsl b/shader/rtsm/rtsm_common.glsl index e5c1b27c6..c66cc2c87 100644 --- a/shader/rtsm/rtsm_common.glsl +++ b/shader/rtsm/rtsm_common.glsl @@ -39,42 +39,6 @@ struct LightId { }; // utility -uint floatToOrderedUint(float value) { - uint uvalue = floatBitsToUint(value); - uint mask = -int(uvalue >> 31) | 0x80000000; - return uvalue ^ mask; - } - -uvec4 floatToOrderedUint(vec4 value) { - uvec4 r; - r.x = floatToOrderedUint(value.x); - r.y = floatToOrderedUint(value.y); - r.z = floatToOrderedUint(value.z); - r.w = floatToOrderedUint(value.w); - return r; - } - -float orderedUintToFloat(uint value) { - uint mask = ((value >> 31) - 1) | 0x80000000; - return uintBitsToFloat(value ^ mask); - } - -vec3 orderedUintToFloat(uvec3 value) { - vec3 r; - r.x = orderedUintToFloat(value.x); - r.y = orderedUintToFloat(value.y); - r.z = orderedUintToFloat(value.z); - return r; - } - -vec4 orderedUintToFloat(uvec4 value) { - vec4 r; - r.x = orderedUintToFloat(value.x); - r.y = orderedUintToFloat(value.y); - r.z = orderedUintToFloat(value.z); - r.w = orderedUintToFloat(value.w); - return r; - } float edgeFunction(const vec2 a, const vec2 b, const vec2 c) { return (c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x); diff --git a/shader/rtsm/rtsm_omni_raster.comp b/shader/rtsm/rtsm_omni_raster.comp index d09ba52df..4a5c8bd5e 100644 --- a/shader/rtsm/rtsm_omni_raster.comp +++ b/shader/rtsm/rtsm_omni_raster.comp @@ -173,7 +173,7 @@ float lightIntensity(const vec3 normal, const float distance, const vec3 ldir, c float falloff = squareFalloffAttenuation(distance, 1.0/lightRange); float lambert = max(0.0,-dot(ldir,normal)); - return lambert * falloff * Fd_Lambert * 0.25; + return lambert * falloff * 0.25; } const uint MaxChunk = NumThreads/2; @@ -270,7 +270,7 @@ void setupLight(uint ptr, const vec3 wpos, const vec3 normal, out float lightPow const vec3 distance = wpos - src.xyz; const float tMax = length(distance); const vec3 ldir = distance/tMax; - lightPower = lightIntensity(normal, tMax, ldir, src.w); + lightPower = lightIntensity(normal, tMax, ldir, src.w) * Fd_Lambert; lightOrigin = vec4(src.xyz, src.w*0.0375); // position and tMin inactiveRays = 0; diff --git a/shader/rtsm/rtsm_omni_rendering.comp b/shader/rtsm/rtsm_omni_rendering.comp index 055090fa8..79458452a 100644 --- a/shader/rtsm/rtsm_omni_rendering.comp +++ b/shader/rtsm/rtsm_omni_rendering.comp @@ -237,7 +237,7 @@ float lightIntensity(const vec3 normal, const float distance, const vec3 ldir, c float lambert = max(0.0,-dot(ldir,normal)); float lx = (lambert/max(factor, 0.05)) * (smoothFactor*smoothFactor); - return lx * Fd_Lambert * 0.1; + return lx * 0.1; } shared uint meshletsIds[NumThreads]; @@ -298,7 +298,7 @@ void processLight(const vec3 wpos, const vec3 normal, const LightId lId) { const float tMax = length(distance); const vec3 ldir = distance/tMax; - const float lpow = lightIntensity(normal, tMax, ldir, src.range); + const float lpow = lightIntensity(normal, tMax, ldir, src.range) * Fd_Lambert; rayBboxses(ldir, tMax, lpow>0); barrier(); diff --git a/shader/swrt/sw_raytracing64.comp b/shader/swrt/sw_raytracing64.comp index a9656e459..ce2d01220 100644 --- a/shader/swrt/sw_raytracing64.comp +++ b/shader/swrt/sw_raytracing64.comp @@ -41,35 +41,6 @@ layout(binding = 7, std430) readonly buffer VBO { float vbo[]; }; uint laneID = gl_LocalInvocationIndex; ivec2 fragCoord = ivec2(gl_GlobalInvocationID.xy); -// utility -uint floatToOrderedUint(float value) { - uint uvalue = floatBitsToUint(value); - uint mask = -int(uvalue >> 31) | 0x80000000; - return uvalue ^ mask; - } - -float orderedUintToFloat(uint value) { - uint mask = ((value >> 31) - 1) | 0x80000000; - return uintBitsToFloat(value ^ mask); - } - -vec3 orderedUintToFloat(uvec3 value) { - vec3 r; - r.x = orderedUintToFloat(value.x); - r.y = orderedUintToFloat(value.y); - r.z = orderedUintToFloat(value.z); - return r; - } - -vec4 orderedUintToFloat(uvec4 value) { - vec4 r; - r.x = orderedUintToFloat(value.x); - r.y = orderedUintToFloat(value.y); - r.z = orderedUintToFloat(value.z); - r.w = orderedUintToFloat(value.w); - return r; - } - bool bboxIntersect(vec4 a, vec4 b) { if(b.z < a.x || b.x > a.z) return false;