Skip to content

Commit

Permalink
Fixed a bunch of VSM bugs (now multiple clipmaps works), "fixed" (not…
Browse files Browse the repository at this point in the history
… 100% sure) shadow bias calculation, added VSM LoD bias slider.
  • Loading branch information
JuanDiegoMontoya committed Sep 18, 2023
1 parent c5d41f3 commit 1cb8f2d
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 67 deletions.
8 changes: 7 additions & 1 deletion data/config/defaultLayout.ini
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ Size=1233,1056
Collapsed=0
DockId=0x00000001,0

[Window][ Shadow]
Pos=1572,580
Size=348,185
Collapsed=0
DockId=0x0000000A,1

[Docking][Data]
DockSpace ID=0x4BBE4C7A Window=0x4647B76E Pos=0,24 Size=1920,1056 Split=X Selected=0xE87781F4
DockNode ID=0x00000005 Parent=0x4BBE4C7A SizeRef=335,1056 Split=Y Selected=0x6A2E32C2
Expand All @@ -86,6 +92,6 @@ DockSpace ID=0x4BBE4C7A Window=0x4647B76E Pos=0,24 Size=1920,1056 Spli
DockNode ID=0x00000007 Parent=0x00000003 SizeRef=348,369 Selected=0xBE92E3B2
DockNode ID=0x00000008 Parent=0x00000003 SizeRef=348,370 Split=Y Selected=0xFCCC74B6
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=348,183 Selected=0x4F8C3FAB
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=348,185 Selected=0xC6672BD8
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=348,185 Selected=0x21E9B10B
DockNode ID=0x00000004 Parent=0x00000002 SizeRef=348,313 Selected=0xE83C69A2

55 changes: 36 additions & 19 deletions data/shaders/ShadeDeferredPbr.frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,27 @@ layout(binding = 6, std430) readonly buffer LightBuffer
GpuLight lights[];
}lightBuffer;

// Returns a shadow bias in the space of whatever texelWidth is in.
// For example, if texelWidth is 0.125 units in world space, the bias will be in world space too.
float GetShadowBias(vec3 N, vec3 L, float texelWidth)
{
const float quantize = 2.0 / (1 << 23);
const float b = texelWidth / 2.0;
const float NoL = clamp(dot(N, L), 0.0, 1.0);
return quantize + b * length(cross(L, N)) / NoL;
}

struct ShadowVsmOut
{
float shadow;
uint pageData;
vec2 vsmUv;
float shadowDepth;
float projectedDepth;
uint clipmapLevel;
};

ShadowVsmOut ShadowVsm(vec3 fragWorldPos)
ShadowVsmOut ShadowVsm(vec3 fragWorldPos, vec3 normal)
{
ShadowVsmOut ret;
ret.pageData = 0;
Expand All @@ -82,6 +93,7 @@ ShadowVsmOut ShadowVsm(vec3 fragWorldPos)

ret.vsmUv = addr.pageUv;
ret.projectedDepth = addr.projectedDepth;
ret.clipmapLevel = addr.clipmapLevel;

ret.pageData = imageLoad(i_pageTables, addr.pageAddress).x;
if (!GetIsPageBacked(ret.pageData))
Expand All @@ -94,7 +106,12 @@ ShadowVsmOut ShadowVsm(vec3 fragWorldPos)
const uint physicalAddress = GetPagePhysicalAddress(ret.pageData);
ret.shadowDepth = uintBitsToFloat(LoadPageTexel(pageTexel, physicalAddress));

if (ret.shadowDepth + 1e-2 < ret.projectedDepth)
const float magicForFarClipmaps = 0.2;
const float magicConstantBias = 2.0 / (1 << 23);
const float halfOrthoFrustumLength = 200.0 / 2;
const float shadowTexelSize = exp2(addr.clipmapLevel + magicForFarClipmaps) * clipmapUniforms.firstClipmapTexelLength;
const float bias = magicConstantBias + GetShadowBias(normal, -shadingUniforms.sunDir.xyz, shadowTexelSize) / halfOrthoFrustumLength;
if (ret.shadowDepth + bias < ret.projectedDepth)
{
ret.shadow = 0.0;
return ret;
Expand Down Expand Up @@ -191,12 +208,9 @@ float Shadow(vec3 fragWorldPos, vec3 normal, vec3 lightDir)

// Analytically compute slope-scaled bias
const float maxBias = 0.0008;
const float quantize = 2.0 / (1 << 23);
ivec2 res = textureSize(s_rsmDepth, 0);
float b = 1.0 / max(res.x, res.y) / 2.0;
float NoD = clamp(-dot(shadingUniforms.sunDir.xyz, normal), 0.0, 1.0);
float bias = quantize + b * length(cross(-shadingUniforms.sunDir.xyz, normal)) / NoD;
bias = min(bias, maxBias);
float bias = maxBias;
//float bias = GetShadowBias(normal, -shadingUniforms.sunDir.xyz, textureSize(s_rsmDepthShadow, 0));
//bias = min(bias, maxBias);

switch (shadowUniforms.shadowMode)
{
Expand Down Expand Up @@ -240,7 +254,7 @@ void main()
vec3 diffuse = albedo * cosTheta * shadingUniforms.sunStrength.rgb;

//float shadow = Shadow(fragWorldPos, normal, -shadingUniforms.sunDir.xyz);
ShadowVsmOut shadowVsm = ShadowVsm(fragWorldPos);
ShadowVsmOut shadowVsm = ShadowVsm(fragWorldPos, normal);
float shadowSun = shadowVsm.shadow;
//shadowSun = 0;

Expand All @@ -264,18 +278,21 @@ void main()

o_color = finalColor;

// Disco view (hashed physical address + outline)
// Disco view (hashed physical address or clipmap level)
// if (GetIsPageVisible(shadowVsm.pageData))
// {
// const float GOLDEN_CONJ = 0.6180339887498948482045868343656;
// vec4 color = vec4(2.0 * hsv_to_rgb(vec3(float(GetPagePhysicalAddress(shadowVsm.pageData)) * GOLDEN_CONJ, 0.875, 0.85)), 1.0);
// o_color.rgb += color.rgb;

// const vec2 pageUv = fract(shadowVsm.vsmUv * 128);
// if (pageUv.x < .05 || pageUv.y < .05 || pageUv.x > .95 || pageUv.y > .95)
// {
// o_color.rgb = vec3(1, 0, 0);
// }
// //vec3 color = 2.0 * hsv_to_rgb(vec3(float(GetPagePhysicalAddress(shadowVsm.pageData)) * GOLDEN_CONJ, 0.875, 0.85));
// vec3 color = 2.0 * hsv_to_rgb(vec3(shadowVsm.clipmapLevel*2 * GOLDEN_CONJ, 0.875, 0.85));
// //vec4 color =
// o_color.rgb += color;
// }

// Page outlines
// const vec2 pageUv = fract(shadowVsm.vsmUv);
// if (pageUv.x < .02 || pageUv.y < .02 || pageUv.x > .98 || pageUv.y > .98)
// {
// o_color.rgb = vec3(1, 0, 0);
// }

// UV + shadow map depth view
Expand All @@ -293,7 +310,7 @@ void main()
// if (GetIsPageBacked(shadowVsm.pageData))
// o_color += vec3(0, .1, 0);
// if (GetIsPageDirty(shadowVsm.pageData))
// o_color += vec3(0, 0, 1);
// o_color += vec3(0, 0, .1);
// if (shadowSun == 0.0)
// o_color = vec3(1, 1, 0);
}
17 changes: 10 additions & 7 deletions data/shaders/shadows/vsm/VsmCommon.h.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ layout(binding = 5, std430) restrict readonly buffer VsmMarkPagesDirectionalUnif
uint clipmapTableIndices[MAX_CLIPMAPS];
uint numClipmaps;

// The area, in world space, of a single texel in the first clipmap
float firstClipmapTexelArea;
// The length, in world space, of a side of single (square) texel in the first clipmap
float firstClipmapTexelLength;
}clipmapUniforms;

layout(binding = 6, std140) uniform VsmGlobalUniforms
{
float lodBias;
}vsmUniforms;

struct VsmPageAllocRequest
{
// Address of the requester
Expand Down Expand Up @@ -167,19 +172,17 @@ bool GetClipmapPageFromDepth(sampler2D depthBuffer, ivec2 gid, out PageAddress a
const vec2 uvCenter = (vec2(gid) + 0.5) * texel;
const vec2 uvTopLeft = uvCenter + vec2(-texel.x, texel.y) * 0.5;
const vec2 uvTopRight = uvCenter + vec2(texel.x, texel.y) * 0.5;
const vec2 uvBottomLeft = uvCenter + vec2(texel.x, -texel.y) * 0.5;

const float depth = texelFetch(depthBuffer, gid, 0).x;

const mat4 invProj = inverse(perFrameUniforms.proj);
const vec3 topLeftV = UnprojectUV_ZO(depth, uvTopLeft, invProj);
const vec3 topRightV = UnprojectUV_ZO(depth, uvTopRight, invProj);
const vec3 bottomLeftV = UnprojectUV_ZO(depth, uvBottomLeft, invProj);

const float projArea = distance(topLeftV, topRightV) * distance(topLeftV, bottomLeftV);
const float projLength = distance(topLeftV, topRightV);

// Assume each clipmap is 2x the size of the previous
const uint clipmapLevel = clamp(uint(ceil(log2(projArea / clipmapUniforms.firstClipmapTexelArea))), 0, clipmapUniforms.numClipmaps - 1);
// Assume each clipmap is 2x the side length of the previous
const uint clipmapLevel = clamp(uint(ceil(vsmUniforms.lodBias + log2(projLength / clipmapUniforms.firstClipmapTexelLength))), 0, clipmapUniforms.numClipmaps - 1);
const uint clipmapIndex = clipmapUniforms.clipmapTableIndices[clipmapLevel];

const vec3 posW = UnprojectUV_ZO(depth, uvCenter, perFrameUniforms.invViewProj);
Expand Down
4 changes: 1 addition & 3 deletions data/shaders/shadows/vsm/VsmShadow.frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ layout(binding = 0, std140) uniform VsmShadowUniforms
void main()
{
const ivec2 pageAddressXy = ivec2(gl_FragCoord.xy) / PAGE_SIZE;
// TODO: implement for multiple clipmaps
//const uint pageData = imageLoad(i_pageTables, ivec3(pageAddressXy, vsmIndex)).x;
const uint pageData = imageLoad(i_pageTables, ivec3(pageAddressXy, 0)).x;
const uint pageData = imageLoad(i_pageTables, ivec3(pageAddressXy, vsmIndex)).x;
const ivec2 pageTexel = ivec2(gl_FragCoord.xy) % PAGE_SIZE;
if (GetIsPageBacked(pageData) && GetIsPageDirty(pageData))
{
Expand Down
39 changes: 25 additions & 14 deletions src/FrogRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ FrogRenderer::FrogRenderer(const Application::CreateInfo& createInfo, std::optio
vsmSun({
.context = vsmContext,
.virtualExtent = Techniques::VirtualShadowMaps::maxExtent,
.numClipmaps = 1,
.numClipmaps = 10,
}),
vsmShadowPipeline(Pipelines::ShadowVsm())
vsmShadowPipeline(Pipelines::ShadowVsm()),
vsmShadowUniformBuffer(Fwog::BufferStorageFlag::DYNAMIC_STORAGE)
{
ZoneScoped;
int x = 0;
Expand Down Expand Up @@ -459,7 +460,8 @@ void FrogRenderer::OnRender([[maybe_unused]] double dt)
const auto viewProj = projJittered * mainCamera.GetViewMatrix();
const auto viewProjUnjittered = projUnjittered * mainCamera.GetViewMatrix();

static constexpr auto viewCount = 3; // TODO: don't hardcode
const auto viewCount = 2 + vsmSun.NumClipmaps();

std::array<View, gMaxViews> views = {};
views[0] = { // Main View
.proj = projUnjittered,
Expand Down Expand Up @@ -512,17 +514,20 @@ void FrogRenderer::OnRender([[maybe_unused]] double dt)
};
MakeFrustumPlanes(shadingUniforms.sunViewProj, views[1].frustumPlanes);

vsmSun.Update(shadingUniforms.sunView, 10);
vsmSun.UpdateExpensive(shadingUniforms.sunView, 10);

views[2] = {
// VSM clipmap 0
.proj = vsmSun.GetProjections()[0],
.view = shadingUniforms.sunView,
.viewProj = vsmSun.GetProjections()[0] * shadingUniforms.sunView,
.cameraPos = {}, // unused
.viewport = {0.f, 0.f, vsmSun.GetExtent().width, vsmSun.GetExtent().height},
};
MakeFrustumPlanes(shadingUniforms.sunViewProj, views[2].frustumPlanes);
for (uint32_t i = 0; i < vsmSun.NumClipmaps(); i++)
{
views[2 + i] = {
// sun vsm 0
.proj = vsmSun.GetProjections()[i],
.view = shadingUniforms.sunView,
.viewProj = vsmSun.GetProjections()[i] * shadingUniforms.sunView,
.cameraPos = {}, // unused
.viewport = {0.f, 0.f, vsmSun.GetExtent().width, vsmSun.GetExtent().height},
};
MakeFrustumPlanes(views[2 + i].viewProj, views[2 + i].frustumPlanes);
}

viewBuffer->UpdateData(std::span<const View>(views));

Expand Down Expand Up @@ -587,8 +592,14 @@ void FrogRenderer::OnRender([[maybe_unused]] double dt)
Fwog::Cmd::BindUniformBuffer("PerFrameUniformsBuffer", globalUniformsBuffer);
Fwog::Cmd::BindStorageBuffer("ViewBuffer", viewBuffer.value());
vsmSun.BindResourcesForDrawing();
Fwog::Cmd::BindUniformBuffer("VsmShadowUniforms", vsmShadowUniformBuffer);
Fwog::Cmd::BindIndexBuffer(*instancedMeshletBuffer, Fwog::IndexType::UNSIGNED_INT);
Fwog::Cmd::DrawIndexedIndirect(*meshletIndirectCommand, 2 * sizeof(Fwog::DrawIndexedIndirectCommand), 1, 0);

for (uint32_t i = 0; i < vsmSun.NumClipmaps(); i++)
{
vsmShadowUniformBuffer.UpdateData(vsmSun.GetClipmapTableIndices()[i]);
Fwog::Cmd::DrawIndexedIndirect(*meshletIndirectCommand, (2 + i) * sizeof(Fwog::DrawIndexedIndirectCommand), 1, 0);
}
});

auto shadowAttachment = Fwog::RenderDepthStencilAttachment{
Expand Down
3 changes: 3 additions & 0 deletions src/FrogRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class FrogRenderer final : public Application
void GuiDrawBloomWindow();
void GuiDrawAutoExposureWindow();
void GuiDrawCameraWindow();
void GuiDrawShadowWindow();

// constants
static constexpr int gMaxViews = 16;
Expand Down Expand Up @@ -326,4 +327,6 @@ class FrogRenderer final : public Application
Techniques::VirtualShadowMaps::Context vsmContext;
Techniques::VirtualShadowMaps::DirectionalVirtualShadowMap vsmSun;
Fwog::GraphicsPipeline vsmShadowPipeline;
Fwog::TypedBuffer<uint32_t> vsmShadowUniformBuffer;
Techniques::VirtualShadowMaps::Context::VsmGlobalUniforms vsmUniforms{};
};
21 changes: 15 additions & 6 deletions src/Gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,19 @@ void FrogRenderer::GuiDrawCameraWindow()
ImGui::End();
}

void FrogRenderer::GuiDrawShadowWindow()
{
if (ImGui::Begin(" Shadow"))
{
ImGui::TextUnformatted("VSM");
if (ImGui::SliderFloat("LoD Bias", &vsmUniforms.lodBias, -3, 3, "%.2f"))
{
vsmContext.UpdateUniforms(vsmUniforms);
}
}
ImGui::End();
}

void FrogRenderer::OnGui(double dt)
{
GuiDrawDockspace();
Expand Down Expand Up @@ -625,14 +638,10 @@ void FrogRenderer::OnGui(double dt)
ImGui::PopStyleVar();

GuiDrawMagnifier(viewportContentOffset, {viewportContentSize.x, viewportContentSize.y}, viewportIsHovered);

GuiDrawDebugWindow();

GuiDrawLightsArray();

GuiDrawBloomWindow();

GuiDrawAutoExposureWindow();

GuiDrawCameraWindow();
}
GuiDrawShadowWindow();
}
19 changes: 12 additions & 7 deletions src/techniques/VirtualShadowMaps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ namespace Techniques::VirtualShadowMaps
pages_(Fwog::CreateTexture2D({(uint32_t)std::ceil(std::sqrt(createInfo.numPages)) * pageSize, (uint32_t)std::ceil(std::sqrt(createInfo.numPages)) * pageSize}, Fwog::Format::R32_UINT, "VSM Physical Pages")),
visiblePagesBitmask_(sizeof(uint32_t) * createInfo.numPages / 32),
pageVisibleTimeTree_(sizeof(uint32_t) * createInfo.numPages * 2),
uniformBuffer_(Fwog::BufferStorageFlag::DYNAMIC_STORAGE),
uniformBuffer_(VsmGlobalUniforms{}, Fwog::BufferStorageFlag::DYNAMIC_STORAGE),
pageAllocRequests_(sizeof(PageAllocRequest) * (createInfo.numPages + 1)),
pagesToClear_(sizeof(uint32_t) + sizeof(uint32_t) * createInfo.numPages),
pageClearDispatchParams_(Fwog::DispatchIndirectCommand{pageSize / 8, pageSize / 8, 0}),
Expand All @@ -99,6 +99,11 @@ namespace Techniques::VirtualShadowMaps
pageVisibleTimeTree_.FillData();
}

void Context::UpdateUniforms(const VsmGlobalUniforms& uniforms)
{
uniformBuffer_.UpdateData(uniforms);
}

std::optional<uint32_t> Context::AllocateLayer()
{
for (size_t i = 0; i < freeLayersBitmask_.size(); i++)
Expand Down Expand Up @@ -189,6 +194,7 @@ namespace Techniques::VirtualShadowMaps

DirectionalVirtualShadowMap::DirectionalVirtualShadowMap(const CreateInfo& createInfo)
: context_(createInfo.context),
numClipmaps_(createInfo.numClipmaps),
virtualExtent_(createInfo.virtualExtent),
uniformBuffer_(Fwog::BufferStorageFlag::DYNAMIC_STORAGE)
{
Expand Down Expand Up @@ -222,23 +228,21 @@ namespace Techniques::VirtualShadowMaps
Fwog::Cmd::BindStorageBuffer("VsmVisiblePagesBitmask", context_.visiblePagesBitmask_);
Fwog::Cmd::BindStorageBuffer("VsmPageAllocRequests", context_.pageAllocRequests_);
Fwog::Cmd::BindStorageBuffer("VsmMarkPagesDirectionalUniforms", uniformBuffer_);
Fwog::Cmd::BindUniformBuffer("VsmGlobalUniforms", context_.uniformBuffer_);
Fwog::Cmd::BindUniformBuffer(0, globalUniforms);
Fwog::Cmd::DispatchInvocations(gDepth.Extent());
});
}

void DirectionalVirtualShadowMap::Update(const glm::mat4& viewMat, float firstClipmapWidth)
void DirectionalVirtualShadowMap::UpdateExpensive(const glm::mat4& viewMat, float firstClipmapWidth)
{
const auto sideLength = firstClipmapWidth / virtualExtent_;
uniforms_.firstClipmapTexelArea = sideLength * sideLength;

//const auto view = glm::inverse(glm::mat4_cast(glm::angleAxis(0.0f, glm::normalize(-direction))));
//const auto view = glm::lookAt(direction * -5.f, glm::vec3(0), glm::vec3(0, 1, 0));
uniforms_.firstClipmapTexelLength = sideLength;

for (uint32_t i = 0; i < uniforms_.numClipmaps; i++)
{
const auto width = firstClipmapWidth * (1 << i) / 2.0f;
clipmapProjections[i] = glm::orthoZO(-width, width, -width, width, -1.f, 10.f);
clipmapProjections[i] = glm::orthoZO(-width, width, -width, width, -100.f, 100.f);

uniforms_.clipmapViewProjections[i] = clipmapProjections[i] * viewMat;
}
Expand All @@ -257,6 +261,7 @@ namespace Techniques::VirtualShadowMaps
void DirectionalVirtualShadowMap::BindResourcesForDrawing()
{
Fwog::Cmd::BindStorageBuffer(5, uniformBuffer_);
Fwog::Cmd::BindUniformBuffer(6, context_.uniformBuffer_);
Fwog::Cmd::BindImage(0, context_.pageTables_, 0);
Fwog::Cmd::BindImage(1, context_.pages_, 0);
}
Expand Down
Loading

0 comments on commit 1cb8f2d

Please sign in to comment.