From 86c748ed98192485e8d47e1013f41ae44fdfa249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Tue, 28 Jun 2022 23:10:48 +0200 Subject: [PATCH] Spotlight inner cone, GLTF KHR_lights_punctual (#474) * added inner cone angle to spotlights * refactors * different shader entity packing * KHR_lights_punctual * features.txt * spot cutoff * seri * fixes * update * angularAttenuation mad * refactor * attenuation refactors --- Editor/Editor.cpp | 2 +- Editor/ForceFieldWindow.cpp | 4 +- Editor/LightWindow.cpp | 23 ++++- Editor/LightWindow.h | 1 + Editor/ModelImporter_GLTF.cpp | 58 ++++++++++- WickedEngine/ArchiveVersionHistory.txt | 1 + WickedEngine/shaders/ShaderInterop_Renderer.h | 96 +++++++++++-------- WickedEngine/shaders/ddgi_raytraceCS.hlsl | 35 +++---- .../shaders/emittedparticle_simulateCS.hlsl | 2 +- .../shaders/forceFieldPlaneVisualizerVS.hlsl | 2 +- .../shaders/forceFieldPointVisualizerVS.hlsl | 2 +- .../shaders/forceFieldVisualizerPS.hlsl | 8 +- .../shaders/hairparticle_simulateCS.hlsl | 2 +- WickedEngine/shaders/lightingHF.hlsli | 43 ++++++--- WickedEngine/shaders/objectHF.hlsli | 4 - WickedEngine/shaders/objectPS_voxelizer.hlsl | 26 ++--- WickedEngine/shaders/raytraceCS.hlsl | 31 +++--- WickedEngine/shaders/renderlightmapPS.hlsl | 33 +++---- WickedEngine/shaders/screenspaceshadowCS.hlsl | 14 +-- WickedEngine/shaders/surfel_raytraceCS.hlsl | 33 +++---- WickedEngine/shaders/vPointLightVS.hlsl | 11 +-- WickedEngine/shaders/vSpotLightVS.hlsl | 11 +-- .../volumetricLight_DirectionalPS.hlsl | 2 +- .../shaders/volumetricLight_PointPS.hlsl | 8 +- .../shaders/volumetricLight_SpotPS.hlsl | 15 ++- WickedEngine/wiArchive.cpp | 2 +- WickedEngine/wiRenderer.cpp | 61 ++++++++---- WickedEngine/wiScene.cpp | 29 +++--- WickedEngine/wiScene.h | 15 +-- WickedEngine/wiScene_BindLua.cpp | 2 +- WickedEngine/wiScene_Serializers.cpp | 18 +++- credits.txt | 2 + features.txt | 4 +- 33 files changed, 346 insertions(+), 254 deletions(-) diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index f0b6e6f48..2c75d098d 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -1532,7 +1532,7 @@ void EditorComponent::Update(float dt) TransformComponent& transform = scene.transforms.Create(grass_interaction_entity); force.type = ENTITY_TYPE_FORCEFIELD_POINT; force.gravity = -80; - force.range_local = 3; + force.range = 3; transform.Translate(P); break; } diff --git a/Editor/ForceFieldWindow.cpp b/Editor/ForceFieldWindow.cpp index 1a49c7699..a67fef5e6 100644 --- a/Editor/ForceFieldWindow.cpp +++ b/Editor/ForceFieldWindow.cpp @@ -109,7 +109,7 @@ void ForceFieldWindow::Create(EditorComponent* editor) ForceFieldComponent* force = wi::scene::GetScene().forces.GetComponent(entity); if (force != nullptr) { - force->range_local = args.fValue; + force->range = args.fValue; } }); rangeSlider.SetEnabled(false); @@ -134,7 +134,7 @@ void ForceFieldWindow::SetEntity(Entity entity) { typeComboBox.SetSelected(force->type == ENTITY_TYPE_FORCEFIELD_POINT ? 0 : 1); gravitySlider.SetValue(force->gravity); - rangeSlider.SetValue(force->range_local); + rangeSlider.SetValue(force->range); gravitySlider.SetEnabled(true); rangeSlider.SetEnabled(true); diff --git a/Editor/LightWindow.cpp b/Editor/LightWindow.cpp index 8753a82e1..34a640ea1 100644 --- a/Editor/LightWindow.cpp +++ b/Editor/LightWindow.cpp @@ -40,7 +40,7 @@ void LightWindow::Create(EditorComponent* editor) LightComponent* light = wi::scene::GetScene().lights.GetComponent(entity); if (light != nullptr) { - light->range_local = args.fValue; + light->range = args.fValue; } }); rangeSlider.SetEnabled(false); @@ -103,6 +103,20 @@ void LightWindow::Create(EditorComponent* editor) fovSlider.SetTooltip("Adjust the cone aperture for spotlight."); AddWidget(&fovSlider); + fovInnerSlider.Create(0, XM_PI - 0.01f, 0, 100000, "FOV (inner): "); + fovInnerSlider.SetSize(XMFLOAT2(100, hei)); + fovInnerSlider.SetPos(XMFLOAT2(x, y += step)); + fovInnerSlider.OnSlide([&](wi::gui::EventArgs args) { + LightComponent* light = wi::scene::GetScene().lights.GetComponent(entity); + if (light != nullptr) + { + light->fov_inner = args.fValue; + } + }); + fovInnerSlider.SetEnabled(false); + fovInnerSlider.SetTooltip("Adjust the inner cone aperture for spotlight.\n(The inner cone will always be inside the outer/main cone)"); + AddWidget(&fovInnerSlider); + shadowCheckBox.Create("Shadow: "); shadowCheckBox.SetSize(XMFLOAT2(hei, hei)); shadowCheckBox.SetPos(XMFLOAT2(x, y += step)); @@ -314,11 +328,12 @@ void LightWindow::SetEntity(Entity entity) { energySlider.SetEnabled(true); energySlider.SetValue(light->energy); - rangeSlider.SetValue(light->range_local); + rangeSlider.SetValue(light->range); //radiusSlider.SetValue(light->radius); //widthSlider.SetValue(light->width); //heightSlider.SetValue(light->height); fovSlider.SetValue(light->fov); + fovInnerSlider.SetValue(light->fov_inner); shadowCheckBox.SetEnabled(true); shadowCheckBox.SetCheck(light->IsCastingShadow()); haloCheckBox.SetEnabled(true); @@ -355,6 +370,7 @@ void LightWindow::SetEntity(Entity entity) widthSlider.SetEnabled(false); heightSlider.SetEnabled(false); fovSlider.SetEnabled(false); + fovInnerSlider.SetEnabled(false); shadowCheckBox.SetEnabled(false); haloCheckBox.SetEnabled(false); volumetricsCheckBox.SetEnabled(false); @@ -375,6 +391,7 @@ void LightWindow::SetLightType(LightComponent::LightType type) { rangeSlider.SetEnabled(false); fovSlider.SetEnabled(false); + fovInnerSlider.SetEnabled(false); } else { @@ -395,10 +412,12 @@ void LightWindow::SetLightType(LightComponent::LightType type) if (type == LightComponent::SPOT) { fovSlider.SetEnabled(true); + fovInnerSlider.SetEnabled(true); } else { fovSlider.SetEnabled(false); + fovInnerSlider.SetEnabled(false); } } } diff --git a/Editor/LightWindow.h b/Editor/LightWindow.h index e426011fa..6507427bb 100644 --- a/Editor/LightWindow.h +++ b/Editor/LightWindow.h @@ -19,6 +19,7 @@ public: wi::gui::Slider widthSlider; wi::gui::Slider heightSlider; wi::gui::Slider fovSlider; + wi::gui::Slider fovInnerSlider; wi::gui::CheckBox shadowCheckBox; wi::gui::CheckBox haloCheckBox; wi::gui::CheckBox volumetricsCheckBox; diff --git a/Editor/ModelImporter_GLTF.cpp b/Editor/ModelImporter_GLTF.cpp index 3e4906373..9131c30a4 100644 --- a/Editor/ModelImporter_GLTF.cpp +++ b/Editor/ModelImporter_GLTF.cpp @@ -190,7 +190,14 @@ void LoadNode(int nodeIndex, Entity parent, LoaderState& state) node.name = "cam" + std::to_string(camID++); } - entity = scene.Entity_CreateCamera(node.name, wi::scene::GetCamera().width, wi::scene::GetCamera().height, 0.1f, 800); + entity = scene.Entity_CreateCamera(node.name, wi::scene::GetCamera().width, wi::scene::GetCamera().height); + } + + auto ext_lights_punctual = node.extensions.find("KHR_lights_punctual"); + if (ext_lights_punctual != node.extensions.end()) + { + // https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_lights_punctual + entity = scene.Entity_CreateLight(""); // light component will be filled later } if (entity == INVALID_ENTITY) @@ -1352,6 +1359,52 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) } + // Create lights: + int lightIndex = 0; + for (auto& x : state.gltfModel.lights) + { + Entity entity = scene.lights.GetEntity(lightIndex); + LightComponent& light = scene.lights[lightIndex++]; + NameComponent& name = *scene.names.GetComponent(entity); + name = x.name; + + if (!x.type.compare("spot")) + { + light.type = LightComponent::LightType::SPOT; + } + if (!x.type.compare("point")) + { + light.type = LightComponent::LightType::POINT; + } + if (!x.type.compare("directional")) + { + light.type = LightComponent::LightType::DIRECTIONAL; + } + + if (!x.color.empty()) + { + light.color = XMFLOAT3(float(x.color[0]), float(x.color[1]), float(x.color[2])); + } + + light.energy = float(x.intensity); + light.range = x.range > 0 ? float(x.range) : std::numeric_limits::max(); + light.fov = float(x.spot.outerConeAngle) * 2; // *2: in engine, fov is a value directly used for shadow camera, in gltf, it's cone angle + light.fov_inner = float(x.spot.innerConeAngle) * 2; // *2: in engine, fov is a value directly used for shadow camera, in gltf, it's cone angle + + // In gltf, default light direction is forward, in engine, it's downwards, so apply a rotation: + TransformComponent& transform = *scene.transforms.GetComponent(entity); + transform.RotateRollPitchYaw(XMFLOAT3(XM_PIDIV2, 0, 0)); + } + + int cameraIndex = 0; + for (auto& x : state.gltfModel.cameras) + { + Entity entity = scene.cameras.GetEntity(cameraIndex); + CameraComponent& camera = scene.cameras[cameraIndex++]; + TransformComponent& transform = *scene.transforms.GetComponent(entity); + transform.RotateRollPitchYaw(XMFLOAT3(XM_PI, 0, XM_PI)); + } + if (transform_to_LH) { TransformComponent& transform = *scene.transforms.GetComponent(rootEntity); @@ -1359,4 +1412,7 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) transform.SetDirty(); } + // Update the scene, to have up to date values immediately after loading: + // For example, snap to camera functionality relies on this + scene.Update(0); } diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index f12e765b7..863f0e7de 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wi::Archive versions +82: serialized LightComponent::fov_inner 81: serialized LightComponent::forced_shadow_resolution 80: object draw distance 79: removed terrain blend materials from mesh diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index beb9560fe..8df2f86c4 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -417,6 +417,7 @@ struct ObjectPushConstants uint instance_offset; }; + // Warning: the size of this structure directly affects shader performance. // Try to reduce it as much as possible! // Keep it aligned to 16 bytes for best performance! @@ -426,12 +427,11 @@ struct ShaderEntity uint type8_flags8_range16; uint2 direction16_coneAngleCos16; - uint energy16_X16; // 16 bits free - uint color; + uint2 color; // half4 packed uint layerMask; uint indices; - uint cubeRemap; + uint remap; uint userdata; float4 shadowAtlasMulAdd; @@ -444,46 +444,48 @@ struct ShaderEntity } inline uint GetFlags() { - return (type8_flags8_range16 >> 8) & 0xFF; + return (type8_flags8_range16 >> 8u) & 0xFF; } inline float GetRange() { - return f16tof32((type8_flags8_range16 >> 16) & 0xFFFF); + return f16tof32(type8_flags8_range16 >> 16u); } inline float3 GetDirection() { - return float3( - f16tof32(direction16_coneAngleCos16.x & 0xFFFF), - f16tof32((direction16_coneAngleCos16.x >> 16) & 0xFFFF), - f16tof32(direction16_coneAngleCos16.y & 0xFFFF) - ); + return normalize(float3( + f16tof32(direction16_coneAngleCos16.x), + f16tof32(direction16_coneAngleCos16.x >> 16u), + f16tof32(direction16_coneAngleCos16.y) + )); } inline float GetConeAngleCos() { - return f16tof32((direction16_coneAngleCos16.y >> 16) & 0xFFFF); + return f16tof32(direction16_coneAngleCos16.y >> 16u); } - inline float GetEnergy() + inline float GetAngleScale() { - return f16tof32(energy16_X16 & 0xFFFF); + return f16tof32(remap); + } + inline float GetAngleOffset() + { + return f16tof32(remap >> 16u); } inline float GetCubemapDepthRemapNear() { - return f16tof32(cubeRemap & 0xFFFF); + return f16tof32(remap); } inline float GetCubemapDepthRemapFar() { - return f16tof32((cubeRemap >> 16) & 0xFFFF); + return f16tof32(remap >> 16u); } inline float4 GetColor() { - float4 fColor; - - fColor.x = (float)((color >> 0) & 0xFF) / 255.0f; - fColor.y = (float)((color >> 8) & 0xFF) / 255.0f; - fColor.z = (float)((color >> 16) & 0xFF) / 255.0f; - fColor.w = (float)((color >> 24) & 0xFF) / 255.0f; - - return fColor; + float4 retVal; + retVal.x = f16tof32(color.x); + retVal.y = f16tof32(color.x >> 16u); + retVal.z = f16tof32(color.y); + retVal.w = f16tof32(color.y >> 16u); + return retVal; } inline uint GetMatrixIndex() { @@ -491,15 +493,16 @@ struct ShaderEntity } inline uint GetTextureIndex() { - return (indices >> 16) & 0xFFFF; + return indices >> 16u; } inline bool IsCastingShadow() { return indices != ~0; } - - // Load decal props: - inline float GetEmissive() { return GetEnergy(); } + inline float GetGravity() + { + return GetAngleOffset(); + } #else // Application-side: @@ -509,38 +512,53 @@ struct ShaderEntity } inline void SetFlags(uint flags) { - type8_flags8_range16 |= (flags & 0xFF) << 8; + type8_flags8_range16 |= (flags & 0xFF) << 8u; } inline void SetRange(float value) { - type8_flags8_range16 |= XMConvertFloatToHalf(value) << 16; + type8_flags8_range16 |= XMConvertFloatToHalf(value) << 16u; + } + inline void SetColor(float4 value) + { + color.x |= XMConvertFloatToHalf(value.x); + color.x |= XMConvertFloatToHalf(value.y) << 16u; + color.y |= XMConvertFloatToHalf(value.z); + color.y |= XMConvertFloatToHalf(value.w) << 16u; } inline void SetDirection(float3 value) { direction16_coneAngleCos16.x |= XMConvertFloatToHalf(value.x); - direction16_coneAngleCos16.x |= XMConvertFloatToHalf(value.y) << 16; + direction16_coneAngleCos16.x |= XMConvertFloatToHalf(value.y) << 16u; direction16_coneAngleCos16.y |= XMConvertFloatToHalf(value.z); } inline void SetConeAngleCos(float value) { - direction16_coneAngleCos16.y |= XMConvertFloatToHalf(value) << 16; + direction16_coneAngleCos16.y |= XMConvertFloatToHalf(value) << 16u; } - inline void SetEnergy(float value) + inline void SetAngleScale(float value) { - energy16_X16 |= XMConvertFloatToHalf(value); + remap |= XMConvertFloatToHalf(value); + } + inline void SetAngleOffset(float value) + { + remap |= XMConvertFloatToHalf(value) << 16u; } inline void SetCubeRemapNear(float value) { - cubeRemap |= XMConvertFloatToHalf(value); + remap |= XMConvertFloatToHalf(value); } inline void SetCubeRemapFar(float value) { - cubeRemap |= XMConvertFloatToHalf(value) << 16; + remap |= XMConvertFloatToHalf(value) << 16u; } inline void SetIndices(uint matrixIndex, uint textureIndex) { indices = matrixIndex & 0xFFFF; - indices |= (textureIndex & 0xFFFF) << 16; + indices |= (textureIndex & 0xFFFF) << 16u; + } + inline void SetGravity(float value) + { + SetAngleOffset(value); } #endif // __cplusplus @@ -778,9 +796,9 @@ CBUFFER(ForwardEntityMaskCB, CBSLOT_RENDERER_FORWARD_LIGHTMASK) CBUFFER(VolumeLightCB, CBSLOT_RENDERER_VOLUMELIGHT) { - float4x4 lightWorld; - float4 lightColor; - float4 lightEnerdis; + float4x4 xLightWorld; + float4 xLightColor; + float4 xLightEnerdis; }; struct LensFlarePush diff --git a/WickedEngine/shaders/ddgi_raytraceCS.hlsl b/WickedEngine/shaders/ddgi_raytraceCS.hlsl index 19e7cdb9a..3be4caf10 100644 --- a/WickedEngine/shaders/ddgi_raytraceCS.hlsl +++ b/WickedEngine/shaders/ddgi_raytraceCS.hlsl @@ -155,7 +155,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn [branch] if (NdotL > 0) { - float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + float3 lightColor = light.GetColor().rgb; [branch] if (GetFrame().options & OPTION_BIT_REALISTIC_SKY) @@ -171,7 +171,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -183,15 +184,9 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn [branch] if (NdotL > 0) { - const float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + const float3 lightColor = light.GetColor().rgb; - lighting.direct.diffuse = lightColor; - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - const float attenuation = att * att; - - lighting.direct.diffuse *= attenuation; + lighting.direct.diffuse = lightColor * attenuation_pointlight(dist, dist2, range, range2); } } } @@ -200,7 +195,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -212,22 +208,15 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn [branch] if (NdotL > 0) { - const float SpotFactor = dot(L, light.GetDirection()); - const float spotCutOff = light.GetConeAngleCos(); + const float spot_factor = dot(L, light.GetDirection()); + const float spot_cutoff = light.GetConeAngleCos(); [branch] - if (SpotFactor > spotCutOff) + if (spot_factor > spot_cutoff) { - const float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + const float3 lightColor = light.GetColor().rgb; - lighting.direct.diffuse = lightColor; - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - float attenuation = att * att; - attenuation *= saturate((1.0 - (1.0 - SpotFactor) * 1.0 / (1.0 - spotCutOff))); - - lighting.direct.diffuse *= attenuation; + lighting.direct.diffuse = lightColor * attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); } } } diff --git a/WickedEngine/shaders/emittedparticle_simulateCS.hlsl b/WickedEngine/shaders/emittedparticle_simulateCS.hlsl index 356f97872..1a93e0dc1 100644 --- a/WickedEngine/shaders/emittedparticle_simulateCS.hlsl +++ b/WickedEngine/shaders/emittedparticle_simulateCS.hlsl @@ -62,7 +62,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) dir = forceField.GetDirection(); } - particle.force += dir * forceField.GetEnergy() * (1 - saturate(dist * forceField.GetRange())); // GetRange() is actually uploaded as 1.0 / range + particle.force += dir * forceField.GetGravity() * (1 - saturate(dist / forceField.GetRange())); } } diff --git a/WickedEngine/shaders/forceFieldPlaneVisualizerVS.hlsl b/WickedEngine/shaders/forceFieldPlaneVisualizerVS.hlsl index e5dbec688..c039ddf08 100644 --- a/WickedEngine/shaders/forceFieldPlaneVisualizerVS.hlsl +++ b/WickedEngine/shaders/forceFieldPlaneVisualizerVS.hlsl @@ -16,7 +16,7 @@ PSIn main(uint vID : SV_VERTEXID) uint forceFieldID = GetFrame().forcefieldarray_offset + (uint)g_xColor.w; ShaderEntity forceField = load_entity(forceFieldID); - Out.pos.xyz *= forceField.GetConeAngleCos(); // range... + Out.pos.xyz *= forceField.GetRange(); Out.pos.xyz += forceField.position; Out.pos3D = Out.pos; diff --git a/WickedEngine/shaders/forceFieldPointVisualizerVS.hlsl b/WickedEngine/shaders/forceFieldPointVisualizerVS.hlsl index 27cebe58c..807458f28 100644 --- a/WickedEngine/shaders/forceFieldPointVisualizerVS.hlsl +++ b/WickedEngine/shaders/forceFieldPointVisualizerVS.hlsl @@ -18,7 +18,7 @@ PSIn main(uint vID : SV_VERTEXID) uint forceFieldID = GetFrame().forcefieldarray_offset + (uint)g_xColor.w; ShaderEntity forceField = load_entity(forceFieldID); - Out.pos.xyz *= forceField.GetConeAngleCos(); // range... + Out.pos.xyz *= forceField.GetRange(); Out.pos.xyz += forceField.position; Out.pos3D = Out.pos; diff --git a/WickedEngine/shaders/forceFieldVisualizerPS.hlsl b/WickedEngine/shaders/forceFieldVisualizerPS.hlsl index 8ceaf57dd..cda39cc92 100644 --- a/WickedEngine/shaders/forceFieldVisualizerPS.hlsl +++ b/WickedEngine/shaders/forceFieldVisualizerPS.hlsl @@ -12,27 +12,27 @@ float4 main(PSIn input) : SV_TARGET uint forceFieldID = GetFrame().forcefieldarray_offset + (uint)g_xColor.w; ShaderEntity forceField = load_entity(forceFieldID); - float4 color = forceField.GetEnergy() < 0 ? float4(0, 0, 1, 1) : float4(1, 0, 0, 1); + float4 color = forceField.GetGravity() < 0 ? float4(0, 0, 1, 1) : float4(1, 0, 0, 1); if (forceField.GetType() == ENTITY_TYPE_FORCEFIELD_POINT) { // point-like forcefield: float3 centerToPos = normalize(input.pos3D.xyz - forceField.position.xyz); float3 eyeToCenter = normalize(forceField.position.xyz - g_xColor.xyz); - color.a *= pow(saturate(dot(centerToPos, eyeToCenter)), 1.0f / max(0.0001f, abs(forceField.GetEnergy()))); + color.a *= pow(saturate(dot(centerToPos, eyeToCenter)), 1.0f / max(0.0001f, abs(forceField.GetGravity()))); } else { // planar forcefield: float3 dir = forceField.position - input.pos3D.xyz; float dist = dot(forceField.GetDirection(), dir); - color.a *= pow(1 - saturate(dist * forceField.GetRange()), 1.0f / max(0.0001f, abs(forceField.GetEnergy()))); + color.a *= pow(1 - saturate(dist / forceField.GetRange()), 1.0f / max(0.0001f, abs(forceField.GetGravity()))); } float2 pTex = input.pos2D.xy / input.pos2D.w * float2(0.5f, -0.5f) + 0.5f; float4 depthScene = texture_lineardepth.GatherRed(sampler_linear_clamp, pTex) * GetCamera().z_far; float depthFragment = input.pos2D.w; - float fade = saturate(1.0 / forceField.GetConeAngleCos() * abs(forceField.GetEnergy()) * (max(max(depthScene.x, depthScene.y), max(depthScene.z, depthScene.w)) - depthFragment)); + float fade = saturate(1.0 / forceField.GetRange() * abs(forceField.GetGravity()) * (max(max(depthScene.x, depthScene.y), max(depthScene.z, depthScene.w)) - depthFragment)); color.a *= fade; color.a = saturate(color.a * 0.78f); diff --git a/WickedEngine/shaders/hairparticle_simulateCS.hlsl b/WickedEngine/shaders/hairparticle_simulateCS.hlsl index 7c4b447fb..8a9a2af70 100644 --- a/WickedEngine/shaders/hairparticle_simulateCS.hlsl +++ b/WickedEngine/shaders/hairparticle_simulateCS.hlsl @@ -139,7 +139,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn dir = forceField.GetDirection(); } - force += dir * forceField.GetEnergy() * (1 - saturate(dist * forceField.GetRange())); // GetRange() is actually uploaded as 1.0 / range + force += dir * forceField.GetGravity() * (1 - saturate(dist / forceField.GetRange())); } } diff --git a/WickedEngine/shaders/lightingHF.hlsli b/WickedEngine/shaders/lightingHF.hlsli index 192eecf2b..ef63e9497 100644 --- a/WickedEngine/shaders/lightingHF.hlsli +++ b/WickedEngine/shaders/lightingHF.hlsli @@ -184,7 +184,7 @@ inline void light_directional(in ShaderEntity light, in Surface surface, inout L [branch] if (any(shadow)) { - float3 light_color = light.GetColor().rgb * light.GetEnergy() * shadow; + float3 light_color = light.GetColor().rgb * shadow; [branch] if (GetFrame().options & OPTION_BIT_REALISTIC_SKY) @@ -216,11 +216,23 @@ inline void light_directional(in ShaderEntity light, in Surface surface, inout L } } } + +inline float attenuation_pointlight(in float dist, in float dist2, in float range, in float range2) +{ +#if 0 + // GLTF recommendation: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_lights_punctual#range-property + return saturate(1 - pow(dist / range, 4)) / dist2; +#else + // Old attenuation: + return sqr(saturate(1 - (dist2 / range2))); +#endif +} inline void light_point(in ShaderEntity light, in Surface surface, inout Lighting lighting, in float shadow_mask = 1) { float3 L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -252,11 +264,8 @@ inline void light_point(in ShaderEntity light, in Surface surface, inout Lightin [branch] if (any(shadow)) { - float3 light_color = light.GetColor().rgb * light.GetEnergy() * shadow; - - const float att = saturate(1 - (dist2 / range2)); - const float attenuation = att * att; - light_color *= attenuation; + float3 light_color = light.GetColor().rgb * shadow; + light_color *= attenuation_pointlight(dist, dist2, range, range2); lighting.direct.diffuse = mad(light_color, BRDF_GetDiffuse(surface, surface_to_light), lighting.direct.diffuse); lighting.direct.specular = mad(light_color, BRDF_GetSpecular(surface, surface_to_light), lighting.direct.specular); @@ -264,11 +273,21 @@ inline void light_point(in ShaderEntity light, in Surface surface, inout Lightin } } } + +inline float attenuation_spotlight(in float dist, in float dist2, in float range, in float range2, in float spot_factor, in float angle_scale, in float angle_offset) +{ + float attenuation = attenuation_pointlight(dist, dist2, range, range2); + float angularAttenuation = saturate(mad(spot_factor, angle_scale, angle_offset)); + angularAttenuation *= angularAttenuation; + attenuation *= angularAttenuation; + return attenuation; +} inline void light_spot(in ShaderEntity light, in Surface surface, inout Lighting lighting, in float shadow_mask = 1) { float3 L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -312,12 +331,8 @@ inline void light_spot(in ShaderEntity light, in Surface surface, inout Lighting [branch] if (any(shadow)) { - float3 light_color = light.GetColor().rgb * light.GetEnergy() * shadow; - - const float att = saturate(1 - (dist2 / range2)); - float attenuation = att * att; - attenuation *= saturate((1 - (1 - spot_factor) * 1 / (1 - spot_cutoff))); - light_color *= attenuation; + float3 light_color = light.GetColor().rgb * shadow; + light_color *= attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); lighting.direct.diffuse = mad(light_color, BRDF_GetDiffuse(surface, surface_to_light), lighting.direct.diffuse); lighting.direct.specular = mad(light_color, BRDF_GetSpecular(surface, surface_to_light), lighting.direct.specular); diff --git a/WickedEngine/shaders/objectHF.hlsli b/WickedEngine/shaders/objectHF.hlsli index f11795ee2..64c617e9e 100644 --- a/WickedEngine/shaders/objectHF.hlsli +++ b/WickedEngine/shaders/objectHF.hlsli @@ -427,8 +427,6 @@ inline void ForwardLighting(inout Surface surface, inout Lighting lighting) float edgeBlend = 1 - pow(saturate(abs(clipSpacePos.z)), 8); decalColor.a *= edgeBlend; decalColor *= decal.GetColor(); - // apply emissive: - lighting.direct.specular += max(0, decalColor.rgb * decal.GetEmissive() * edgeBlend); // perform manual blending of decals: // NOTE: they are sorted top-to-bottom, but blending is performed bottom-to-top decalAccumulation.rgb = mad(1 - decalAccumulation.a, decalColor.a * decalColor.rgb, decalAccumulation.rgb); @@ -652,8 +650,6 @@ inline void TiledDecals(inout Surface surface, uint flatTileIndex) float edgeBlend = 1 - pow(saturate(abs(clipSpacePos.z)), 8); decalColor.a *= edgeBlend; decalColor *= decal.GetColor(); - // apply emissive: - surface.emissiveColor += max(0, decalColor.rgb * decal.GetEmissive() * edgeBlend); // perform manual blending of decals: // NOTE: they are sorted top-to-bottom, but blending is performed bottom-to-top decalAccumulation.rgb = mad(1 - decalAccumulation.a, decalColor.a * decalColor.rgb, decalAccumulation.rgb); diff --git a/WickedEngine/shaders/objectPS_voxelizer.hlsl b/WickedEngine/shaders/objectPS_voxelizer.hlsl index 7b4ddac71..18e2a8c6d 100644 --- a/WickedEngine/shaders/objectPS_voxelizer.hlsl +++ b/WickedEngine/shaders/objectPS_voxelizer.hlsl @@ -93,7 +93,7 @@ void main(PSInput input) [branch] if (NdotL > 0) { - float3 lightColor = light.GetColor().rgb * light.GetEnergy() * NdotL; + float3 lightColor = light.GetColor().rgb * NdotL; [branch] if (light.IsCastingShadow() >= 0) @@ -116,7 +116,8 @@ void main(PSInput input) { float3 L = light.position - P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -129,10 +130,7 @@ void main(PSInput input) [branch] if (NdotL > 0) { - const float att = saturate(1.0 - (dist2 / range2)); - const float attenuation = att * att; - - float3 lightColor = light.GetColor().rgb * light.GetEnergy() * NdotL * attenuation; + float3 lightColor = light.GetColor().rgb * NdotL * attenuation_pointlight(dist, dist2, range, range2); [branch] if (light.IsCastingShadow() >= 0) { @@ -148,7 +146,8 @@ void main(PSInput input) { float3 L = light.position - P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -160,18 +159,13 @@ void main(PSInput input) [branch] if (NdotL > 0) { - const float SpotFactor = dot(L, light.GetDirection()); - const float spotCutOff = light.GetConeAngleCos(); + const float spot_factor = dot(L, light.GetDirection()); + const float spot_cutoff = light.GetConeAngleCos(); [branch] - if (SpotFactor > spotCutOff) + if (spot_factor > spot_cutoff) { - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - float attenuation = att * att; - attenuation *= saturate((1.0 - (1.0 - SpotFactor) * 1.0 / (1.0 - spotCutOff))); - - float3 lightColor = light.GetColor().rgb * light.GetEnergy() * NdotL * attenuation; + float3 lightColor = light.GetColor().rgb * NdotL * attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); [branch] if (light.IsCastingShadow() >= 0) diff --git a/WickedEngine/shaders/raytraceCS.hlsl b/WickedEngine/shaders/raytraceCS.hlsl index 0ff999042..883224149 100644 --- a/WickedEngine/shaders/raytraceCS.hlsl +++ b/WickedEngine/shaders/raytraceCS.hlsl @@ -217,7 +217,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) [branch] if (any(surfaceToLight.NdotL_sss)) { - lightColor = light.GetColor().rgb * light.GetEnergy(); + lightColor = light.GetColor().rgb; [branch] if (GetFrame().options & OPTION_BIT_REALISTIC_SKY) @@ -231,7 +231,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -244,12 +245,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) [branch] if (any(surfaceToLight.NdotL_sss)) { - lightColor = light.GetColor().rgb * light.GetEnergy(); - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1 - (dist2 / range2)); - const float attenuation = att * att; - lightColor *= attenuation; + lightColor = light.GetColor().rgb; + lightColor *= attenuation_pointlight(dist, dist2, range, range2); } } } @@ -258,7 +255,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -271,19 +269,14 @@ void main(uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) [branch] if (any(surfaceToLight.NdotL_sss)) { - const float SpotFactor = dot(L, light.GetDirection()); - const float spotCutOff = light.GetConeAngleCos(); + const float spot_factor = dot(L, light.GetDirection()); + const float spot_cutoff = light.GetConeAngleCos(); [branch] - if (SpotFactor > spotCutOff) + if (spot_factor > spot_cutoff) { - lightColor = light.GetColor().rgb * light.GetEnergy(); - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1 - (dist2 / range2)); - float attenuation = att * att; - attenuation *= saturate((1 - (1 - SpotFactor) * 1 / (1 - spotCutOff))); - lightColor *= attenuation; + lightColor = light.GetColor().rgb; + lightColor *= attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); } } } diff --git a/WickedEngine/shaders/renderlightmapPS.hlsl b/WickedEngine/shaders/renderlightmapPS.hlsl index a3f309559..050590738 100644 --- a/WickedEngine/shaders/renderlightmapPS.hlsl +++ b/WickedEngine/shaders/renderlightmapPS.hlsl @@ -77,7 +77,7 @@ float4 main(Input input) : SV_TARGET atmosphereTransmittance = GetAtmosphericLightTransmittance(GetWeather().atmosphere, surface.P, L, texture_transmittancelut); } - float3 lightColor = light.GetColor().rgb * light.GetEnergy() * atmosphereTransmittance; + float3 lightColor = light.GetColor().rgb * atmosphereTransmittance; lighting.direct.diffuse = lightColor; } @@ -87,7 +87,8 @@ float4 main(Input input) : SV_TARGET { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -99,15 +100,10 @@ float4 main(Input input) : SV_TARGET [branch] if (NdotL > 0) { - const float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + const float3 lightColor = light.GetColor().rgb; lighting.direct.diffuse = lightColor; - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - const float attenuation = att * att; - - lighting.direct.diffuse *= attenuation; + lighting.direct.diffuse *= attenuation_pointlight(dist, dist2, range, range2); } } } @@ -116,7 +112,8 @@ float4 main(Input input) : SV_TARGET { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -128,22 +125,16 @@ float4 main(Input input) : SV_TARGET [branch] if (NdotL > 0) { - const float SpotFactor = dot(L, light.GetDirection()); - const float spotCutOff = light.GetConeAngleCos(); + const float spot_factor = dot(L, light.GetDirection()); + const float spot_cutoff = light.GetConeAngleCos(); [branch] - if (SpotFactor > spotCutOff) + if (spot_factor > spot_cutoff) { - const float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + const float3 lightColor = light.GetColor().rgb; lighting.direct.diffuse = lightColor; - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - float attenuation = att * att; - attenuation *= saturate((1.0 - (1.0 - SpotFactor) * 1.0 / (1.0 - spotCutOff))); - - lighting.direct.diffuse *= attenuation; + lighting.direct.diffuse *= attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); } } } diff --git a/WickedEngine/shaders/screenspaceshadowCS.hlsl b/WickedEngine/shaders/screenspaceshadowCS.hlsl index e050fcf4d..9f015c459 100644 --- a/WickedEngine/shaders/screenspaceshadowCS.hlsl +++ b/WickedEngine/shaders/screenspaceshadowCS.hlsl @@ -145,7 +145,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint3 GTid : { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -181,16 +182,9 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint3 GTid : surfaceToLight.create(surface, L); [branch] - if (any(surfaceToLight.NdotL_sss)) + if (any(surfaceToLight.NdotL_sss) && (dot(L, light.GetDirection()) > light.GetConeAngleCos())) { - const float SpotFactor = dot(L, light.GetDirection()); - const float spotCutOff = light.GetConeAngleCos(); - - [branch] - if (SpotFactor > spotCutOff) - { - ray.TMax = dist; - } + ray.TMax = dist; } } } diff --git a/WickedEngine/shaders/surfel_raytraceCS.hlsl b/WickedEngine/shaders/surfel_raytraceCS.hlsl index 59f127133..894dc9c07 100644 --- a/WickedEngine/shaders/surfel_raytraceCS.hlsl +++ b/WickedEngine/shaders/surfel_raytraceCS.hlsl @@ -147,7 +147,7 @@ void main(uint3 DTid : SV_DispatchThreadID) [branch] if (NdotL > 0) { - float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + float3 lightColor = light.GetColor().rgb; [branch] if (GetFrame().options & OPTION_BIT_REALISTIC_SKY) @@ -163,7 +163,8 @@ void main(uint3 DTid : SV_DispatchThreadID) { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -175,15 +176,10 @@ void main(uint3 DTid : SV_DispatchThreadID) [branch] if (NdotL > 0) { - const float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + const float3 lightColor = light.GetColor().rgb; lighting.direct.diffuse = lightColor; - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - const float attenuation = att * att; - - lighting.direct.diffuse *= attenuation; + lighting.direct.diffuse *= attenuation_pointlight(dist, dist2, range, range2); } } } @@ -192,7 +188,8 @@ void main(uint3 DTid : SV_DispatchThreadID) { L = light.position - surface.P; const float dist2 = dot(L, L); - const float range2 = light.GetRange() * light.GetRange(); + const float range = light.GetRange(); + const float range2 = range * range; [branch] if (dist2 < range2) @@ -204,22 +201,16 @@ void main(uint3 DTid : SV_DispatchThreadID) [branch] if (NdotL > 0) { - const float SpotFactor = dot(L, light.GetDirection()); - const float spotCutOff = light.GetConeAngleCos(); + const float spot_factor = dot(L, light.GetDirection()); + const float spot_cutoff = light.GetConeAngleCos(); [branch] - if (SpotFactor > spotCutOff) + if (spot_factor > spot_cutoff) { - const float3 lightColor = light.GetColor().rgb * light.GetEnergy(); + const float3 lightColor = light.GetColor().rgb; lighting.direct.diffuse = lightColor; - - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - float attenuation = att * att; - attenuation *= saturate((1.0 - (1.0 - SpotFactor) * 1.0 / (1.0 - spotCutOff))); - - lighting.direct.diffuse *= attenuation; + lighting.direct.diffuse *= attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); } } } diff --git a/WickedEngine/shaders/vPointLightVS.hlsl b/WickedEngine/shaders/vPointLightVS.hlsl index ec2d08e06..ba4a9261c 100644 --- a/WickedEngine/shaders/vPointLightVS.hlsl +++ b/WickedEngine/shaders/vPointLightVS.hlsl @@ -1,17 +1,16 @@ #include "volumeLightHF.hlsli" #include "circle.hlsli" +#include "lightingHF.hlsli" VertexToPixel main(uint vID : SV_VertexID) { VertexToPixel Out = (VertexToPixel)0; float4 pos = CIRCLE[vID]; - pos = mul(lightWorld, pos); + Out.col = float4(xLightColor.rgb, 1) * saturate(1 - dot(pos.xyz, pos.xyz)); + + pos = mul(xLightWorld, pos); Out.pos = mul(GetCamera().view_projection, pos); - Out.col = lerp( - float4(lightColor.rgb, 1), float4(0, 0, 0, 0), - distance(pos.xyz, float3(lightWorld._14, lightWorld._24, lightWorld._34)) / (lightEnerdis.w) - ); return Out; -} \ No newline at end of file +} diff --git a/WickedEngine/shaders/vSpotLightVS.hlsl b/WickedEngine/shaders/vSpotLightVS.hlsl index 994ae0d35..df5120854 100644 --- a/WickedEngine/shaders/vSpotLightVS.hlsl +++ b/WickedEngine/shaders/vSpotLightVS.hlsl @@ -1,17 +1,16 @@ #include "volumeLightHF.hlsli" #include "cone.hlsli" +#include "lightingHF.hlsli" VertexToPixel main(uint vID : SV_VertexID) { VertexToPixel Out = (VertexToPixel)0; float4 pos = CONE[vID]; - pos = mul(lightWorld, pos); + Out.col = float4(xLightColor.rgb, 1) * saturate(1 - dot(pos.xyz, pos.xyz)); + + pos = mul(xLightWorld, pos); Out.pos = mul(GetCamera().view_projection, pos); - Out.col = lerp( - float4(lightColor.rgb, 1), float4(0, 0, 0, 0), - distance(pos.xyz, float3(lightWorld._14, lightWorld._24, lightWorld._34)) / (lightEnerdis.w) - ); return Out; -} \ No newline at end of file +} diff --git a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl index e734e889e..58a769ce3 100644 --- a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl @@ -75,5 +75,5 @@ float4 main(VertexToPixel input) : SV_TARGET atmosphere_transmittance = GetAtmosphericLightTransmittance(GetWeather().atmosphere, P, L, texture_transmittancelut); } - return max(0, float4(accumulation * light.GetColor().rgb * light.GetEnergy() * atmosphere_transmittance, 1)); + return max(0, float4(accumulation * light.GetColor().rgb * atmosphere_transmittance, 1)); } diff --git a/WickedEngine/shaders/volumetricLight_PointPS.hlsl b/WickedEngine/shaders/volumetricLight_PointPS.hlsl index ea07f219a..b85b58d9b 100644 --- a/WickedEngine/shaders/volumetricLight_PointPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_PointPS.hlsl @@ -40,9 +40,9 @@ float4 main(VertexToPixel input) : SV_TARGET const float dist = sqrt(dist2); L /= dist; - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - float3 attenuation = att * att; + const float range = light.GetRange(); + const float range2 = range * range; + float3 attenuation = attenuation_pointlight(dist, dist2, range, range2); [branch] if (light.IsCastingShadow()) @@ -61,5 +61,5 @@ float4 main(VertexToPixel input) : SV_TARGET accumulation /= sampleCount; - return max(0, float4(accumulation * light.GetColor().rgb * light.GetEnergy(), 1)); + return max(0, float4(accumulation * light.GetColor().rgb, 1)); } diff --git a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl index 4514a1543..6faf95af5 100644 --- a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl @@ -34,16 +34,15 @@ float4 main(VertexToPixel input) : SV_TARGET const float dist = sqrt(dist2); L /= dist; - float SpotFactor = dot(L, light.GetDirection()); - float spotCutOff = light.GetConeAngleCos(); + const float spot_factor = dot(L, light.GetDirection()); + const float spot_cutoff = light.GetConeAngleCos(); [branch] - if (SpotFactor > spotCutOff) + if (spot_factor > spot_cutoff) { - const float range2 = light.GetRange() * light.GetRange(); - const float att = saturate(1.0 - (dist2 / range2)); - float3 attenuation = att * att; - attenuation *= saturate((1.0 - (1.0 - SpotFactor) * 1.0 / (1.0 - spotCutOff))); + const float range = light.GetRange(); + const float range2 = range * range; + float3 attenuation = attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); [branch] if (light.IsCastingShadow()) @@ -70,5 +69,5 @@ float4 main(VertexToPixel input) : SV_TARGET accumulation /= sampleCount; - return max(0, float4(accumulation * light.GetColor().rgb * light.GetEnergy(), 1)); + return max(0, float4(accumulation * light.GetColor().rgb, 1)); } diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index f35050e13..9e2a7c683 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -5,7 +5,7 @@ namespace wi { // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! - static constexpr uint64_t __archiveVersion = 81; + static constexpr uint64_t __archiveVersion = 82; // this is the version number of which below the archive is not compatible with the current version static constexpr uint64_t __archiveVersionBarrier = 22; diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index bdae12fe6..1f201897b 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -3434,8 +3434,8 @@ void UpdateRenderData( shaderentity.SetType(ENTITY_TYPE_DECAL); shaderentity.position = decal.position; shaderentity.SetRange(decal.range); - shaderentity.color = wi::math::CompressColor(XMFLOAT4(decal.color.x, decal.color.y, decal.color.z, decal.GetOpacity())); - shaderentity.SetEnergy(decal.emissive); + float emissive_mul = 1 + decal.emissive; + shaderentity.SetColor(float4(decal.color.x * emissive_mul, decal.color.y * emissive_mul, decal.color.z * emissive_mul, decal.color.w)); shaderentity.SetIndices(matrixCounter, 0); shadermatrix = XMMatrixInverse(nullptr, XMLoadFloat4x4(&decal.world)); @@ -3530,9 +3530,11 @@ void UpdateRenderData( shaderentity.SetType(light.GetType()); shaderentity.position = light.position; - shaderentity.SetRange(light.GetRange()); - shaderentity.color = wi::math::CompressColor(light.color); - shaderentity.SetEnergy(light.energy); + float range = light.GetRange(); + range = std::max(0.001f, range); + range = std::min(range, 65504.0f); // clamp to 16-bit float max value + shaderentity.SetRange(range); + shaderentity.SetColor(float4(light.color.x* light.energy, light.color.y * light.energy, light.color.z * light.energy, 1)); // mark as no shadow by default: shaderentity.indices = ~0; @@ -3588,7 +3590,18 @@ void UpdateRenderData( break; case LightComponent::SPOT: { - shaderentity.SetConeAngleCos(cosf(light.fov * 0.5f)); + const float outerConeAngle = light.fov * 0.5f; + const float innerConeAngle = std::min(light.fov_inner * 0.5f, outerConeAngle); + const float outerConeAngleCos = std::cos(outerConeAngle); + const float innerConeAngleCos = std::cos(innerConeAngle); + + // https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_lights_punctual#inner-and-outer-cone-angles + const float lightAngleScale = 1.0f / std::max(0.001f, innerConeAngleCos - outerConeAngleCos); + const float lightAngleOffset = -outerConeAngleCos * lightAngleScale; + + shaderentity.SetConeAngleCos(outerConeAngleCos); + shaderentity.SetAngleScale(lightAngleScale); + shaderentity.SetAngleOffset(lightAngleOffset); shaderentity.SetDirection(light.direction); if (shadow) @@ -3634,9 +3647,8 @@ void UpdateRenderData( shaderentity.SetType(force.type); shaderentity.position = force.position; - shaderentity.SetEnergy(force.gravity); - shaderentity.SetRange(1.0f / std::max(0.0001f, force.GetRange())); // avoid division in shader - shaderentity.SetConeAngleCos(force.GetRange()); // this will be the real range in the less common shaders... + shaderentity.SetGravity(force.gravity); + shaderentity.SetRange(std::max(0.001f, force.GetRange())); // The default planar force field is facing upwards, and thus the pull direction is downwards: shaderentity.SetDirection(force.direction); @@ -4404,14 +4416,14 @@ void DrawLightVisualizers( { VolumeLightCB lcb; - lcb.lightColor = XMFLOAT4(light.color.x, light.color.y, light.color.z, 1); - lcb.lightEnerdis = XMFLOAT4(light.energy, light.GetRange(), light.fov, light.energy); + lcb.xLightColor = XMFLOAT4(light.color.x, light.color.y, light.color.z, 1); + lcb.xLightEnerdis = XMFLOAT4(light.energy, light.GetRange(), light.fov, light.energy); if (type == LightComponent::POINT) { - lcb.lightEnerdis.w = light.GetRange()*light.energy*0.01f; // scale - XMStoreFloat4x4(&lcb.lightWorld, - XMMatrixScaling(lcb.lightEnerdis.w, lcb.lightEnerdis.w, lcb.lightEnerdis.w)* + lcb.xLightEnerdis.w = light.GetRange() * 0.025f; // scale + XMStoreFloat4x4(&lcb.xLightWorld, + XMMatrixScaling(lcb.xLightEnerdis.w, lcb.xLightEnerdis.w, lcb.xLightEnerdis.w)* camrot* XMMatrixTranslationFromVector(XMLoadFloat3(&light.position)) ); @@ -4422,10 +4434,25 @@ void DrawLightVisualizers( } else if (type == LightComponent::SPOT) { + if (light.fov_inner > 0) + { + float coneS = (float)(std::min(light.fov_inner, light.fov) / 0.7853981852531433); + lcb.xLightEnerdis.w = std::min(light.GetRange(), 65504.0f) * 0.1f; // scale + XMStoreFloat4x4(&lcb.xLightWorld, + XMMatrixScaling(coneS * lcb.xLightEnerdis.w, lcb.xLightEnerdis.w, coneS * lcb.xLightEnerdis.w) * + XMMatrixRotationQuaternion(XMLoadFloat4(&light.rotation)) * + XMMatrixTranslationFromVector(XMLoadFloat3(&light.position)) + ); + + device->BindDynamicConstantBuffer(lcb, CB_GETBINDSLOT(VolumeLightCB), cmd); + + device->Draw(192, 0, cmd); // cone + } + float coneS = (float)(light.fov / 0.7853981852531433); - lcb.lightEnerdis.w = light.GetRange()*light.energy*0.03f; // scale - XMStoreFloat4x4(&lcb.lightWorld, - XMMatrixScaling(coneS*lcb.lightEnerdis.w, lcb.lightEnerdis.w, coneS*lcb.lightEnerdis.w)* + lcb.xLightEnerdis.w = std::min(light.GetRange(), 65504.0f) * 0.1f; // scale + XMStoreFloat4x4(&lcb.xLightWorld, + XMMatrixScaling(coneS*lcb.xLightEnerdis.w, lcb.xLightEnerdis.w, coneS*lcb.xLightEnerdis.w)* XMMatrixRotationQuaternion(XMLoadFloat4(&light.rotation))* XMMatrixTranslationFromVector(XMLoadFloat3(&light.position)) ); diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index 000ed838f..004cbf969 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -1421,21 +1421,17 @@ namespace wi::scene } void CameraComponent::TransformCamera(const TransformComponent& transform) { - XMVECTOR S, R, T; - XMMatrixDecompose(&S, &R, &T, XMLoadFloat4x4(&transform.world)); + XMMATRIX W = XMLoadFloat4x4(&transform.world); - XMVECTOR _Eye = T; - XMVECTOR _At = XMVectorSet(0, 0, 1, 0); - XMVECTOR _Up = XMVectorSet(0, 1, 0, 0); - - XMMATRIX _Rot = XMMatrixRotationQuaternion(R); - _At = XMVector3TransformNormal(_At, _Rot); - _Up = XMVector3TransformNormal(_Up, _Rot); - XMStoreFloat3x3(&rotationMatrix, _Rot); + XMVECTOR _Eye = XMVector3Transform(XMVectorSet(0, 0, 0, 1), W); + XMVECTOR _At = XMVector3Normalize(XMVector3TransformNormal(XMVectorSet(0, 0, 1, 0), W)); + XMVECTOR _Up = XMVector3Normalize(XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), W)); XMMATRIX _V = XMMatrixLookToLH(_Eye, _At, _Up); XMStoreFloat4x4(&View, _V); + XMStoreFloat3x3(&rotationMatrix, XMMatrixInverse(nullptr, _V)); + XMStoreFloat3(&Eye, _Eye); XMStoreFloat3(&At, _At); XMStoreFloat3(&Up, _Up); @@ -2229,7 +2225,9 @@ namespace wi::scene const XMFLOAT3& color, float energy, float range, - LightComponent::LightType type) + LightComponent::LightType type, + float fov, + float fov_inner) { Entity entity = CreateEntity(); @@ -2245,10 +2243,12 @@ namespace wi::scene LightComponent& light = lights.Create(entity); light.energy = energy; - light.range_local = range; + light.range = range; light.fov = XM_PIDIV4; light.color = color; light.SetType(type); + light.fov = fov; + light.fov_inner = fov_inner; return entity; } @@ -2269,7 +2269,7 @@ namespace wi::scene ForceFieldComponent& force = forces.Create(entity); force.gravity = 0; - force.range_local = 0; + force.range = 0; force.type = ENTITY_TYPE_FORCEFIELD_POINT; return entity; @@ -3940,7 +3940,6 @@ namespace wi::scene XMStoreFloat3(&force.position, T); XMStoreFloat3(&force.direction, XMVector3Normalize(XMVector3TransformNormal(XMVectorSet(0, -1, 0, 0), W))); - force.range_global = force.range_local * std::max(XMVectorGetX(S), std::max(XMVectorGetY(S), XMVectorGetZ(S))); }); } void Scene::RunLightUpdateSystem(wi::jobsystem::context& ctx) @@ -3975,8 +3974,6 @@ namespace wi::scene XMStoreFloat3(&light.scale, S); XMStoreFloat3(&light.direction, XMVector3Normalize(XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), W))); - light.range_global = light.range_local * std::max(XMVectorGetX(S), std::max(XMVectorGetY(S), XMVectorGetZ(S))); - switch (light.type) { default: diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index e52b20024..7e0f2e3a3 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -855,8 +855,9 @@ namespace wi::scene }; LightType type = POINT; float energy = 1.0f; - float range_local = 10.0f; + float range = 10.0f; float fov = XM_PIDIV4; + float fov_inner = 0; wi::vector lensFlareNames; @@ -864,7 +865,6 @@ namespace wi::scene // Non-serialized attributes: XMFLOAT3 position; - float range_global; XMFLOAT3 direction; XMFLOAT4 rotation; XMFLOAT3 scale; @@ -885,7 +885,7 @@ namespace wi::scene inline bool IsVisualizerEnabled() const { return _flags & VISUALIZER; } inline bool IsStatic() const { return _flags & LIGHTMAPONLY_STATIC; } - inline float GetRange() const { return range_global; } + inline float GetRange() const { return range; } inline void SetType(LightType val) { type = val; } inline LightType GetType() const { return type; } @@ -1003,14 +1003,13 @@ namespace wi::scene int type = ENTITY_TYPE_FORCEFIELD_POINT; float gravity = 0.0f; // negative = deflector, positive = attractor - float range_local = 0.0f; // affection range + float range = 0.0f; // affection range // Non-serialized attributes: XMFLOAT3 position; - float range_global; XMFLOAT3 direction; - inline float GetRange() const { return range_global; } + inline float GetRange() const { return range; } void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; @@ -1478,7 +1477,9 @@ namespace wi::scene const XMFLOAT3& color = XMFLOAT3(1, 1, 1), float energy = 1, float range = 10, - LightComponent::LightType type = LightComponent::POINT + LightComponent::LightType type = LightComponent::POINT, + float fov = XM_PIDIV4, + float fov_inner = 0 ); wi::ecs::Entity Entity_CreateForce( const std::string& name, diff --git a/WickedEngine/wiScene_BindLua.cpp b/WickedEngine/wiScene_BindLua.cpp index 1c396e388..2c4cfc808 100644 --- a/WickedEngine/wiScene_BindLua.cpp +++ b/WickedEngine/wiScene_BindLua.cpp @@ -2203,7 +2203,7 @@ int LightComponent_BindLua::SetRange(lua_State* L) if (argc > 0) { float value = wi::lua::SGetFloat(L, 1); - component->range_local = value; + component->range = value; } else { diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index f8147dcb6..764ab46cf 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -735,7 +735,7 @@ namespace wi::scene type = POINT; // fallback from old area light } archive >> energy; - archive >> range_local; + archive >> range; archive >> fov; if (archive.GetVersion() < 55) { @@ -756,6 +756,11 @@ namespace wi::scene archive >> forced_shadow_resolution; } + if (archive.GetVersion() >= 82) + { + archive >> fov_inner; + } + wi::jobsystem::Execute(seri.ctx, [&](wi::jobsystem::JobArgs args) { lensFlareRimTextures.resize(lensFlareNames.size()); for (size_t i = 0; i < lensFlareNames.size(); ++i) @@ -774,7 +779,7 @@ namespace wi::scene archive << color; archive << (uint32_t)type; archive << energy; - archive << range_local; + archive << range; archive << fov; if (archive.GetVersion() < 55) { @@ -802,6 +807,11 @@ namespace wi::scene { archive << forced_shadow_resolution; } + + if (archive.GetVersion() >= 82) + { + archive << fov_inner; + } } } void CameraComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) @@ -861,14 +871,14 @@ namespace wi::scene archive >> _flags; archive >> type; archive >> gravity; - archive >> range_local; + archive >> range; } else { archive << _flags; archive << type; archive << gravity; - archive << range_local; + archive << range; } } void DecalComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) diff --git a/credits.txt b/credits.txt index e7c473b30..c3b905072 100644 --- a/credits.txt +++ b/credits.txt @@ -41,6 +41,8 @@ Contributions: - QOI image loader integration - Eddie Ataberk https://github.com/eddieataberk - Physics improvements +- matpx https://github.com/matpx + - Fixes Support the project on Patreon: https://www.patreon.com/wickedengine diff --git a/features.txt b/features.txt index ec574ef20..2c4471490 100644 --- a/features.txt +++ b/features.txt @@ -31,7 +31,7 @@ GPU-based particles (emit from point, mesh, animated mesh) Soft particles Hair particle systems (grass/vegetation) Instanced rendering -MSAA (Forward rendering only) +MSAA FXAA TAA (Temporal Antialiasing) Supersampling @@ -75,7 +75,6 @@ Lightmap baking (with GPU path tracing) Job system Inverse Kinematics Springs -Terrain Rendering (material blending) Variable Rate Shading Real time ray tracing: ambient occlusion, shadows, reflections (DXR and Vulkan raytracing) Screen Space Contact Shadows @@ -94,3 +93,4 @@ KHR_materials_clearcoat KHR_materials_specular KHR_materials_ior KHR_texture_basisu +KHR_lights_punctual