From d1578b0179bdf69bd4ca1c9ddbda8a8d73b853ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Fri, 28 Mar 2025 08:58:56 +0100 Subject: [PATCH] added light color mask texture support --- Editor/LightWindow.cpp | 11 +++++++- Editor/LightWindow.h | 2 ++ WickedEngine/shaders/ShaderInterop_Renderer.h | 11 +++----- WickedEngine/shaders/globals.hlsli | 25 ++++++++++++++++++ WickedEngine/shaders/lightingHF.hlsli | 21 ++++++++++++++- .../shaders/volumetricLight_PointPS.hlsl | 9 +++++++ .../shaders/volumetricLight_SpotPS.hlsl | 12 +++++++++ WickedEngine/wiMath.h | 17 ++++++++++++ WickedEngine/wiRenderer.cpp | 26 +++++++++---------- WickedEngine/wiScene.cpp | 15 +++++++++++ WickedEngine/wiScene_Components.h | 1 + WickedEngine/wiVersion.cpp | 2 +- 12 files changed, 128 insertions(+), 24 deletions(-) diff --git a/Editor/LightWindow.cpp b/Editor/LightWindow.cpp index d8705627b..9911fa034 100644 --- a/Editor/LightWindow.cpp +++ b/Editor/LightWindow.cpp @@ -319,7 +319,12 @@ void LightWindow::Create(EditorComponent* _editor) shadowResolutionComboBox.SetSelected(0); AddWidget(&shadowResolutionComboBox); - y += step * 0.5f; + + tipLabel.Create("TipLabel"); + tipLabel.SetText("Tip: you can add a material to this entity, and the base color texture of it will be used to tint the light color."); + tipLabel.SetPos(XMFLOAT2(mod_x, y += step)); + tipLabel.SetSize(XMFLOAT2(100, 50)); + AddWidget(&tipLabel); lensflare_Label.Create("Lens flare textures: "); lensflare_Label.SetPos(XMFLOAT2(mod_x, y += step)); @@ -590,6 +595,10 @@ void LightWindow::ResizeLayout() y += jump; + add_fullwidth(tipLabel); + + y += jump; + add_fullwidth(lensflare_Label); add_fullwidth(lensflare_Button[0]); add_fullwidth(lensflare_Button[1]); diff --git a/Editor/LightWindow.h b/Editor/LightWindow.h index 9d5e0db05..c461b7f2d 100644 --- a/Editor/LightWindow.h +++ b/Editor/LightWindow.h @@ -28,6 +28,8 @@ public: wi::gui::ComboBox typeSelectorComboBox; wi::gui::ComboBox shadowResolutionComboBox; + wi::gui::Label tipLabel; + wi::gui::Label lensflare_Label; wi::gui::Button lensflare_Button[7]; diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index af9cd0ec0..c27ae4ff3 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -791,9 +791,6 @@ enum SHADER_ENTITY_FLAGS ENTITY_FLAG_CAPSULE_SHADOW_COLLIDER = 1 << 4, }; -// 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! struct alignas(16) ShaderEntity { float3 position; @@ -874,11 +871,11 @@ struct alignas(16) ShaderEntity } inline min16uint GetMatrixIndex() { - return indices & 0xFFFF; + return indices & 0xFFF; } inline min16uint GetTextureIndex() { - return indices >> 16u; + return indices >> 12u; } inline bool IsCastingShadow() { @@ -958,8 +955,8 @@ struct alignas(16) ShaderEntity } inline void SetIndices(uint matrixIndex, uint textureIndex) { - indices = matrixIndex & 0xFFFF; - indices |= (textureIndex & 0xFFFF) << 16u; + indices = matrixIndex & 0xFFF; + indices |= (textureIndex & 0xFFFFF) << 12u; } inline void SetGravity(float value) { diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli index c8c27b511..01c666cbd 100644 --- a/WickedEngine/shaders/globals.hlsli +++ b/WickedEngine/shaders/globals.hlsli @@ -156,6 +156,31 @@ inline uint2 unpack_pixel(uint value) return retVal; } +uint pack_unorm16x2(float2 value) +{ + return uint(saturate(value.x) * 65535.0) | (uint(saturate(value.x) * 65535.0) << 16u); +} +uint2 pack_unorm16x4(float4 value) +{ + return uint2(pack_unorm16x2(value.xy), pack_unorm16x2(value.zw)); +} +float2 unpack_unorm16x2(uint value) +{ + float2 retVal; + retVal.x = (value & 0xFFFF) / 65535.0; + retVal.y = (value >> 16u) / 65535.0; + return retVal; +} +float4 unpack_unorm16x4(uint2 value) +{ + float4 retVal; + retVal.x = (value.x & 0xFFFF) / 65535.0; + retVal.y = (value.x >> 16u) / 65535.0; + retVal.z = (value.y & 0xFFFF) / 65535.0; + retVal.w = (value.y >> 16u) / 65535.0; + return retVal; +} + template T inverse_lerp(T value1, T value2, T pos) { diff --git a/WickedEngine/shaders/lightingHF.hlsli b/WickedEngine/shaders/lightingHF.hlsli index 7370c42a8..ff0be2db5 100644 --- a/WickedEngine/shaders/lightingHF.hlsli +++ b/WickedEngine/shaders/lightingHF.hlsli @@ -210,6 +210,14 @@ inline void light_point(in ShaderEntity light, in Surface surface, inout Lightin if (!any(light_color)) return; // light color lost after shadow } + + const uint maskTex = light.GetTextureIndex(); + [branch] + if (maskTex > 0) + { + half4 mask = bindless_cubemaps_half4[descriptor_index(maskTex)].SampleLevel(sampler_linear_clamp, -LunnormalizedShadow, 0); + light_color *= mask.rgb * mask.a; + } light_color *= attenuation_pointlight(dist2, range, range2); @@ -295,7 +303,7 @@ inline void light_spot(in ShaderEntity light, in Surface surface, inout Lighting return; // outside spotlight cone half3 light_color = light.GetColor().rgb * shadow_mask; - + [branch] if (light.IsCastingShadow() && surface.IsReceiveShadow()) { @@ -317,6 +325,17 @@ inline void light_spot(in ShaderEntity light, in Surface surface, inout Lighting if (!any(light_color)) return; // light color lost after shadow } + + const uint maskTex = light.GetTextureIndex(); + [branch] + if (maskTex > 0) + { + float4 shadow_pos = mul(load_entitymatrix(light.GetMatrixIndex() + 0), float4(surface.P, 1)); + shadow_pos.xyz /= shadow_pos.w; + float2 shadow_uv = clipspace_to_uv(shadow_pos.xy); + half4 mask = bindless_textures_half4[descriptor_index(maskTex)].SampleLevel(sampler_linear_clamp, shadow_uv, 0); + light_color *= mask.rgb * mask.a; + } light_color *= attenuation_spotlight(dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); diff --git a/WickedEngine/shaders/volumetricLight_PointPS.hlsl b/WickedEngine/shaders/volumetricLight_PointPS.hlsl index 394e12e61..f57fb0415 100644 --- a/WickedEngine/shaders/volumetricLight_PointPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_PointPS.hlsl @@ -46,6 +46,8 @@ float4 main(VertexToPixel input) : SV_TARGET // dither ray start to help with undersampling: P = P + V * stepSize * dither(input.pos.xy); + + const uint maskTex = light.GetTextureIndex(); // Perform ray marching to integrate light volume along view ray: [loop] @@ -66,6 +68,13 @@ float4 main(VertexToPixel input) : SV_TARGET { attenuation *= shadow_cube(light, Lunnormalized, input.pos.xy); } + + [branch] + if (maskTex > 0) + { + half4 mask = bindless_cubemaps_half4[descriptor_index(maskTex)].Sample(sampler_linear_clamp, -Lunnormalized); + attenuation *= mask.rgb * mask.a; + } // Evaluate sample height for height fog calculation, given 0 for V: attenuation *= (half)g_xColor.y + GetFogAmount(cameraDistance - marchedDistance, P, 0); diff --git a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl index 4975e7b2c..2dd665d60 100644 --- a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl @@ -87,6 +87,8 @@ float4 main(VertexToPixel input) : SV_TARGET // dither ray start to help with undersampling: P = P + V * stepSize * dither(input.pos.xy); + + const uint maskTex = light.GetTextureIndex(); // Perform ray marching to integrate light volume along view ray: [loop] @@ -119,6 +121,16 @@ float4 main(VertexToPixel input) : SV_TARGET attenuation *= shadow_2D(light, shadow_pos.xyz, shadow_uv.xy, 0, input.pos.xy); } } + + [branch] + if (maskTex > 0) + { + float4 shadow_pos = mul(load_entitymatrix(light.GetMatrixIndex() + 0), float4(P, 1)); + shadow_pos.xyz /= shadow_pos.w; + float2 shadow_uv = shadow_pos.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + half4 mask = bindless_textures_half4[descriptor_index(maskTex)].Sample(sampler_linear_clamp, shadow_uv); + attenuation *= mask.rgb * mask.a; + } // Evaluate sample height for exponential fog calculation, given 0 for V: attenuation *= g_xColor.y + GetFogAmount(cameraDistance - marchedDistance, P, 0); diff --git a/WickedEngine/wiMath.h b/WickedEngine/wiMath.h index 4521be064..e2acb5032 100644 --- a/WickedEngine/wiMath.h +++ b/WickedEngine/wiMath.h @@ -527,6 +527,23 @@ namespace wi::math } + constexpr uint32_t pack_unorm16x2(float x, float y) + { + return uint32_t(saturate(x) * 65535.0f) | (uint32_t(saturate(y) * 65535.0f) << 16u); + } + constexpr uint32_t pack_unorm16x2(XMFLOAT2 value) + { + return pack_unorm16x2(value.x, value.y); + } + constexpr XMUINT2 pack_unorm16x4(float x, float y, float z, float w) + { + return XMUINT2(pack_unorm16x2(x, y), pack_unorm16x2(z, w)); + } + constexpr XMUINT2 pack_unorm16x4(XMFLOAT4 value) + { + return pack_unorm16x4(value.x, value.y, value.z, value.w); + } + //----------------------------------------------------------------------------- // Compute the intersection of a ray (Origin, Direction) with a triangle diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 911773043..df03e69ab 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -4412,9 +4412,6 @@ void UpdatePerFrameData( shaderentity.SetDirection(light.direction); shaderentity.SetColor(float4(light.color.x * light.intensity, light.color.y * light.intensity, light.color.z * light.intensity, 1)); - // mark as no shadow by default: - shaderentity.indices = ~0; - const bool shadowmap = IsShadowsEnabled() && light.IsCastingShadow() && !light.IsStatic(); const wi::rectpacker::Rect& shadow_rect = vis.visibleLightShadowRects[lightIndex]; @@ -4424,9 +4421,10 @@ void UpdatePerFrameData( shaderentity.shadowAtlasMulAdd.y = shadow_rect.h * atlas_dim_rcp.y; shaderentity.shadowAtlasMulAdd.z = shadow_rect.x * atlas_dim_rcp.x; shaderentity.shadowAtlasMulAdd.w = shadow_rect.y * atlas_dim_rcp.y; - shaderentity.SetIndices(matrixCounter, 0); } + shaderentity.SetIndices(matrixCounter, 0); + const uint cascade_count = std::min((uint)light.cascade_distances.size(), MATRIXARRAY_COUNT - matrixCounter); shaderentity.SetShadowCascadeCount(cascade_count); @@ -4491,21 +4489,21 @@ void UpdatePerFrameData( shaderentity.SetDirection(light.direction); shaderentity.SetColor(float4(light.color.x * light.intensity, light.color.y * light.intensity, light.color.z * light.intensity, 1)); - // mark as no shadow by default: - shaderentity.indices = ~0; - const bool shadowmap = IsShadowsEnabled() && light.IsCastingShadow() && !light.IsStatic(); const wi::rectpacker::Rect& shadow_rect = vis.visibleLightShadowRects[lightIndex]; + const uint maskTex = light.maskTexDescriptor < 0 ? 0 : light.maskTexDescriptor; + if (shadowmap) { shaderentity.shadowAtlasMulAdd.x = shadow_rect.w * atlas_dim_rcp.x; shaderentity.shadowAtlasMulAdd.y = shadow_rect.h * atlas_dim_rcp.y; shaderentity.shadowAtlasMulAdd.z = shadow_rect.x * atlas_dim_rcp.x; shaderentity.shadowAtlasMulAdd.w = shadow_rect.y * atlas_dim_rcp.y; - shaderentity.SetIndices(matrixCounter, 0); } + shaderentity.SetIndices(matrixCounter, maskTex); + const float outerConeAngle = light.outerConeAngle; const float innerConeAngle = std::min(light.innerConeAngle, outerConeAngle); const float outerConeAngleCos = std::cos(outerConeAngle); @@ -4519,7 +4517,7 @@ void UpdatePerFrameData( shaderentity.SetAngleScale(lightAngleScale); shaderentity.SetAngleOffset(lightAngleOffset); - if (shadowmap) + if (shadowmap || (maskTex > 0)) { SHCAM shcam; CreateSpotLightShadowCam(light, shcam); @@ -4577,21 +4575,21 @@ void UpdatePerFrameData( shaderentity.SetDirection(light.direction); shaderentity.SetColor(float4(light.color.x * light.intensity, light.color.y * light.intensity, light.color.z * light.intensity, 1)); - // mark as no shadow by default: - shaderentity.indices = ~0; - const bool shadowmap = IsShadowsEnabled() && light.IsCastingShadow() && !light.IsStatic(); const wi::rectpacker::Rect& shadow_rect = vis.visibleLightShadowRects[lightIndex]; + const uint maskTex = light.maskTexDescriptor < 0 ? 0 : light.maskTexDescriptor; + if (shadowmap) { shaderentity.shadowAtlasMulAdd.x = shadow_rect.w * atlas_dim_rcp.x; shaderentity.shadowAtlasMulAdd.y = shadow_rect.h * atlas_dim_rcp.y; shaderentity.shadowAtlasMulAdd.z = shadow_rect.x * atlas_dim_rcp.x; shaderentity.shadowAtlasMulAdd.w = shadow_rect.y * atlas_dim_rcp.y; - shaderentity.SetIndices(matrixCounter, 0); } + shaderentity.SetIndices(matrixCounter, maskTex); + if (shadowmap) { const float FarZ = 0.1f; // watch out: reversed depth buffer! Also, light near plane is constant for simplicity, this should match on cpu side! @@ -4660,7 +4658,7 @@ void UpdatePerFrameData( shaderentity.SetType(ENTITY_TYPE_COLLIDER_PLANE); shaderentity.position = collider.plane.origin; shaderentity.SetDirection(collider.plane.normal); - shaderentity.SetIndices(matrixCounter, ~0u); + shaderentity.SetIndices(matrixCounter, 0); matrixArray[matrixCounter++] = collider.plane.projection; break; default: diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index 8fbb3f492..721d479ab 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -4836,6 +4836,21 @@ namespace wi::scene break; } + light.maskTexDescriptor = -1; + + const MaterialComponent* material = materials.GetComponent(entity); + if (material != nullptr && material->textures[MaterialComponent::BASECOLORMAP].resource.IsValid()) + { + const Texture& tex = material->textures[MaterialComponent::BASECOLORMAP].resource.GetTexture(); + if ( + (light.type == LightComponent::SPOT && !has_flag(tex.desc.misc_flags, ResourceMiscFlag::TEXTURECUBE)) || + (light.type == LightComponent::POINT && has_flag(tex.desc.misc_flags, ResourceMiscFlag::TEXTURECUBE)) + ) + { + light.maskTexDescriptor = GetDevice()->GetDescriptorIndex(&tex, SubresourceType::SRV, material->textures[MaterialComponent::BASECOLORMAP].resource.GetTextureSRGBSubresource()); + } + } + }); } void Scene::RunParticleUpdateSystem(wi::jobsystem::context& ctx) diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index 21e5bf1d4..7f67d7169 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -1220,6 +1220,7 @@ namespace wi::scene mutable int occlusionquery = -1; XMFLOAT4 rotation = XMFLOAT4(0, 0, 0, 1); XMFLOAT3 scale = XMFLOAT3(1, 1, 1); + int maskTexDescriptor = -1; wi::vector lensFlareRimTextures; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 387ed7638..b4155eb4c 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -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 = 724; + const int revision = 725; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);