Capsule shadows (#1055)
This commit is contained in:
@@ -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)");
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -56,6 +56,9 @@ public:
|
||||
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;
|
||||
|
||||
@@ -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));
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
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;
|
||||
|
||||
@@ -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.");
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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;
|
||||
|
||||
@@ -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: -----------------
|
||||
@@ -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;
|
||||
@@ -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];
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
@@ -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); }
|
||||
@@ -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; }
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "brdf.hlsli"
|
||||
#include "ShaderInterop_SurfelGI.h"
|
||||
#include "ShaderInterop_DDGI.h"
|
||||
#include "capsuleShadowHF.hlsli"
|
||||
|
||||
inline void LightMapping(in int lightmap, in float2 ATLAS, inout Lighting lighting, inout Surface surface)
|
||||
{
|
||||
@@ -492,6 +493,57 @@ inline void TiledLighting(inout Surface surface, inout Lighting lighting, uint f
|
||||
}
|
||||
}
|
||||
|
||||
// Capsule shadows:
|
||||
[branch]
|
||||
if ((GetFrame().options & OPTION_BIT_CAPSULE_SHADOW_ENABLED) && !surface.IsCapsuleShadowDisabled() && !forces().empty()) // capsule shadows are contained in forces array for now...
|
||||
{
|
||||
half4 cone = half4(GetSunDirection() * half3(-1, 1, -1), GetCapsuleShadowAngle()); // horizontally reverse of sun direction (better would be precomputed dominant light dir)
|
||||
half capsuleshadow = 1;
|
||||
|
||||
// Loop through light buckets in the tile:
|
||||
ShaderEntityIterator iterator = forces();
|
||||
for (uint bucket = iterator.first_bucket(); (bucket <= iterator.last_bucket()) && (capsuleshadow > 0); ++bucket)
|
||||
{
|
||||
uint bucket_bits = load_entitytile(flatTileIndex + bucket);
|
||||
bucket_bits = iterator.mask_entity(bucket, bucket_bits);
|
||||
|
||||
#ifndef ENTITY_TILE_UNIFORM
|
||||
// Bucket scalarizer - Siggraph 2017 - Improved Culling [Michal Drobot]:
|
||||
bucket_bits = WaveReadLaneFirst(WaveActiveBitOr(bucket_bits));
|
||||
#endif // ENTITY_TILE_UNIFORM
|
||||
|
||||
[loop]
|
||||
while ((bucket_bits != 0) && (capsuleshadow > 0))
|
||||
{
|
||||
// Retrieve global entity index from local bucket, then remove bit from local bucket:
|
||||
const uint bucket_bit_index = firstbitlow(bucket_bits);
|
||||
const uint entity_index = bucket * 32 + bucket_bit_index;
|
||||
bucket_bits ^= 1u << bucket_bit_index;
|
||||
|
||||
ShaderEntity entity = load_entity(entity_index);
|
||||
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;
|
||||
half occ = directionalOcclusionCapsule(surface.P, A, B, radius, cone);
|
||||
|
||||
// attenutaion based on capsule-sphere:
|
||||
float3 center = lerp(A, B, 0.5);
|
||||
half range = distance(center, A) + radius + CAPSULE_SHADOW_AFFECTION_RANGE;
|
||||
half range2 = range * range;
|
||||
half dist2 = distance_squared(surface.P, center);
|
||||
occ = 1 - saturate((1 - occ) * saturate(attenuation_pointlight(dist2, range, range2)));
|
||||
|
||||
capsuleshadow *= occ;
|
||||
}
|
||||
}
|
||||
capsuleshadow = lerp(capsuleshadow, 1, GetCapsuleShadowFade());
|
||||
capsuleshadow = saturate(capsuleshadow);
|
||||
surface.occlusion *= capsuleshadow;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline void TiledDecals(inout Surface surface, uint flatTileIndex, inout half4 surfaceMap, SamplerState sam)
|
||||
|
||||
@@ -98,6 +98,7 @@ struct Surface
|
||||
bool receiveshadow;
|
||||
bool is_backface;
|
||||
bool gi_applied;
|
||||
bool capsuleshadow_disabled;
|
||||
|
||||
// These will be computed when calling Update():
|
||||
half NdotV; // cos(angle between normal and view vector)
|
||||
@@ -150,6 +151,7 @@ struct Surface
|
||||
receiveshadow = true;
|
||||
is_backface = false;
|
||||
gi_applied = false;
|
||||
capsuleshadow_disabled = true;
|
||||
|
||||
uid_validate = 0;
|
||||
hit_depth = 0;
|
||||
@@ -181,6 +183,7 @@ struct Surface
|
||||
sss = material.GetSSS();
|
||||
sss_inv = material.GetSSSInverse();
|
||||
SetReceiveShadow(material.IsReceiveShadow());
|
||||
SetCapsuleShadowDisabled(material.IsCapsuleShadowDisabled());
|
||||
}
|
||||
|
||||
inline void create(
|
||||
@@ -305,10 +308,12 @@ struct Surface
|
||||
inline bool IsReceiveShadow() { return receiveshadow; }
|
||||
inline bool IsBackface() { return is_backface; }
|
||||
inline bool IsGIApplied() { return gi_applied; }
|
||||
inline bool IsCapsuleShadowDisabled() { return capsuleshadow_disabled; }
|
||||
|
||||
inline void SetReceiveShadow(bool value) { receiveshadow = value; }
|
||||
inline void SetBackface(bool value) { is_backface = value; }
|
||||
inline void SetGIApplied(bool value) { gi_applied = value; }
|
||||
inline void SetCapsuleShadowDisabled(bool value) { capsuleshadow_disabled = value; }
|
||||
|
||||
|
||||
ShaderMeshInstance inst;
|
||||
|
||||
@@ -190,20 +190,25 @@ namespace wi::jobsystem
|
||||
#ifdef PLATFORM_LINUX
|
||||
std::thread& worker = res.threads.emplace_back([threadID, priority, &res] {
|
||||
|
||||
// from the sched(2) manpage:
|
||||
// In the current [Linux 2.6.23+] implementation, each unit of
|
||||
// difference in the nice values of two processes results in a
|
||||
// factor of 1.25 in the degree to which the scheduler favors
|
||||
// the higher priority process.
|
||||
//
|
||||
// so 3 would mean that other (prio 0) threads are around twice as important
|
||||
|
||||
switch (priority) {
|
||||
case Priority::Low:
|
||||
case Priority::Streaming:
|
||||
// from the sched(2) manpage:
|
||||
// In the current [Linux 2.6.23+] implementation, each unit of
|
||||
// difference in the nice values of two processes results in a
|
||||
// factor of 1.25 in the degree to which the scheduler favors
|
||||
// the higher priority process.
|
||||
//
|
||||
// so 3 would mean that other (prio 0) threads are around twice as important
|
||||
if (setpriority(PRIO_PROCESS, 0, 3) != 0)
|
||||
{
|
||||
perror("setpriority");
|
||||
}
|
||||
case Priority::Streaming:
|
||||
if (setpriority(PRIO_PROCESS, 0, 2) != 0)
|
||||
{
|
||||
perror("setpriority");
|
||||
}
|
||||
break;
|
||||
case Priority::High:
|
||||
// nothing to do
|
||||
@@ -262,7 +267,7 @@ namespace wi::jobsystem
|
||||
}
|
||||
else if (priority == Priority::Streaming)
|
||||
{
|
||||
BOOL priority_result = SetThreadPriority(handle, THREAD_PRIORITY_LOWEST);
|
||||
BOOL priority_result = SetThreadPriority(handle, THREAD_PRIORITY_BELOW_NORMAL);
|
||||
assert(priority_result != 0);
|
||||
|
||||
std::wstring wthreadname = L"wi::job_st_" + std::to_wstring(threadID);
|
||||
|
||||
@@ -132,6 +132,15 @@ namespace wi::primitive
|
||||
{
|
||||
assert(radius >= 0);
|
||||
}
|
||||
inline Sphere getSphere() const
|
||||
{
|
||||
XMVECTOR B = XMLoadFloat3(&base);
|
||||
XMVECTOR T = XMLoadFloat3(&tip);
|
||||
Sphere ret;
|
||||
XMStoreFloat3(&ret.center, XMVectorLerp(B, T, 0.5f));
|
||||
XMStoreFloat(&ret.radius, XMVector3Length(B - T) * 0.5f);
|
||||
return ret;
|
||||
}
|
||||
inline AABB getAABB() const
|
||||
{
|
||||
XMFLOAT3 halfWidth = XMFLOAT3(radius, radius, radius);
|
||||
|
||||
+246
-158
File diff suppressed because it is too large
Load Diff
@@ -117,9 +117,10 @@ namespace wi::renderer
|
||||
ALLOW_ENVPROBES = 1 << 3,
|
||||
ALLOW_EMITTERS = 1 << 4,
|
||||
ALLOW_HAIRS = 1 << 5,
|
||||
ALLOW_REQUEST_REFLECTION = 1 << 6,
|
||||
ALLOW_OCCLUSION_CULLING = 1 << 7,
|
||||
ALLOW_SHADOW_ATLAS_PACKING = 1 << 8,
|
||||
ALLOW_COLLIDERS = 1 << 6,
|
||||
ALLOW_REQUEST_REFLECTION = 1 << 7,
|
||||
ALLOW_OCCLUSION_CULLING = 1 << 8,
|
||||
ALLOW_SHADOW_ATLAS_PACKING = 1 << 9,
|
||||
|
||||
ALLOW_EVERYTHING = ~0u
|
||||
};
|
||||
@@ -133,6 +134,7 @@ namespace wi::renderer
|
||||
wi::vector<uint32_t> visibleEmitters;
|
||||
wi::vector<uint32_t> visibleHairs;
|
||||
wi::vector<uint32_t> visibleLights;
|
||||
wi::vector<wi::scene::ColliderComponent> visibleColliders;
|
||||
wi::rectpacker::State shadow_packer;
|
||||
wi::rectpacker::Rect rain_blocker_shadow_rect;
|
||||
wi::vector<wi::rectpacker::Rect> visibleLightShadowRects;
|
||||
@@ -155,6 +157,7 @@ namespace wi::renderer
|
||||
visibleEnvProbes.clear();
|
||||
visibleEmitters.clear();
|
||||
visibleHairs.clear();
|
||||
visibleColliders.clear();
|
||||
|
||||
object_counter.store(0);
|
||||
light_counter.store(0);
|
||||
@@ -1129,6 +1132,12 @@ namespace wi::renderer
|
||||
void SetMeshletOcclusionCullingEnabled(bool value);
|
||||
bool IsMeshletOcclusionCullingEnabled();
|
||||
void Workaround( const int bug, wi::graphics::CommandList cmd);
|
||||
void SetCapsuleShadowEnabled(bool value);
|
||||
bool IsCapsuleShadowEnabled();
|
||||
void SetCapsuleShadowAngle(float value); // cone angle in radians
|
||||
float GetCapsuleShadowAngle();
|
||||
void SetCapsuleShadowFade(float value);
|
||||
float GetCapsuleShadowFade();
|
||||
|
||||
// Gets pick ray according to the current screen resolution and pointer coordinates. Can be used as input into RayIntersectWorld()
|
||||
wi::primitive::Ray GetPickRay(long cursorX, long cursorY, const wi::Canvas& canvas, const wi::scene::CameraComponent& camera = wi::scene::GetCamera());
|
||||
|
||||
@@ -1093,12 +1093,14 @@ namespace wi::scene
|
||||
collider_allocator_cpu.store(0u);
|
||||
collider_allocator_gpu.store(0u);
|
||||
collider_deinterleaved_data.reserve(
|
||||
sizeof(wi::primitive::AABB) * colliders.GetCount() +
|
||||
sizeof(wi::primitive::AABB) * colliders.GetCount() +
|
||||
sizeof(ColliderComponent) * colliders.GetCount() +
|
||||
sizeof(ColliderComponent) * colliders.GetCount()
|
||||
);
|
||||
aabb_colliders_cpu = (wi::primitive::AABB*)collider_deinterleaved_data.data();
|
||||
colliders_cpu = (ColliderComponent*)(aabb_colliders_cpu + colliders.GetCount());
|
||||
aabb_colliders_gpu = aabb_colliders_cpu + colliders.GetCount();
|
||||
colliders_cpu = (ColliderComponent*)(aabb_colliders_gpu + colliders.GetCount());
|
||||
colliders_gpu = colliders_cpu + colliders.GetCount();
|
||||
|
||||
for (size_t i = 0; i < colliders.GetCount(); ++i)
|
||||
@@ -1167,6 +1169,7 @@ namespace wi::scene
|
||||
{
|
||||
uint32_t index = collider_allocator_gpu.fetch_add(1u);
|
||||
colliders_gpu[index] = collider;
|
||||
aabb_colliders_gpu[index] = aabb;
|
||||
}
|
||||
}
|
||||
collider_count_cpu = collider_allocator_cpu.load();
|
||||
@@ -3726,12 +3729,14 @@ namespace wi::scene
|
||||
collider_allocator_cpu.store(0u);
|
||||
collider_allocator_gpu.store(0u);
|
||||
collider_deinterleaved_data.reserve(
|
||||
sizeof(wi::primitive::AABB)* colliders.GetCount() +
|
||||
sizeof(wi::primitive::AABB) * colliders.GetCount() +
|
||||
sizeof(ColliderComponent) * colliders.GetCount() +
|
||||
sizeof(ColliderComponent) * colliders.GetCount()
|
||||
);
|
||||
aabb_colliders_cpu = (wi::primitive::AABB*)collider_deinterleaved_data.data();
|
||||
colliders_cpu = (ColliderComponent*)(aabb_colliders_cpu + colliders.GetCount());
|
||||
aabb_colliders_gpu = aabb_colliders_cpu + colliders.GetCount();
|
||||
colliders_cpu = (ColliderComponent*)(aabb_colliders_gpu + colliders.GetCount());
|
||||
colliders_gpu = colliders_cpu + colliders.GetCount();
|
||||
|
||||
wi::jobsystem::Dispatch(ctx, (uint32_t)colliders.GetCount(), small_subtask_groupsize, [&](wi::jobsystem::JobArgs args) {
|
||||
@@ -3806,6 +3811,7 @@ namespace wi::scene
|
||||
{
|
||||
uint32_t index = collider_allocator_gpu.fetch_add(1u);
|
||||
colliders_gpu[index] = collider;
|
||||
aabb_colliders_gpu[index] = aabb;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -279,6 +279,7 @@ namespace wi::scene
|
||||
uint32_t collider_count_cpu = 0;
|
||||
uint32_t collider_count_gpu = 0;
|
||||
wi::primitive::AABB* aabb_colliders_cpu = nullptr;
|
||||
wi::primitive::AABB* aabb_colliders_gpu = nullptr;
|
||||
ColliderComponent* colliders_cpu = nullptr;
|
||||
ColliderComponent* colliders_gpu = nullptr;
|
||||
wi::BVH collider_bvh;
|
||||
|
||||
@@ -374,6 +374,10 @@ namespace wi::scene
|
||||
{
|
||||
material.options_stencilref |= SHADERMATERIAL_OPTION_BIT_USE_VERTEXAO;
|
||||
}
|
||||
if (IsCapsuleShadowDisabled())
|
||||
{
|
||||
material.options_stencilref |= SHADERMATERIAL_OPTION_BIT_CAPSULE_SHADOW_DISABLED;
|
||||
}
|
||||
|
||||
material.options_stencilref |= wi::renderer::CombineStencilrefs(engineStencilRef, userStencilRef) << 24u;
|
||||
|
||||
|
||||
@@ -134,6 +134,7 @@ namespace wi::scene
|
||||
DISABLE_VERTEXAO = 1 << 14,
|
||||
DISABLE_TEXTURE_STREAMING = 1 << 15,
|
||||
COPLANAR_BLENDING = 1 << 16, // force transparent material draw in opaque pass (useful for coplanar polygons)
|
||||
DISABLE_CAPSULE_SHADOW = 1 << 17,
|
||||
};
|
||||
uint32_t _flags = CAST_SHADOW;
|
||||
|
||||
@@ -364,6 +365,10 @@ namespace wi::scene
|
||||
interiorMappingRotation = value;
|
||||
}
|
||||
|
||||
constexpr bool IsCapsuleShadowDisabled() const { return _flags & DISABLE_CAPSULE_SHADOW; }
|
||||
constexpr void SetCapsuleShadowDisabled(bool value = true) { if (value) { _flags |= DISABLE_CAPSULE_SHADOW; } else { _flags &= ~DISABLE_CAPSULE_SHADOW; } }
|
||||
|
||||
|
||||
void SetPreferUncompressedTexturesEnabled(bool value = true) { if (value) { _flags |= PREFER_UNCOMPRESSED_TEXTURES; } else { _flags &= ~PREFER_UNCOMPRESSED_TEXTURES; } CreateRenderData(true); }
|
||||
|
||||
// The MaterialComponent will be written to ShaderMaterial (a struct that is optimized for GPU use)
|
||||
@@ -1842,14 +1847,17 @@ namespace wi::scene
|
||||
EMPTY = 0,
|
||||
CPU = 1 << 0,
|
||||
GPU = 1 << 1,
|
||||
CAPSULE_SHADOW = 1 << 2,
|
||||
};
|
||||
uint32_t _flags = CPU;
|
||||
|
||||
constexpr void SetCPUEnabled(bool value = true) { if (value) { _flags |= CPU; } else { _flags &= ~CPU; } }
|
||||
constexpr void SetGPUEnabled(bool value = true) { if (value) { _flags |= GPU; } else { _flags &= ~GPU; } }
|
||||
constexpr void SetCapsuleShadowEnabled(bool value = true) { if (value) { _flags |= CAPSULE_SHADOW; } else { _flags &= ~CAPSULE_SHADOW; } }
|
||||
|
||||
constexpr bool IsCPUEnabled() const { return _flags & CPU; }
|
||||
constexpr bool IsGPUEnabled() const { return _flags & GPU; }
|
||||
constexpr bool IsCapsuleShadowEnabled() const { return _flags & CAPSULE_SHADOW; }
|
||||
|
||||
enum class Shape
|
||||
{
|
||||
@@ -1868,6 +1876,7 @@ namespace wi::scene
|
||||
wi::primitive::Capsule capsule;
|
||||
wi::primitive::Plane plane;
|
||||
uint32_t layerMask = ~0u;
|
||||
float dist = 0;
|
||||
|
||||
void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri);
|
||||
};
|
||||
@@ -2034,6 +2043,7 @@ namespace wi::scene
|
||||
LOOKAT = 1 << 0,
|
||||
RAGDOLL_PHYSICS = 1 << 1,
|
||||
DISABLE_INTERSECTION = 1 << 2,
|
||||
DISABLE_CAPSULE_SHADOW = 1 << 3,
|
||||
};
|
||||
uint32_t _flags = LOOKAT;
|
||||
|
||||
@@ -2112,10 +2122,12 @@ namespace wi::scene
|
||||
constexpr bool IsLookAtEnabled() const { return _flags & LOOKAT; }
|
||||
constexpr bool IsRagdollPhysicsEnabled() const { return _flags & RAGDOLL_PHYSICS; }
|
||||
constexpr bool IsIntersectionDisabled() const { return _flags & DISABLE_INTERSECTION; }
|
||||
constexpr bool IsCapsuleShadowDisabled() const { return _flags & DISABLE_CAPSULE_SHADOW; }
|
||||
|
||||
constexpr void SetLookAtEnabled(bool value = true) { if (value) { _flags |= LOOKAT; } else { _flags &= ~LOOKAT; } }
|
||||
constexpr void SetRagdollPhysicsEnabled(bool value = true) { if (value) { _flags |= RAGDOLL_PHYSICS; } else { _flags &= ~RAGDOLL_PHYSICS; } }
|
||||
constexpr void SetIntersectionDisabled(bool value = true) { if (value) { _flags |= DISABLE_INTERSECTION; } else { _flags &= ~DISABLE_INTERSECTION; } }
|
||||
constexpr void SetCapsuleShadowDisabled(bool value = true) { if (value) { _flags |= DISABLE_CAPSULE_SHADOW; } else { _flags &= ~DISABLE_CAPSULE_SHADOW; } }
|
||||
|
||||
XMFLOAT2 head_rotation_max = XMFLOAT2(XM_PI / 3.0f, XM_PI / 6.0f);
|
||||
float head_rotation_speed = 0.1f;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace wi::version
|
||||
// minor features, major updates, breaking compatibility changes
|
||||
const int minor = 71;
|
||||
// minor bug fixes, alterations, refactors, updates
|
||||
const int revision = 694;
|
||||
const int revision = 695;
|
||||
|
||||
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user