Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capsule shadows #1055

Merged
merged 2 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Editor/GraphicsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,53 @@ void GraphicsWindow::Create(EditorComponent* _editor)
});
AddWidget(&lightShaftsStrengthStrengthSlider);

capsuleshadowCheckbox.Create("Capsule Shadows: ");
capsuleshadowCheckbox.SetTooltip("Enable ambient occlusion capsule shadows.");
capsuleshadowCheckbox.SetSize(XMFLOAT2(hei, hei));
capsuleshadowCheckbox.SetPos(XMFLOAT2(x, y += step));
if (editor->main->config.GetSection("graphics").Has("capsule_shadows"))
{
wi::renderer::SetCapsuleShadowEnabled(editor->main->config.GetSection("graphics").GetBool("capsule_shadows"));
}
capsuleshadowCheckbox.OnClick([=](wi::gui::EventArgs args) {
wi::renderer::SetCapsuleShadowEnabled(args.bValue);
editor->main->config.GetSection("graphics").Set("capsule_shadows", args.bValue);
editor->main->config.Commit();
});
AddWidget(&capsuleshadowCheckbox);

capsuleshadowFadeSlider.Create(0, 1, 0.2f, 100, "CapsuleShadow.Fade: ");
capsuleshadowFadeSlider.SetText("Capsule Shadow Fade: ");
capsuleshadowFadeSlider.SetTooltip("Set capsule shadow fading.");
capsuleshadowFadeSlider.SetSize(XMFLOAT2(mod_wid, hei));
capsuleshadowFadeSlider.SetPos(XMFLOAT2(x + 100, y));
if (editor->main->config.GetSection("graphics").Has("capsule_shadow_fade"))
{
wi::renderer::SetCapsuleShadowFade(editor->main->config.GetSection("graphics").GetFloat("capsule_shadow_fade"));
}
capsuleshadowFadeSlider.OnSlide([=](wi::gui::EventArgs args) {
wi::renderer::SetCapsuleShadowFade(args.fValue);
editor->main->config.GetSection("graphics").Set("capsule_shadow_fade", args.fValue);
editor->main->config.Commit();
});
AddWidget(&capsuleshadowFadeSlider);

capsuleshadowAngleSlider.Create(0, 90, 45, 90, "CapsuleShadow.Angle: ");
capsuleshadowAngleSlider.SetText("Angle: ");
capsuleshadowAngleSlider.SetTooltip("Set capsule shadow spread angle.");
capsuleshadowAngleSlider.SetSize(XMFLOAT2(mod_wid, hei));
capsuleshadowAngleSlider.SetPos(XMFLOAT2(x + 100, y));
if (editor->main->config.GetSection("graphics").Has("capsule_shadow_angle"))
{
wi::renderer::SetCapsuleShadowAngle(wi::math::DegreesToRadians(editor->main->config.GetSection("graphics").GetFloat("capsule_shadow_angle")));
}
capsuleshadowAngleSlider.OnSlide([=](wi::gui::EventArgs args) {
wi::renderer::SetCapsuleShadowAngle(wi::math::DegreesToRadians(args.fValue));
editor->main->config.GetSection("graphics").Set("capsule_shadow_angle", args.fValue);
editor->main->config.Commit();
});
AddWidget(&capsuleshadowAngleSlider);

aoComboBox.Create("AO: ");
aoComboBox.SetTooltip("Choose Ambient Occlusion type. RTAO is only available if hardware supports ray tracing");
aoComboBox.SetScriptTip("RenderPath3D::SetAO(int value)");
Expand Down Expand Up @@ -1558,6 +1605,9 @@ void GraphicsWindow::Update()
lensFlareCheckBox.SetCheck(editor->renderPath->getLensFlareEnabled());
lightShaftsCheckBox.SetCheck(editor->renderPath->getLightShaftsEnabled());
lightShaftsStrengthStrengthSlider.SetValue(editor->renderPath->getLightShaftsStrength());
capsuleshadowCheckbox.SetCheck(wi::renderer::IsCapsuleShadowEnabled());
capsuleshadowAngleSlider.SetValue(wi::math::RadiansToDegrees(wi::renderer::GetCapsuleShadowAngle()));
capsuleshadowFadeSlider.SetValue(wi::renderer::GetCapsuleShadowFade());
aoComboBox.SetSelectedWithoutCallback(editor->renderPath->getAO());
aoPowerSlider.SetValue((float)editor->renderPath->getAOPower());

Expand Down Expand Up @@ -1845,6 +1895,9 @@ void GraphicsWindow::ResizeLayout()
add_right(lensFlareCheckBox);
add_right(lightShaftsStrengthStrengthSlider);
lightShaftsCheckBox.SetPos(XMFLOAT2(lightShaftsStrengthStrengthSlider.GetPos().x - lightShaftsCheckBox.GetSize().x - 80, lightShaftsStrengthStrengthSlider.GetPos().y));
add_right(capsuleshadowAngleSlider);
add_right(capsuleshadowFadeSlider);
capsuleshadowCheckbox.SetPos(XMFLOAT2(capsuleshadowAngleSlider.GetPos().x - capsuleshadowCheckbox.GetSize().x - 80, capsuleshadowAngleSlider.GetPos().y));
add(aoComboBox);
add(aoPowerSlider);
add(aoRangeSlider);
Expand Down
3 changes: 3 additions & 0 deletions Editor/GraphicsWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class GraphicsWindow : public wi::gui::Window
wi::gui::CheckBox lensFlareCheckBox;
wi::gui::CheckBox lightShaftsCheckBox;
wi::gui::Slider lightShaftsStrengthStrengthSlider;
wi::gui::CheckBox capsuleshadowCheckbox;
wi::gui::Slider capsuleshadowFadeSlider;
wi::gui::Slider capsuleshadowAngleSlider;
wi::gui::ComboBox aoComboBox;
wi::gui::Slider aoPowerSlider;
wi::gui::Slider aoRangeSlider;
Expand Down
15 changes: 15 additions & 0 deletions Editor/HumanoidWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ void HumanoidWindow::Create(EditorComponent* _editor)
});
AddWidget(&ragdollCheckBox);

capsuleShadowCheckBox.Create("Capsule Shadow Disabled: ");
capsuleShadowCheckBox.SetTooltip("Disable capsule shadow for this specific humanoid.");
capsuleShadowCheckBox.SetSize(XMFLOAT2(hei, hei));
capsuleShadowCheckBox.OnClick([=](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
HumanoidComponent* humanoid = scene.humanoids.GetComponent(entity);
if (humanoid != nullptr)
{
humanoid->SetCapsuleShadowDisabled(args.bValue);
}
});
AddWidget(&capsuleShadowCheckBox);

headRotMaxXSlider.Create(0, 90, 60, 180, "Head horizontal: ");
headRotMaxXSlider.SetTooltip("Limit horizontal head movement (input in degrees)");
headRotMaxXSlider.SetSize(XMFLOAT2(wid, hei));
Expand Down Expand Up @@ -275,6 +288,7 @@ void HumanoidWindow::SetEntity(Entity entity)
{
lookatCheckBox.SetCheck(humanoid->IsLookAtEnabled());
ragdollCheckBox.SetCheck(humanoid->IsRagdollPhysicsEnabled());
capsuleShadowCheckBox.SetCheck(humanoid->IsCapsuleShadowDisabled());
headRotMaxXSlider.SetValue(wi::math::RadiansToDegrees(humanoid->head_rotation_max.x));
headRotMaxYSlider.SetValue(wi::math::RadiansToDegrees(humanoid->head_rotation_max.y));
headRotSpeedSlider.SetValue(humanoid->head_rotation_speed);
Expand Down Expand Up @@ -572,6 +586,7 @@ void HumanoidWindow::ResizeLayout()
lookatMouseCheckBox.SetPos(XMFLOAT2(lookatCheckBox.GetPos().x - 120, lookatCheckBox.GetPos().y));
add(lookatEntityCombo);
add_right(ragdollCheckBox);
add_right(capsuleShadowCheckBox);
add(headRotMaxXSlider);
add(headRotMaxYSlider);
add(headRotSpeedSlider);
Expand Down
1 change: 1 addition & 0 deletions Editor/HumanoidWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class HumanoidWindow : public wi::gui::Window
wi::gui::CheckBox lookatCheckBox;
wi::gui::ComboBox lookatEntityCombo;
wi::gui::CheckBox ragdollCheckBox;
wi::gui::CheckBox capsuleShadowCheckBox;
wi::gui::Slider headRotMaxXSlider;
wi::gui::Slider headRotMaxYSlider;
wi::gui::Slider headRotSpeedSlider;
Expand Down
18 changes: 18 additions & 0 deletions Editor/MaterialWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,22 @@ void MaterialWindow::Create(EditorComponent* _editor)
});
AddWidget(&coplanarCheckBox);

capsuleShadowCheckBox.Create("Capsule Shadow Disabled: ");
capsuleShadowCheckBox.SetTooltip("Disable receiving capsule shadows for this material.");
capsuleShadowCheckBox.SetPos(XMFLOAT2(x, y += step));
capsuleShadowCheckBox.SetSize(XMFLOAT2(hei, hei));
capsuleShadowCheckBox.OnClick([&](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
for (auto& x : editor->translator.selected)
{
MaterialComponent* material = get_material(scene, x);
if (material == nullptr)
continue;
material->SetCapsuleShadowDisabled(args.bValue);
}
});
AddWidget(&capsuleShadowCheckBox);


shaderTypeComboBox.Create("Shader: ");
shaderTypeComboBox.SetTooltip("Select a shader for this material. \nCustom shaders (*) will also show up here (see wi::renderer:RegisterCustomShader() for more info.)\nNote that custom shaders (*) can't select between blend modes, as they are created with an explicit blend mode.");
Expand Down Expand Up @@ -1187,6 +1203,7 @@ void MaterialWindow::SetEntity(Entity entity)
preferUncompressedCheckBox.SetCheck(material->IsPreferUncompressedTexturesEnabled());
disableStreamingCheckBox.SetCheck(material->IsTextureStreamingDisabled());
coplanarCheckBox.SetCheck(material->IsCoplanarBlending());
capsuleShadowCheckBox.SetCheck(material->IsCapsuleShadowDisabled());
normalMapSlider.SetValue(material->normalMapStrength);
roughnessSlider.SetValue(material->roughness);
reflectanceSlider.SetValue(material->reflectance);
Expand Down Expand Up @@ -1379,6 +1396,7 @@ void MaterialWindow::ResizeLayout()
add_right(preferUncompressedCheckBox);
add_right(disableStreamingCheckBox);
add_right(coplanarCheckBox);
add_right(capsuleShadowCheckBox);
add(shaderTypeComboBox);
add(blendModeComboBox);
add(shadingRateComboBox);
Expand Down
1 change: 1 addition & 0 deletions Editor/MaterialWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class MaterialWindow : public wi::gui::Window
wi::gui::CheckBox preferUncompressedCheckBox;
wi::gui::CheckBox disableStreamingCheckBox;
wi::gui::CheckBox coplanarCheckBox;
wi::gui::CheckBox capsuleShadowCheckBox;
wi::gui::ComboBox shaderTypeComboBox;
wi::gui::ComboBox blendModeComboBox;
wi::gui::ComboBox shadingRateComboBox;
Expand Down
16 changes: 14 additions & 2 deletions WickedEngine/shaders/ShaderInterop_Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ enum SHADERMATERIAL_OPTIONS
SHADERMATERIAL_OPTION_BIT_ADDITIVE = 1 << 9,
SHADERMATERIAL_OPTION_BIT_UNLIT = 1 << 10,
SHADERMATERIAL_OPTION_BIT_USE_VERTEXAO = 1 << 11,
SHADERMATERIAL_OPTION_BIT_CAPSULE_SHADOW_DISABLED = 1 << 12,
};

// Same as MaterialComponent::TEXTURESLOT
Expand Down Expand Up @@ -460,6 +461,7 @@ struct alignas(16) ShaderMaterial
inline bool IsTransparent() { return GetOptions() & SHADERMATERIAL_OPTION_BIT_TRANSPARENT; }
inline bool IsAdditive() { return GetOptions() & SHADERMATERIAL_OPTION_BIT_ADDITIVE; }
inline bool IsDoubleSided() { return GetOptions() & SHADERMATERIAL_OPTION_BIT_DOUBLE_SIDED; }
inline bool IsCapsuleShadowDisabled() { return GetOptions() & SHADERMATERIAL_OPTION_BIT_CAPSULE_SHADOW_DISABLED; }
};

// For binning shading based on shader types:
Expand Down Expand Up @@ -1017,9 +1019,13 @@ enum SHADER_ENTITY_FLAGS
{
ENTITY_FLAG_LIGHT_STATIC = 1 << 0,
ENTITY_FLAG_LIGHT_VOLUMETRICCLOUDS = 1 << 1,
ENTITY_FLAG_DECAL_BASECOLOR_ONLY_ALPHA = 1 << 0,
ENTITY_FLAG_DECAL_BASECOLOR_ONLY_ALPHA = 1 << 2,
ENTITY_FLAG_CAPSULE_SHADOW_COLLIDER = 1 << 3,
};

static const float CAPSULE_SHADOW_AFFECTION_RANGE = 2; // how far away the capsule shadow can reach outside of their own radius
static const float CAPSULE_SHADOW_BOLDEN = 1.2f;

static const uint SHADER_ENTITY_COUNT = 256;
static const uint SHADER_ENTITY_TILE_BUCKET_COUNT = SHADER_ENTITY_COUNT / 32;

Expand Down Expand Up @@ -1131,6 +1137,7 @@ enum FRAME_OPTIONS
OPTION_BIT_REALISTIC_SKY_HIGH_QUALITY = 1 << 17,
OPTION_BIT_REALISTIC_SKY_RECEIVE_SHADOW = 1 << 18,
OPTION_BIT_VOLUMETRICCLOUDS_RECEIVE_SHADOW = 1 << 19,
OPTION_BIT_CAPSULE_SHADOW_ENABLED = 1 << 20,
};

// ---------- Common Constant buffers: -----------------
Expand Down Expand Up @@ -1158,6 +1165,11 @@ struct alignas(16) FrameCB
uint giboost_packed; // force fp16 load
uint entity_culling_count;

uint capsuleshadow_fade_angle;
int indirect_debugbufferindex;
int padding0;
int padding1;

float blue_noise_phase;
int texture_random64x64_index;
int texture_bluenoise_index;
Expand Down Expand Up @@ -1193,7 +1205,7 @@ struct alignas(16) FrameCB
uint lights;
uint decals;
uint forces;
int indirect_debugbufferindex;
uint padding2;

ShaderEntity entityArray[SHADER_ENTITY_COUNT];
float4x4 matrixArray[SHADER_ENTITY_COUNT];
Expand Down
1 change: 1 addition & 0 deletions WickedEngine/shaders/Shaders_SOURCE.vcxitems
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<None Include="$(MSBuildThisFileDirectory)bitonicSortHF.hlsli" />
<None Include="$(MSBuildThisFileDirectory)BlockCompress.hlsli" />
<None Include="$(MSBuildThisFileDirectory)brdf.hlsli" />
<None Include="$(MSBuildThisFileDirectory)capsuleShadowHF.hlsli" />
<None Include="$(MSBuildThisFileDirectory)circle.hlsli" />
<None Include="$(MSBuildThisFileDirectory)ColorSpaceUtility.hlsli" />
<None Include="$(MSBuildThisFileDirectory)cone.hlsli" />
Expand Down
3 changes: 3 additions & 0 deletions WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@
<None Include="$(MSBuildThisFileDirectory)objectHF_mesh_shading.hlsli">
<Filter>HF</Filter>
</None>
<None Include="$(MSBuildThisFileDirectory)capsuleShadowHF.hlsli">
<Filter>HF</Filter>
</None>
</ItemGroup>
<ItemGroup>
<FxCompile Include="$(MSBuildThisFileDirectory)hairparticle_simulateCS.hlsl">
Expand Down
69 changes: 69 additions & 0 deletions WickedEngine/shaders/capsuleShadowHF.hlsli
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#ifndef CAPSULE_SHADOW_HF
#define CAPSULE_SHADOW_HF

// Source: https://www.shadertoy.com/view/3stcD4

float acosFast(float x) {
// Lagarde 2014, "Inverse trigonometric functions GPU optimization for AMD GCN architecture"
// This is the approximation of degree 1, with a max absolute error of 9.0x10^-3
float y = abs(x);
float p = -0.1565827 * y + 1.570796;
p *= sqrt(1.0 - y);
return x >= 0.0 ? p : PI - p;
}

float acosFastPositive(float x) {
// Lagarde 2014, "Inverse trigonometric functions GPU optimization for AMD GCN architecture"
float p = -0.1565827 * x + 1.570796;
return p * sqrt(1.0 - x);
}

float sphericalCapsIntersection(float cosCap1, float cosCap2, float cap2, float cosDistance) {
// Oat and Sander 2007, "Ambient Aperture Lighting"
// Approximation mentioned by Jimenez et al. 2016
float r1 = acosFastPositive(cosCap1);
float r2 = cap2;
float d = acosFast(cosDistance);

// We work with cosine angles, replace the original paper's use of
// cos(min(r1, r2)_ with max(cosCap1, cosCap2)
// We also remove a multiplication by 2 * PI to simplify the computation
// since we divide by 2 * PI at the call site

if (min(r1, r2) <= max(r1, r2) - d) {
return 1.0 - max(cosCap1, cosCap2);
} else if (r1 + r2 <= d) {
return 0.0;
}

float delta = abs(r1 - r2);
float x = 1.0 - saturate((d - delta) / max(r1 + r2 - delta, 0.0001));
// simplified smoothstep()
float area = sqr(x) * (-2.0 * x + 3.0);
return area * (1.0 - max(cosCap1, cosCap2));
}

float directionalOcclusionSphere(in float3 pos, in float4 sphere, in float4 cone) {
float3 occluder = sphere.xyz - pos;
float occluderLength2 = dot(occluder, occluder);
float3 occluderDir = occluder * rsqrt(occluderLength2);

float cosPhi = dot(occluderDir, cone.xyz);
// sqr(sphere.w) should be a uniform --> capsuleRadius^2
float cosTheta = sqrt(occluderLength2 / (sqr(sphere.w) + occluderLength2));
float cosCone = cos(cone.w);

return 1.0 - sphericalCapsIntersection(cosTheta, cosCone, cone.w, cosPhi) / (1.0 - cosCone);
}

float directionalOcclusionCapsule(in float3 pos, in float3 capsuleA, in float3 capsuleB, in float capsuleRadius, in float4 cone) {
float3 Ld = capsuleB - capsuleA;
float3 L0 = capsuleA - pos;
float a = dot(cone.xyz, Ld);
float t = saturate(dot(L0, a * cone.xyz - Ld) / (dot(Ld, Ld) - a * a));
float3 posToRay = capsuleA + t * Ld;

return directionalOcclusionSphere(pos, float4(posToRay, capsuleRadius), cone);
}

#endif // CAPSULE_SHADOW_HF
15 changes: 14 additions & 1 deletion WickedEngine/shaders/globals.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ inline half3 clipspace_to_uv(in half3 clipspace)
}

inline half3 GetSunColor() { return unpack_half3(GetWeather().sun_color); } // sun color with intensity applied
inline half3 GetSunDirection() { return unpack_half3(GetWeather().sun_direction); }
inline half3 GetSunDirection() { return normalize(unpack_half3(GetWeather().sun_direction)); }
inline half3 GetHorizonColor() { return unpack_half3(GetWeather().horizon); }
inline half3 GetZenithColor() { return unpack_half3(GetWeather().zenith); }
inline half3 GetAmbientColor() { return unpack_half3(GetWeather().ambient); }
Expand All @@ -881,6 +881,8 @@ inline float GetTime() { return GetFrame().time; }
inline float GetTimePrev() { return GetFrame().time_previous; }
inline float GetFrameCount() { return GetFrame().frame_count; }
inline min16uint2 GetTemporalAASampleRotation() { return uint2(GetFrame().temporalaa_samplerotation & 0xFF, (GetFrame().temporalaa_samplerotation >> 8u) & 0xFF); }
inline half GetCapsuleShadowFade() { return f16tof32(GetFrame().capsuleshadow_fade_angle); }
inline half GetCapsuleShadowAngle() { return f16tof32(GetFrame().capsuleshadow_fade_angle >> 16u); }
inline bool IsStaticSky() { return GetScene().globalenvmap >= 0; }
inline half GetGIBoost() { return unpack_half2(GetFrame().giboost_packed).x; }

Expand Down Expand Up @@ -1762,6 +1764,17 @@ inline half sphere_volume(in half radius)
return 4.0 / 3.0 * PI * radius * radius * radius;
}

inline float distance_squared(float3 a, float3 b)
{
float3 diff = b - a;
return dot(diff, diff);
}
inline half distance_squared(half3 a, half3 b)
{
half3 diff = b - a;
return dot(diff, diff);
}


float plane_point_distance(float3 planeOrigin, float3 planeNormal, float3 P)
{
Expand Down
33 changes: 33 additions & 0 deletions WickedEngine/shaders/lightCullingCS.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,39 @@ void main(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid :
}
}
}

// Capsule shadows:
for (uint i = forces().first_item() + groupIndex; i < forces().end_item(); i += TILED_CULLING_THREADSIZE * TILED_CULLING_THREADSIZE)
{
ShaderEntity entity = load_entity(i);
if ((entity.GetFlags() & ENTITY_FLAG_CAPSULE_SHADOW_COLLIDER) == 0)
continue;

float3 A = entity.position;
float3 B = entity.GetColliderTip();
half radius = entity.GetRange() * CAPSULE_SHADOW_BOLDEN;

// culling based on capsule-sphere:
float3 center = lerp(A, B, 0.5);
half range = distance(center, A) + radius + CAPSULE_SHADOW_AFFECTION_RANGE;

float3 positionVS = mul(GetCamera().view, float4(center, 1)).xyz;
Sphere sphere = { positionVS.xyz, range };
if (SphereInsideFrustum(sphere, GroupFrustum, nearClipVS, maxDepthVS))
{
AppendEntity_Transparent(i);

if (SphereIntersectsAABB(sphere, GroupAABB)) // tighter fit than sphere-frustum culling
{
#ifdef ADVANCED_CULLING
if (depth_mask & ConstructEntityMask(minDepthVS, __depthRangeRecip, sphere))
#endif
{
AppendEntity_Opaque(i);
}
}
}
}

#endif

Expand Down
Loading