diff --git a/src/common/rendering/hwrenderer/data/hw_dynlightdata.h b/src/common/rendering/hwrenderer/data/hw_dynlightdata.h index 41c0c7284..e48aaf767 100644 --- a/src/common/rendering/hwrenderer/data/hw_dynlightdata.h +++ b/src/common/rendering/hwrenderer/data/hw_dynlightdata.h @@ -25,15 +25,55 @@ #include "tarray.h" +enum FDynLightInfoFlags +{ + LIGHTINFO_ATTENUATED = 1, + LIGHTINFO_SHADOWMAPPED = 2, + LIGHTINFO_SPOT = 4, +}; + +struct FDynLightInfo +{ + float x; + float y; + float z; + float padding0; // 4 + float r; + float g; + float b; + float padding1; // 8 + float spotDirX; + float spotDirY; + float spotDirZ; + float padding2; // 12 + float radius; + float linearity; + float softShadowRadius; + float strength; // 16 + float spotInnerAngle; + float spotOuterAngle; + int shadowIndex; + int flags; // 20 +}; + +enum FDynLightDataArrays +{ + LIGHTARRAY_NORMAL, + LIGHTARRAY_SUBTRACTIVE, + LIGHTARRAY_ADDITIVE, +}; + +#define MAX_LIGHT_DATA 65536 + struct FDynLightData { - TArray arrays[3]; + TArray arrays[3]; void Clear() { - arrays[0].Clear(); - arrays[1].Clear(); - arrays[2].Clear(); + arrays[LIGHTARRAY_NORMAL].Clear(); + arrays[LIGHTARRAY_SUBTRACTIVE].Clear(); + arrays[LIGHTARRAY_ADDITIVE].Clear(); } }; diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp index 1a0376ff0..1f2fc6b7a 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp @@ -1,6 +1,7 @@ #include "hw_levelmesh.h" #include "halffloat.h" +#include "hw_dynlightdata.h" LevelMesh::LevelMesh() { @@ -35,11 +36,10 @@ void LevelMesh::Reset(const LevelMeshLimits& limits) Mesh.Materials.Resize(limits.MaxUniforms); int maxLights = 20'000; - int maxDynlights = 50'000; Mesh.Lights.Resize(maxLights); Mesh.LightIndexes.Resize(limits.MaxSurfaces * 10); - Mesh.DynLights.Resize(maxDynlights * 4); + Mesh.DynLights.Resize((sizeof(int) * 4) + MAX_LIGHT_DATA * sizeof(FDynLightInfo)); Mesh.Indexes.Resize(limits.MaxIndexes); Mesh.SurfaceIndexes.Resize(limits.MaxIndexes / 3 + 1); diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h index b66e0b544..bb82e869d 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.h +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -173,7 +173,7 @@ public: // Lights TArray Lights; - TArray DynLights; + TArray DynLights; // Index data TArray Indexes; diff --git a/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp b/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp index 08a3854f4..9f741d821 100644 --- a/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp +++ b/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp @@ -73,16 +73,16 @@ VkRSBuffers::VkRSBuffers(VulkanRenderDevice* fb) Viewpoint.Data = Viewpoint.UBO->Map(0, Viewpoint.UBO->size); - Lightbuffer.UBO = BufferBuilder() - .Usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) + Lightbuffer.SSO = BufferBuilder() + .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) .MemoryType( VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) - .Size(Lightbuffer.Count * 4 * sizeof(FVector4)) - .DebugName("Lightbuffer.UBO") + .Size((Lightbuffer.Count * 4 * sizeof(int)) + Lightbuffer.Count * sizeof(FDynLightInfo)) + .DebugName("Lightbuffer.SSO") .Create(fb->GetDevice()); - Lightbuffer.Data = Lightbuffer.UBO->Map(0, Lightbuffer.UBO->size); + Lightbuffer.Data = Lightbuffer.SSO->Map(0, Lightbuffer.SSO->size); Bonebuffer.SSO = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) @@ -122,9 +122,9 @@ VkRSBuffers::~VkRSBuffers() Viewpoint.UBO->Unmap(); Viewpoint.UBO.reset(); - if (Lightbuffer.UBO) - Lightbuffer.UBO->Unmap(); - Lightbuffer.UBO.reset(); + if (Lightbuffer.SSO) + Lightbuffer.SSO->Unmap(); + Lightbuffer.SSO.reset(); if (Bonebuffer.SSO) Bonebuffer.SSO->Unmap(); diff --git a/src/common/rendering/vulkan/buffers/vk_rsbuffers.h b/src/common/rendering/vulkan/buffers/vk_rsbuffers.h index 396e958e1..aa24f5745 100644 --- a/src/common/rendering/vulkan/buffers/vk_rsbuffers.h +++ b/src/common/rendering/vulkan/buffers/vk_rsbuffers.h @@ -38,8 +38,9 @@ public: struct { int UploadIndex = 0; - int Count = 80000; - std::unique_ptr UBO; + int DataIndex = 0; + int Count = MAX_LIGHT_DATA; + std::unique_ptr SSO; void* Data = nullptr; } Lightbuffer; diff --git a/src/common/rendering/vulkan/descriptorsets/vk_descriptorset.cpp b/src/common/rendering/vulkan/descriptorsets/vk_descriptorset.cpp index 2c4229c4d..1da064fb6 100644 --- a/src/common/rendering/vulkan/descriptorsets/vk_descriptorset.cpp +++ b/src/common/rendering/vulkan/descriptorsets/vk_descriptorset.cpp @@ -73,7 +73,7 @@ void VkDescriptorSetManager::Init() .AddBuffer(RSBuffer.Set.get(), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, rsbuffers->Viewpoint.UBO.get(), 0, sizeof(HWViewpointUniforms)) .AddBuffer(RSBuffer.Set.get(), 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, rsbuffers->MatrixBuffer->UBO(), 0, sizeof(MatricesUBO)) .AddBuffer(RSBuffer.Set.get(), 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, rsbuffers->SurfaceUniformsBuffer->UBO(), 0, sizeof(SurfaceUniformsUBO)) - .AddBuffer(RSBuffer.Set.get(), 3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, rsbuffers->Lightbuffer.UBO.get(), 0, sizeof(LightBufferUBO)) + .AddBuffer(RSBuffer.Set.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, rsbuffers->Lightbuffer.SSO.get(), 0, sizeof(LightBufferSSO)) .AddBuffer(RSBuffer.Set.get(), 4, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, rsbuffers->Fogballbuffer.UBO.get(), 0, sizeof(FogballBufferUBO)) .AddBuffer(RSBuffer.Set.get(), 5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, rsbuffers->Bonebuffer.SSO.get()) .Execute(fb->GetDevice()); @@ -263,7 +263,7 @@ void VkDescriptorSetManager::CreateRSBufferLayout() .AddBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) - .AddBinding(3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(4, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT) .DebugName("VkDescriptorSetManager.RSBuffer.Layout") diff --git a/src/common/rendering/vulkan/shaders/vk_shader.cpp b/src/common/rendering/vulkan/shaders/vk_shader.cpp index 96e5569c1..64195d7e8 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -290,9 +290,6 @@ void VkShaderManager::BuildLayoutBlock(FString &layoutBlock, bool isFrag, const } } layoutBlock << "};\n"; - layoutBlock << "#line 1\n"; - - layoutBlock << LoadPrivateShaderLump("shaders/scene/layout_shared.glsl").GetChars() << "\n"; if(!isFrag) { @@ -317,6 +314,10 @@ void VkShaderManager::BuildLayoutBlock(FString &layoutBlock, bool isFrag, const AddBuiltinFields(layoutBlock, index, false, fragShaderOutputs, key, hasClipDistance); } + layoutBlock << "#line 1\n"; + + layoutBlock << LoadPrivateShaderLump("shaders/scene/layout_shared.glsl").GetChars() << "\n"; + } void VkShaderManager::BuildDefinesBlock(FString &definesBlock, const char *defines, bool isFrag, const VkShaderKey& key, const UserShaderDesc *shader) diff --git a/src/common/rendering/vulkan/shaders/vk_shader.h b/src/common/rendering/vulkan/shaders/vk_shader.h index 2caf5c762..7f21801f8 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.h +++ b/src/common/rendering/vulkan/shaders/vk_shader.h @@ -10,6 +10,8 @@ #include #include +#include "hw_dynlightdata.h" + #include "hwrenderer/postprocessing/hw_useruniforms.h" class ShaderIncludeResult; @@ -33,11 +35,11 @@ struct SurfaceUniformsUBO SurfaceUniforms data[MAX_SURFACE_UNIFORMS]; }; -#define MAX_LIGHT_DATA ((int)(65536 / sizeof(FVector4))) - -struct LightBufferUBO +struct LightBufferSSO { - FVector4 lights[MAX_LIGHT_DATA]; + //TODO deduplicate individual lights + int lightIndex[MAX_LIGHT_DATA * 4]; + FDynLightInfo lights[MAX_LIGHT_DATA]; }; #define MAX_FOGBALL_DATA ((int)(65536 / sizeof(Fogball))) diff --git a/src/common/rendering/vulkan/textures/vk_renderbuffers.cpp b/src/common/rendering/vulkan/textures/vk_renderbuffers.cpp index 57dab614a..e9315382d 100644 --- a/src/common/rendering/vulkan/textures/vk_renderbuffers.cpp +++ b/src/common/rendering/vulkan/textures/vk_renderbuffers.cpp @@ -330,7 +330,7 @@ void VkRenderBuffers::CreateSceneLightTiles(int width, int height) height = (height + 63) / 64; // Make room for 16 lights plus the lightdata header - size_t blockSize = (1 + 4 * 16) * sizeof(FVector4); + size_t blockSize = (4 * sizeof(int)) + 16 * sizeof(FDynLightInfo); SceneLightTiles = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) diff --git a/src/common/rendering/vulkan/vk_levelmesh.cpp b/src/common/rendering/vulkan/vk_levelmesh.cpp index 5409d717c..e5922c7fb 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.cpp +++ b/src/common/rendering/vulkan/vk_levelmesh.cpp @@ -317,7 +317,7 @@ void VkLevelMesh::CreateBuffers() DynLightBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) - .Size(Mesh->Mesh.DynLights.Size() * sizeof(FVector4)) + .Size(Mesh->Mesh.DynLights.Size()) .DebugName("DynLightBuffer") .Create(fb->GetDevice()); } @@ -976,7 +976,7 @@ size_t VkLevelMeshUploader::GetTransferSize() for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Portals.GetRanges()) transferBufferSize += range.Count() * sizeof(PortalInfo); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.LightIndex.GetRanges()) transferBufferSize += range.Count() * sizeof(int32_t); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Light.GetRanges()) transferBufferSize += range.Count() * sizeof(LightInfo); - for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.DynLight.GetRanges()) transferBufferSize += range.Count() * sizeof(FVector4); + for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.DynLight.GetRanges()) transferBufferSize += range.Count(); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.DrawIndex.GetRanges()) transferBufferSize += range.Count() * sizeof(uint32_t); return transferBufferSize; } diff --git a/src/common/rendering/vulkan/vk_renderstate.cpp b/src/common/rendering/vulkan/vk_renderstate.cpp index c65f959f4..f51ea542a 100644 --- a/src/common/rendering/vulkan/vk_renderstate.cpp +++ b/src/common/rendering/vulkan/vk_renderstate.cpp @@ -537,7 +537,7 @@ void VkRenderState::ApplyBufferSets() { uint32_t matrixOffset = mRSBuffers->MatrixBuffer->Offset(); uint32_t surfaceUniformsOffset = mRSBuffers->SurfaceUniformsBuffer->Offset(); - uint32_t lightsOffset = mLightIndex >= 0 ? (uint32_t)(mLightIndex / MAX_LIGHT_DATA) * sizeof(LightBufferUBO) : mLastLightsOffset; + uint32_t lightsOffset = mLightIndex >= 0 ? (uint32_t)(mLightIndex / MAX_LIGHT_DATA) * sizeof(LightBufferSSO) : mLastLightsOffset; uint32_t fogballsOffset = mFogballIndex >= 0 ? (uint32_t)(mFogballIndex / MAX_FOGBALL_DATA) * sizeof(FogballBufferUBO) : mLastFogballsOffset; if (mViewpointOffset != mLastViewpointOffset || matrixOffset != mLastMatricesOffset || surfaceUniformsOffset != mLastSurfaceUniformsOffset || lightsOffset != mLastLightsOffset || fogballsOffset != mLastFogballsOffset) { @@ -603,51 +603,31 @@ void VkRenderState::SetTextureMatrix(const VSMatrix& matrix) int VkRenderState::UploadLights(const FDynLightData& data) { // All meaasurements here are in vec4's. - int size0 = data.arrays[0].Size() / 4; - int size1 = data.arrays[1].Size() / 4; - int size2 = data.arrays[2].Size() / 4; - int totalsize = size0 + size1 + size2 + 1; + int size0 = data.arrays[LIGHTARRAY_NORMAL].Size(); + int size1 = data.arrays[LIGHTARRAY_SUBTRACTIVE].Size(); + int size2 = data.arrays[LIGHTARRAY_ADDITIVE].Size(); + int totalsize = size0 + size1 + size2; - // Clamp lights so they aren't bigger than what fits into a single dynamic uniform buffer page - if (totalsize > MAX_LIGHT_DATA) + int indexindex = mRSBuffers->Lightbuffer.UploadIndex; + int dataindex = mRSBuffers->Lightbuffer.DataIndex; + + if((indexindex <= mRSBuffers->Lightbuffer.Count) && (dataindex + totalsize <= mRSBuffers->Lightbuffer.Count)) { - int diff = totalsize - MAX_LIGHT_DATA; + mRSBuffers->Lightbuffer.UploadIndex++; - size2 -= diff; - if (size2 < 0) - { - size1 += size2; - size2 = 0; - } - if (size1 < 0) - { - size0 += size1; - size1 = 0; - } - totalsize = size0 + size1 + size2 + 1; - } + mRSBuffers->Lightbuffer.DataIndex += totalsize; - // Check if we still have any lights - if (totalsize <= 1) - return -1; + int parmcnt[] = { dataindex, dataindex + size0, dataindex + size0 + size1, dataindex + size0 + size1 + size2 }; - // Make sure the light list doesn't cross a page boundary - if (mRSBuffers->Lightbuffer.UploadIndex % MAX_LIGHT_DATA + totalsize > MAX_LIGHT_DATA) - mRSBuffers->Lightbuffer.UploadIndex = (mRSBuffers->Lightbuffer.UploadIndex / MAX_LIGHT_DATA + 1) * MAX_LIGHT_DATA; + int* indexptr = ((int*)mRSBuffers->Lightbuffer.Data) + (indexindex * 4); + memcpy(indexptr, parmcnt, sizeof(int) * 4); - int thisindex = mRSBuffers->Lightbuffer.UploadIndex; - if (thisindex + totalsize <= mRSBuffers->Lightbuffer.Count) - { - mRSBuffers->Lightbuffer.UploadIndex += totalsize; + FDynLightInfo* dataptr = ((FDynLightInfo*)(((int*)mRSBuffers->Lightbuffer.Data) + (mRSBuffers->Lightbuffer.Count * 4))) + dataindex; + memcpy(dataptr, &data.arrays[0][0], size0 * sizeof(FDynLightInfo)); + memcpy(dataptr + size0, &data.arrays[1][0], size1 * sizeof(FDynLightInfo)); + memcpy(dataptr + (size0 + size1), &data.arrays[2][0], size2 * sizeof(FDynLightInfo)); - float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) }; - - float* copyptr = (float*)mRSBuffers->Lightbuffer.Data + thisindex * 4; - memcpy(©ptr[0], parmcnt, sizeof(FVector4)); - memcpy(©ptr[4], &data.arrays[0][0], size0 * sizeof(FVector4)); - memcpy(©ptr[4 + 4 * size0], &data.arrays[1][0], size1 * sizeof(FVector4)); - memcpy(©ptr[4 + 4 * (size0 + size1)], &data.arrays[2][0], size2 * sizeof(FVector4)); - return thisindex; + return indexindex; } else { @@ -774,6 +754,7 @@ void VkRenderState::BeginFrame() mRSBuffers->Viewpoint.UploadIndex = 0; mRSBuffers->Lightbuffer.UploadIndex = 0; + mRSBuffers->Lightbuffer.DataIndex = 0; mRSBuffers->Bonebuffer.UploadIndex = 0; mRSBuffers->Fogballbuffer.UploadIndex = 0; mRSBuffers->OcclusionQuery.NextIndex = 0; diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index 85923c88a..694ef3976 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -356,41 +356,25 @@ void DoomLevelMesh::UploadDynLights(FLevelLocals& doomMap) } // All meaasurements here are in vec4's. - int size0 = lightdata.arrays[0].Size() / 4; - int size1 = lightdata.arrays[1].Size() / 4; - int size2 = lightdata.arrays[2].Size() / 4; - int totalsize = size0 + size1 + size2 + 1; + int size0 = lightdata.arrays[LIGHTARRAY_NORMAL].Size(); + int size1 = lightdata.arrays[LIGHTARRAY_SUBTRACTIVE].Size(); + int size2 = lightdata.arrays[LIGHTARRAY_ADDITIVE].Size(); + int totalsize = size0 + size1 + size2; int maxLightData = Mesh.DynLights.Size(); - // Clamp lights so they aren't bigger than what fits into a single dynamic uniform buffer page - if (totalsize > maxLightData) - { - int diff = totalsize - maxLightData; + int parmcnt[] = { 0, size0, size0 + size1, size0 + size1 + size2 }; - size2 -= diff; - if (size2 < 0) - { - size1 += size2; - size2 = 0; - } - if (size1 < 0) - { - size0 += size1; - size1 = 0; - } - totalsize = size0 + size1 + size2 + 1; - } - size0 = std::min(size0, maxLightData - 1); + int* indexptr = (int*)Mesh.DynLights.Data(); - float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) }; + memcpy(indexptr, parmcnt, sizeof(int) * 4); - float* copyptr = (float*)Mesh.DynLights.Data(); - memcpy(©ptr[0], parmcnt, sizeof(FVector4)); - memcpy(©ptr[4], &lightdata.arrays[0][0], size0 * sizeof(FVector4)); - memcpy(©ptr[4 + 4 * size0], &lightdata.arrays[1][0], size1 * sizeof(FVector4)); - memcpy(©ptr[4 + 4 * (size0 + size1)], &lightdata.arrays[2][0], size2 * sizeof(FVector4)); + FDynLightInfo* dataptr = (FDynLightInfo*)(indexptr + 4); - UploadRanges.DynLight.Add(0, totalsize); + memcpy(dataptr, &lightdata.arrays[0][0], size0 * sizeof(FDynLightInfo)); + memcpy(dataptr + size0, &lightdata.arrays[1][0], size1 * sizeof(FDynLightInfo)); + memcpy(dataptr + (size0 + size1), &lightdata.arrays[2][0], size2 * sizeof(FDynLightInfo)); + + UploadRanges.DynLight.Add(0, sizeof(int) * 4 + totalsize * sizeof(FDynLightInfo)); } void DoomLevelMesh::UpdateWallPortals() diff --git a/src/rendering/hwrenderer/hw_dynlightdata.cpp b/src/rendering/hwrenderer/hw_dynlightdata.cpp index 325241402..6345a5a16 100644 --- a/src/rendering/hwrenderer/hw_dynlightdata.cpp +++ b/src/rendering/hwrenderer/hw_dynlightdata.cpp @@ -77,16 +77,19 @@ bool GetLight(FDynLightData& dld, int group, Plane & p, FDynamicLight * light, b //========================================================================== void AddLightToList(FDynLightData &dld, int group, FDynamicLight * light, bool forceAttenuate) { - int i = 0; + FDynLightInfo info = {}; + + int i = LIGHTARRAY_NORMAL; DVector3 pos = light->PosRelative(group); - float radius = light->GetRadius(); + + info.radius = light->GetRadius(); float cs; if (light->IsAdditive()) { cs = 0.2f; - i = 2; + i = LIGHTARRAY_ADDITIVE; } else { @@ -96,110 +99,80 @@ void AddLightToList(FDynLightData &dld, int group, FDynamicLight * light, bool f if (light->target) cs *= (float)light->target->Alpha; - float r = light->GetRed() / 255.0f * cs; - float g = light->GetGreen() / 255.0f * cs; - float b = light->GetBlue() / 255.0f * cs; + info.r = light->GetRed() / 255.0f * cs; + info.g = light->GetGreen() / 255.0f * cs; + info.b = light->GetBlue() / 255.0f * cs; if (light->IsSubtractive()) { - DVector3 v(r, g, b); + DVector3 v(info.r, info.g, info.b); float length = (float)v.Length(); - r = length - r; - g = length - g; - b = length - b; - i = 1; + info.r = length - info.r; + info.g = length - info.g; + info.b = length - info.b; + i = LIGHTARRAY_SUBTRACTIVE; } - float shadowIndex; - if (screen->mShadowMap->Enabled()) // note: with shadowmaps switched off, we cannot rely on properly set indices anymore. + if(light->shadowmapped && screen->mShadowMap->Enabled()) { - shadowIndex = light->mShadowmapIndex + 1.0f; + info.flags |= LIGHTINFO_SHADOWMAPPED; + info.shadowIndex = light->mShadowmapIndex + 1.0f; + } + else + { + info.shadowIndex = 1025.f; + } + + // Store attenuate flag in the sign bit of the float. + if (light->IsAttenuated() || forceAttenuate) + { + info.flags |= LIGHTINFO_ATTENUATED; } - else shadowIndex = 1025.f; - // Store attenuate flag in the sign bit of the float. - if (light->IsAttenuated() || forceAttenuate) shadowIndex = -shadowIndex; - bool lightType = false; - float spotInnerAngle = 0.0f; - float spotOuterAngle = 0.0f; - float spotDirX = 0.0f; - float spotDirY = 0.0f; - float spotDirZ = 0.0f; if (light->IsSpot()) { - lightType = true; - spotInnerAngle = (float)light->pSpotInnerAngle->Cos(); - spotOuterAngle = (float)light->pSpotOuterAngle->Cos(); + info.flags |= LIGHTINFO_SPOT; + + info.spotInnerAngle = (float)light->pSpotInnerAngle->Cos(); + info.spotOuterAngle = (float)light->pSpotOuterAngle->Cos(); DAngle negPitch = -*light->pPitch; DAngle Angle = light->target->Angles.Yaw; double xzLen = negPitch.Cos(); - spotDirX = float(-Angle.Cos() * xzLen); - spotDirY = float(-negPitch.Sin()); - spotDirZ = float(-Angle.Sin() * xzLen); + info.spotDirX = float(-Angle.Cos() * xzLen); + info.spotDirY = float(-negPitch.Sin()); + info.spotDirZ = float(-Angle.Sin() * xzLen); } + info.x = float(pos.X); + info.z = float(pos.Y); + info.y = float(pos.Z); - float softShadowRadius = light->GetSoftShadowRadius(); + info.softShadowRadius = light->GetSoftShadowRadius(); - float linearity = light->GetLinearity(); + info.linearity = std::clamp(light->GetLinearity(), 0.0f, 1.0f); - float strength = light->GetStrength(); + info.strength = light->GetStrength(); - float *data = &dld.arrays[i][dld.arrays[i].Reserve(16)]; - data[0] = float(pos.X); - data[1] = float(pos.Z); - data[2] = float(pos.Y); - data[3] = lightType ? -radius : radius; - data[4] = r; - data[5] = g; - data[6] = b; - data[7] = shadowIndex; - data[8] = spotDirX; - data[9] = spotDirY; - data[10] = spotDirZ; - data[11] = std::clamp(linearity, 0.0f, 1.0f); - data[12] = spotInnerAngle; - data[13] = spotOuterAngle; - data[14] = softShadowRadius; - data[15] = strength; + dld.arrays[i].Push(info); } void AddSunLightToList(FDynLightData& dld, float x, float y, float z, const FVector3& sundir, const FVector3& suncolor) { + FDynLightInfo info = {}; + // Cheap way of faking a directional light float dist = 100000.0f; - float radius = 100000000.0f; - x += sundir.X * dist; - y += sundir.Y * dist; - z += sundir.Z * dist; + info.radius = 100000000.0f; + info.x = x + sundir.X * dist; + info.z = y + sundir.Y * dist; + info.y = z + sundir.Z * dist; + info.r = suncolor.X; + info.g = suncolor.Y; + info.b = suncolor.Z; + info.flags = LIGHTINFO_ATTENUATED; + info.strength = 1500.0f; - int i = 0; - float spotInnerAngle = 0.0f; - float spotOuterAngle = 0.0f; - float spotDirX = 0.0f; - float spotDirY = 0.0f; - float spotDirZ = 0.0f; - float shadowIndex = -1025.f; // Note: 1025 disables shadowmap and the attenuate flag is in the sign bit of the float - - float strength = 1500.0f; - - float* data = &dld.arrays[i][dld.arrays[i].Reserve(16)]; - data[0] = float(x); - data[1] = float(z); - data[2] = float(y); - data[3] = radius; - data[4] = suncolor.X; - data[5] = suncolor.Y; - data[6] = suncolor.Z; - data[7] = shadowIndex; - data[8] = spotDirX; - data[9] = spotDirY; - data[10] = spotDirZ; - data[11] = 0.0f; // unused - data[12] = spotInnerAngle; - data[13] = spotOuterAngle; - data[14] = 0.0f; // unused - data[15] = strength; + dld.arrays[LIGHTARRAY_NORMAL].Push(info); } diff --git a/wadsrc/static/shaders/binding_struct_definitions.glsl b/wadsrc/static/shaders/binding_struct_definitions.glsl new file mode 100644 index 000000000..a5ae8175d --- /dev/null +++ b/wadsrc/static/shaders/binding_struct_definitions.glsl @@ -0,0 +1,161 @@ + +struct SurfaceInfo +{ + vec3 Normal; + float Sky; + uint PortalIndex; + int TextureIndex; + float Alpha; + float Padding0; + uint LightStart; + uint LightEnd; + uint Padding1; + uint Padding2; +}; + +struct PortalInfo +{ + mat4 Transformation; +}; + +struct LightInfo +{ + vec3 Origin; + float Padding0; + vec3 RelativeOrigin; + float Padding1; + float Radius; + float Intensity; + float InnerAngleCos; + float OuterAngleCos; + vec3 SpotDir; + float SoftShadowRadius; + vec3 Color; + float Padding3; +}; + +struct CollisionNode +{ + vec3 center; + float padding1; + vec3 extents; + float padding2; + int left; + int right; + int element_index; + int padding3; +}; + +struct SurfaceVertex // Note: this must always match the FFlatVertex struct +{ + vec3 pos; + float lindex; + vec2 uv; + vec2 luv; +}; + +struct LightmapRaytracePC +{ + int SurfaceIndex; + int Padding0; + int Padding1; + int Padding2; + vec3 WorldToLocal; + float TextureSize; + vec3 ProjLocalToU; + float Padding3; + vec3 ProjLocalToV; + float Padding4; + float TileX; + float TileY; + float TileWidth; + float TileHeight; +}; + +// This must match the C++ SurfaceUniforms struct +struct SurfaceUniforms +{ + vec4 uObjectColor; + vec4 uObjectColor2; + vec4 uDynLightColor; + vec4 uAddColor; + vec4 uTextureAddColor; + vec4 uTextureModulateColor; + vec4 uTextureBlendColor; + vec4 uFogColor; + float uDesaturationFactor; + float uInterpolationFactor; + float timer; // timer data for material shaders + int useVertexData; + vec4 uVertexColor; + vec4 uVertexNormal; + + vec4 uGlowTopPlane; + vec4 uGlowTopColor; + vec4 uGlowBottomPlane; + vec4 uGlowBottomColor; + + vec4 uGradientTopPlane; + vec4 uGradientBottomPlane; + + vec4 uSplitTopPlane; + vec4 uSplitBottomPlane; + + vec4 uDetailParms; + vec4 uNpotEmulation; + + vec2 uClipSplit; + vec2 uSpecularMaterial; + + float uLightLevel; + float uFogDensity; + float uLightFactor; + float uLightDist; + + float uAlphaThreshold; + int uTextureIndex; + float uDepthFadeThreshold; + float padding3; +}; + +struct SurfaceLightUniforms +{ + vec4 uVertexColor; + float uDesaturationFactor; + float uLightLevel; + uint padding0; + uint padding1; +}; + +struct Fogball +{ + vec3 position; + float radius; + vec3 color; + float fog; +}; + +#define LIGHTINFO_ATTENUATED 1 +#define LIGHTINFO_SHADOWMAPPED 2 +#define LIGHTINFO_SPOT 4 + +struct DynLightInfo +{ + vec3 pos; float padding0; + vec3 color; float padding1; + vec3 spotDir; float padding2; + float radius; + float linarity; + float softShadowRadius; + float strength; + float spotInnerAngle; + float spotOuterAngle; + int shadowIndex; + int flags; +}; + +struct LightTileBlock +{ + ivec4 indices; + DynLightInfo lights[16]; +}; \ No newline at end of file diff --git a/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl b/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl index a12377cab..80df8a9ff 100644 --- a/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl +++ b/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl @@ -1,4 +1,4 @@ -#include +#include layout(set = 0, binding = 0) uniform readonly Uniforms { diff --git a/wadsrc/static/shaders/lightmap/binding_raytrace.glsl b/wadsrc/static/shaders/lightmap/binding_raytrace.glsl index 0f2fa9a9d..df14c28d0 100644 --- a/wadsrc/static/shaders/lightmap/binding_raytrace.glsl +++ b/wadsrc/static/shaders/lightmap/binding_raytrace.glsl @@ -1,4 +1,4 @@ -#include +#include #if defined(USE_RAYQUERY) diff --git a/wadsrc/static/shaders/lightmap/binding_struct_definitions.glsl b/wadsrc/static/shaders/lightmap/binding_struct_definitions.glsl deleted file mode 100644 index 50147ed4d..000000000 --- a/wadsrc/static/shaders/lightmap/binding_struct_definitions.glsl +++ /dev/null @@ -1,73 +0,0 @@ - -struct SurfaceInfo -{ - vec3 Normal; - float Sky; - uint PortalIndex; - int TextureIndex; - float Alpha; - float Padding0; - uint LightStart; - uint LightEnd; - uint Padding1; - uint Padding2; -}; - -struct PortalInfo -{ - mat4 Transformation; -}; - -struct LightInfo -{ - vec3 Origin; - float Padding0; - vec3 RelativeOrigin; - float Padding1; - float Radius; - float Intensity; - float InnerAngleCos; - float OuterAngleCos; - vec3 SpotDir; - float SoftShadowRadius; - vec3 Color; - float Padding3; -}; - -struct CollisionNode -{ - vec3 center; - float padding1; - vec3 extents; - float padding2; - int left; - int right; - int element_index; - int padding3; -}; - -struct SurfaceVertex // Note: this must always match the FFlatVertex struct -{ - vec3 pos; - float lindex; - vec2 uv; - vec2 luv; -}; - -struct LightmapRaytracePC -{ - int SurfaceIndex; - int Padding0; - int Padding1; - int Padding2; - vec3 WorldToLocal; - float TextureSize; - vec3 ProjLocalToU; - float Padding3; - vec3 ProjLocalToV; - float Padding4; - float TileX; - float TileY; - float TileWidth; - float TileHeight; -}; \ No newline at end of file diff --git a/wadsrc/static/shaders/lightmap/binding_viewer.glsl b/wadsrc/static/shaders/lightmap/binding_viewer.glsl index 4b1fca5c5..28ce3fe3d 100644 --- a/wadsrc/static/shaders/lightmap/binding_viewer.glsl +++ b/wadsrc/static/shaders/lightmap/binding_viewer.glsl @@ -1,4 +1,4 @@ -#include +#include layout(set = 0, binding = 0, std430) buffer readonly SurfaceIndexBuffer { uint surfaceIndices[]; }; layout(set = 0, binding = 1, std430) buffer readonly SurfaceBuffer { SurfaceInfo surfaces[]; }; diff --git a/wadsrc/static/shaders/scene/binding_fixed.glsl b/wadsrc/static/shaders/scene/binding_fixed.glsl index 0741e7dfc..4d2215353 100644 --- a/wadsrc/static/shaders/scene/binding_fixed.glsl +++ b/wadsrc/static/shaders/scene/binding_fixed.glsl @@ -1,4 +1,4 @@ -#include +#include layout(set = 0, binding = 0) uniform sampler2D ShadowMap; layout(set = 0, binding = 1) uniform sampler2DArray LightMap; diff --git a/wadsrc/static/shaders/scene/binding_rsbuffers.glsl b/wadsrc/static/shaders/scene/binding_rsbuffers.glsl index 63fd5a7bb..1ef772200 100644 --- a/wadsrc/static/shaders/scene/binding_rsbuffers.glsl +++ b/wadsrc/static/shaders/scene/binding_rsbuffers.glsl @@ -27,68 +27,6 @@ layout(set = 1, binding = 1, std140) uniform readonly MatricesUBO mat4 TextureMatrix; }; -// This must match the C++ SurfaceUniforms struct -struct SurfaceUniforms -{ - vec4 uObjectColor; - vec4 uObjectColor2; - vec4 uDynLightColor; - vec4 uAddColor; - vec4 uTextureAddColor; - vec4 uTextureModulateColor; - vec4 uTextureBlendColor; - vec4 uFogColor; - float uDesaturationFactor; - float uInterpolationFactor; - float timer; // timer data for material shaders - int useVertexData; - vec4 uVertexColor; - vec4 uVertexNormal; - - vec4 uGlowTopPlane; - vec4 uGlowTopColor; - vec4 uGlowBottomPlane; - vec4 uGlowBottomColor; - - vec4 uGradientTopPlane; - vec4 uGradientBottomPlane; - - vec4 uSplitTopPlane; - vec4 uSplitBottomPlane; - - vec4 uDetailParms; - vec4 uNpotEmulation; - - vec2 uClipSplit; - vec2 uSpecularMaterial; - - float uLightLevel; - float uFogDensity; - float uLightFactor; - float uLightDist; - - float uAlphaThreshold; - int uTextureIndex; - float uDepthFadeThreshold; - float padding3; -}; - -struct SurfaceLightUniforms -{ - vec4 uVertexColor; - float uDesaturationFactor; - float uLightLevel; - uint padding0; - uint padding1; -}; - -struct Fogball -{ - vec3 position; - float radius; - vec3 color; - float fog; -}; #ifdef USE_LEVELMESH @@ -104,9 +42,13 @@ layout(set = 1, binding = 3, std430) buffer readonly SurfaceLightUniformsSSO layout(set = 1, binding = 4, std430) buffer readonly LightBufferSSO { - vec4 lights[]; + LightTileBlock lights[]; }; +#define getLightRange() lights[uLightIndex].indices + +#define getLights() lights[uLightIndex].lights + layout(set = 1, binding = 5, std140) uniform readonly FogballBufferUBO { Fogball fogballs[MAX_FOGBALL_DATA]; @@ -125,12 +67,18 @@ layout(set = 1, binding = 2, std140) uniform readonly SurfaceUniformsUBO SurfaceUniforms data[MAX_SURFACE_UNIFORMS]; }; + // light buffers -layout(set = 1, binding = 3, std140) uniform readonly LightBufferUBO +layout(set = 1, binding = 3, std430) buffer readonly LightBufferSSO { - vec4 lights[MAX_LIGHT_DATA]; + ivec4 lightIndex[MAX_LIGHT_DATA]; + DynLightInfo lights[]; }; +#define getLightRange() lightIndex[uLightIndex] + +#define getLights() lights + layout(set = 1, binding = 4, std140) uniform readonly FogballBufferUBO { Fogball fogballs[MAX_FOGBALL_DATA]; diff --git a/wadsrc/static/shaders/scene/comp_lighttiles.glsl b/wadsrc/static/shaders/scene/comp_lighttiles.glsl index a6ded965c..de5f8c8f6 100644 --- a/wadsrc/static/shaders/scene/comp_lighttiles.glsl +++ b/wadsrc/static/shaders/scene/comp_lighttiles.glsl @@ -1,3 +1,25 @@ +//#include //doesn't work somehow? + +struct DynLightInfo +{ + vec3 pos; float padding0; + vec3 color; float padding1; + vec3 spotDir; float padding2; + float radius; + float linarity; + float softShadowRadius; + float strength; + float spotInnerAngle; + float spotOuterAngle; + int shadowIndex; + int flags; +}; + +struct LightTileBlock +{ + ivec4 indices; + DynLightInfo lights[16]; +}; #define TILE_SIZE 64 @@ -7,12 +29,13 @@ layout(set = 0, binding = 0, rg32f) readonly uniform image2D zminmax; layout(set = 0, binding = 1) readonly buffer Lights { - vec4 lights[]; + ivec4 indices; + DynLightInfo lights[]; }; layout(set = 0, binding = 2) buffer Tiles { - vec4 tiles[]; + LightTileBlock tiles[]; }; layout(push_constant) uniform LightTilesPushConstants @@ -40,70 +63,57 @@ void main() ivec2 tilePos = ivec2(gl_GlobalInvocationID.xy); if (tilePos.x >= zminmaxSize.x || tilePos.y >= zminmaxSize.y) return; - + vec2 minmax = imageLoad(zminmax, tilePos).xy; float zmin = minmax.x; float zmax = minmax.y; Tile tile = findTileFrustum(zmin, zmax, tilePos.x, tilePos.y); - + const int maxLights = 16; - const int lightSize = 4; - int tileOffset = (tilePos.x + tilePos.y * zminmaxSize.x) * (1 + maxLights * lightSize); - - ivec4 inRanges = ivec4(lights[0]) + ivec4(1); + int tileOffset = (tilePos.x + tilePos.y * zminmaxSize.x); + + ivec4 inRanges = indices; ivec4 outRanges = ivec4(0); - + int count = 0; - int offset = tileOffset + 1; - for (int i = inRanges.x; i < inRanges.y; i += 4) + + if(count < maxLights) for (int i = inRanges.x; i < inRanges.y; i++) { - vec4 inLight = lights[i]; - vec3 pos = (worldToView * vec4(inLight.xyz, 1.0)).xyz; - float radius = inLight.w; + vec3 pos = (worldToView * vec4(lights[i].pos, 1.0)).xyz; + float radius = lights[i].radius; if (isLightVisible(tile, pos, radius)) { - tiles[offset++] = inLight; - tiles[offset++] = lights[i + 1]; - tiles[offset++] = lights[i + 2]; - tiles[offset++] = lights[i + 3]; - count += 4; + tiles[tileOffset].lights[count++] = lights[i]; + if(count == maxLights) break; } } outRanges.y = count; - - for (int i = inRanges.y; i < inRanges.z; i += 4) + + if(count < maxLights) for (int i = inRanges.y; i < inRanges.z; i++) { - vec4 inLight = lights[i]; - vec3 pos = (worldToView * vec4(inLight.xyz, 1.0)).xyz; - float radius = inLight.w; + vec3 pos = (worldToView * vec4(lights[i].pos, 1.0)).xyz; + float radius = lights[i].radius; if (isLightVisible(tile, pos, radius)) { - tiles[offset++] = inLight; - tiles[offset++] = lights[i + 1]; - tiles[offset++] = lights[i + 2]; - tiles[offset++] = lights[i + 3]; - count += 4; + tiles[tileOffset].lights[count++] = lights[i]; + if(count == maxLights) break; } } outRanges.z = count; - - for (int i = inRanges.z; i < inRanges.w; i += 4) + + if(count < maxLights) for (int i = inRanges.z; i < inRanges.w; i++) { - vec4 inLight = lights[i]; - vec3 pos = (worldToView * vec4(inLight.xyz, 1.0)).xyz; - float radius = inLight.w; + vec3 pos = (worldToView * vec4(lights[i].pos, 1.0)).xyz; + float radius = lights[i].radius; if (isLightVisible(tile, pos, radius)) { - tiles[offset++] = inLight; - tiles[offset++] = lights[i + 1]; - tiles[offset++] = lights[i + 2]; - tiles[offset++] = lights[i + 3]; - count += 4; + tiles[tileOffset].lights[count++] = lights[i]; + if(count == maxLights) break; } } outRanges.w = count; - - tiles[tileOffset] = vec4(outRanges); + + tiles[tileOffset].indices = outRanges; } bool isLightVisible(Tile tile, vec3 lightPos, float lightRadius) @@ -136,5 +146,5 @@ Tile findTileFrustum(float viewZNear, float viewZFar, uint tileX, uint tileY) vec3 unprojectDirection(vec2 pos) { - return vec3(posToViewA * (pos - viewportPos) + posToViewB, 1.0f); + return vec3(posToViewA * (pos - viewportPos) + posToViewB, 1.0f); } diff --git a/wadsrc/static/shaders/scene/frag_main.glsl b/wadsrc/static/shaders/scene/frag_main.glsl index d5d9a22ed..465918a01 100644 --- a/wadsrc/static/shaders/scene/frag_main.glsl +++ b/wadsrc/static/shaders/scene/frag_main.glsl @@ -26,7 +26,7 @@ void main() #endif #if defined(USE_LEVELMESH) - const int lightTileSize = 1 + 16 * 4; + const int lightTileSize = 1; uLightIndex = int(uint(gl_FragCoord.x) / 64 + uint(gl_FragCoord.y) / 64 * uLightTilesWidth) * lightTileSize; #endif diff --git a/wadsrc/static/shaders/scene/light_shadow.glsl b/wadsrc/static/shaders/scene/light_shadow.glsl index b7be3719e..2f6bf0647 100644 --- a/wadsrc/static/shaders/scene/light_shadow.glsl +++ b/wadsrc/static/shaders/scene/light_shadow.glsl @@ -48,7 +48,7 @@ float traceHit(vec3 origin, vec3 direction, float dist) #endif } -float traceShadow(vec4 lightpos, float softShadowRadius) +float traceShadow(vec3 lightpos, float softShadowRadius) { vec3 origin = pixelpos.xyz + vWorldNormal.xyz; vec3 target = lightpos.xyz + 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes @@ -82,14 +82,8 @@ float traceShadow(vec4 lightpos, float softShadowRadius) #endif } -float shadowAttenuation(vec4 lightpos, float lightcolorA, float softShadowRadius) +float shadowAttenuation(vec3 lightpos, int shadowIndex, float softShadowRadius) { - float shadowIndex = abs(lightcolorA) - 1.0; - if (shadowIndex >= 16000000.0) - return 1.0; // No shadowmap available for this light - - if (lightpos.w > 1000000.0) - return 1.0; // Sunlight return traceShadow(lightpos, softShadowRadius); } @@ -200,7 +194,7 @@ float sampleShadowmapPCF(vec3 planePoint, float v) return sum / (SHADOWMAP_FILTER * 2.0 + 1.0); } -float shadowmapAttenuation(vec4 lightpos, float shadowIndex) +float shadowmapAttenuation(vec3 lightpos, float shadowIndex) { vec3 planePoint = pixelpos.xyz - lightpos.xyz; planePoint += 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes @@ -217,21 +211,17 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex) #endif } -float shadowAttenuation(vec4 lightpos, float lightcolorA, float softShadowRadius) +float shadowAttenuation(vec3 lightpos, int shadowIndex, float softShadowRadius) { - if (lightpos.w > 1000000.0) - return 1.0; // Sunlight - - float shadowIndex = abs(lightcolorA) - 1.0; - if (shadowIndex >= 1024.0) + if (shadowIndex >= 1024) return 1.0; // No shadowmap available for this light - - return shadowmapAttenuation(lightpos, shadowIndex); + + return shadowmapAttenuation(lightpos, float(shadowIndex)); } #else -float shadowAttenuation(vec4 lightpos, float lightcolorA, float softShadowRadius) +float shadowAttenuation(vec3 lightpos, int shadowIndex, float softShadowRadius) { return 1.0; } diff --git a/wadsrc/static/shaders/scene/light_spot.glsl b/wadsrc/static/shaders/scene/light_spot.glsl index 204cf5924..a101b9d9f 100644 --- a/wadsrc/static/shaders/scene/light_spot.glsl +++ b/wadsrc/static/shaders/scene/light_spot.glsl @@ -1,5 +1,5 @@ -float spotLightAttenuation(vec4 lightpos, vec3 spotdir, float lightCosInnerAngle, float lightCosOuterAngle) +float spotLightAttenuation(vec3 lightpos, vec3 spotdir, float lightCosInnerAngle, float lightCosOuterAngle) { vec3 lightDirection = normalize(lightpos.xyz - pixelpos.xyz); float cosDir = dot(lightDirection, spotdir); diff --git a/wadsrc/static/shaders/scene/lightmodel_normal.glsl b/wadsrc/static/shaders/scene/lightmodel_normal.glsl index 72016e587..bc1a32921 100644 --- a/wadsrc/static/shaders/scene/lightmodel_normal.glsl +++ b/wadsrc/static/shaders/scene/lightmodel_normal.glsl @@ -1,62 +1,65 @@ #ifndef SIMPLE3D - vec3 lightContribution(int i, vec3 normal) + vec3 lightContribution(DynLightInfo light, vec3 normal) { - vec4 lightpos = lights[i]; - vec4 lightcolor = lights[i+1]; - vec4 lightspot1 = lights[i+2]; - vec4 lightspot2 = lights[i+3]; + float lightdistance = distance(light.pos.xyz, pixelpos.xyz); - float radius = abs(lightpos.w); - - float lightdistance = distance(lightpos.xyz, pixelpos.xyz); - if (radius < lightdistance) + if (light.radius < lightdistance) return vec3(0.0); // Early out lights touching surface but not this fragment - vec3 lightdir = normalize(lightpos.xyz - pixelpos.xyz); + vec3 lightdir = normalize(light.pos.xyz - pixelpos.xyz); float dotprod = dot(normal, lightdir); if (dotprod < -0.0001) return vec3(0.0); // light hits from the backside. This can happen with full sector light lists and must be rejected for all cases. Note that this can cause precision issues. + + float attenuation = distanceAttenuation(lightdistance, light.radius, light.strength, light.linearity); - float attenuation = distanceAttenuation(lightdistance, radius, lightspot2.w, lightspot1.w); + if ((light.flags & LIGHTINFO_SPOT) != 0) + { + attenuation *= spotLightAttenuation(light.pos.xyz, light.spotDir.xyz, light.spotInnerAngle, light.spotOuterAngle); + } - if (lightpos.w < 0.0) - attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); // Sign bit is the spotlight flag - - if (lightcolor.a < 0.0) // Sign bit is the attenuated light flag + if ((light.flags & LIGHTINFO_ATTENUATED) != 0) { attenuation *= clamp(dotprod, 0.0, 1.0); } + if (attenuation > 0.0) // Skip shadow map test if possible { - attenuation *= shadowAttenuation(lightpos, lightcolor.a, lightspot2.z); - return lightcolor.rgb * attenuation; + // light.radius >= 1000000.0 is sunlight(?), skip attenuation + if(light.radius < 1000000.0 && (light.flags & LIGHTINFO_SHADOWMAPPED) != 0) + { + attenuation *= shadowAttenuation(light.pos.xyz, light.shadowIndex, light.softShadowRadius); + } + + return light.color.rgb * attenuation; } else { return vec3(0.0); } } - + vec3 ProcessMaterialLight(Material material, vec3 color) { vec4 dynlight = uDynLightColor; vec3 normal = material.Normal; - + if (uLightIndex >= 0) { - ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + ivec4 lightRange = getLightRange(); + if (lightRange.z > lightRange.x) { // modulated lights - for(int i=lightRange.x; i= 0) { - ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + ivec4 lightRange = getLightRange(); if (lightRange.w > lightRange.z) { vec4 addlight = vec4(0.0,0.0,0.0,0.0); // additive lights - for(int i=lightRange.z; i 0.0) + { + // light.radius >= 1000000.0 is sunlight(?), skip attenuation + + if(light.radius < 1000000.0 && (light.flags & LIGHTINFO_SHADOWMAPPED) != 0) + { + attenuation *= shadowAttenuation(light.pos.xyz, light.shadowIndex, light.softShadowRadius); + } + + vec3 radiance = light.color.rgb * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); + + vec3 kS = F; + vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); + + vec3 nominator = NDF * G * F; + float denominator = 4.0 * clamp(dot(N, V), 0.0, 1.0) * clamp(dot(N, L), 0.0, 1.0); + vec3 specular = nominator / max(denominator, 0.001); + + return (kD * albedo / PI + specular) * radiance; + } + + return vec3(0.0); +} + vec3 ProcessMaterialLight(Material material, vec3 ambientLight) { - vec3 worldpos = pixelpos.xyz; - vec3 albedo = material.Base.rgb; float metallic = material.Metallic; @@ -55,7 +98,7 @@ vec3 ProcessMaterialLight(Material material, vec3 ambientLight) float ao = material.AO; vec3 N = material.Normal; - vec3 V = normalize(uCameraPos.xyz - worldpos); + vec3 V = normalize(uCameraPos.xyz - pixelpos.xyz); vec3 F0 = mix(vec3(0.04), albedo, metallic); @@ -63,88 +106,22 @@ vec3 ProcessMaterialLight(Material material, vec3 ambientLight) if (uLightIndex >= 0) { - ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + ivec4 lightRange = getLightRange(); if (lightRange.z > lightRange.x) { // // modulated lights // - for(int i=lightRange.x; i 0.0) - { - attenuation *= shadowAttenuation(lightpos, lightcolor.a, lightspot2.z); - - vec3 radiance = lightcolor.rgb * attenuation; - - // cook-torrance brdf - float NDF = DistributionGGX(N, H, roughness); - float G = GeometrySmith(N, V, L, roughness); - vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); - - vec3 kS = F; - vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); - - vec3 nominator = NDF * G * F; - float denominator = 4.0 * clamp(dot(N, V), 0.0, 1.0) * clamp(dot(N, L), 0.0, 1.0); - vec3 specular = nominator / max(denominator, 0.001); - - Lo += (kD * albedo / PI + specular) * radiance; - } + Lo += ProcessLight(getLights()[i], albedo, metallic, roughness, N, V, F0); } // // subtractive lights // - for(int i=lightRange.y; i 0.0) - { - attenuation *= shadowAttenuation(lightpos, lightcolor.a, lightspot2.z); - - vec3 radiance = lightcolor.rgb * attenuation; - - // cook-torrance brdf - float NDF = DistributionGGX(N, H, roughness); - float G = GeometrySmith(N, V, L, roughness); - vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); - - vec3 kS = F; - vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); - - vec3 nominator = NDF * G * F; - float denominator = 4.0 * clamp(dot(N, V), 0.0, 1.0) * clamp(dot(N, L), 0.0, 1.0); - vec3 specular = nominator / max(denominator, 0.001); - - Lo -= (kD * albedo / PI + specular) * radiance; - } + Lo -= ProcessLight(getLights()[i], albedo, metallic, roughness, N, V, F0); } } } diff --git a/wadsrc/static/shaders/scene/lightmodel_specular.glsl b/wadsrc/static/shaders/scene/lightmodel_specular.glsl index 4c4b943f9..03fae3a69 100644 --- a/wadsrc/static/shaders/scene/lightmodel_specular.glsl +++ b/wadsrc/static/shaders/scene/lightmodel_specular.glsl @@ -1,27 +1,31 @@ -vec2 lightAttenuation(int i, vec3 normal, vec3 viewdir, float lightcolorA, float glossiness, float specularLevel) +vec2 lightAttenuation(DynLightInfo light, vec3 normal, vec3 viewdir, float glossiness, float specularLevel) { - vec4 lightpos = lights[i]; - vec4 lightspot1 = lights[i+2]; - vec4 lightspot2 = lights[i+3]; - - float radius = abs(lightpos.w); - - float lightdistance = distance(lightpos.xyz, pixelpos.xyz); - if (radius < lightdistance) + float lightdistance = distance(light.pos.xyz, pixelpos.xyz); + if (light.radius < lightdistance) return vec2(0.0); // Early out lights touching surface but not this fragment - float attenuation = distanceAttenuation(lightdistance, radius, lightspot2.w, lightspot1.w); + float attenuation = distanceAttenuation(lightdistance, light.radius, light.strength, light.linearity); - if (lightpos.w < 0.0) - attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); // Sign bit is the spotlight flag + if ((light.flags & LIGHTINFO_SPOT) != 0) + { + attenuation *= spotLightAttenuation(light.pos.xyz, light.spotDir.xyz, light.spotInnerAngle, light.spotOuterAngle); + } - vec3 lightdir = normalize(lightpos.xyz - pixelpos.xyz); + vec3 lightdir = normalize(light.pos.xyz - pixelpos.xyz); - if (lightcolorA < 0.0) // Sign bit is the attenuated light flag + if ((light.flags & LIGHTINFO_ATTENUATED) != 0) + { attenuation *= clamp(dot(normal, lightdir), 0.0, 1.0); + } if (attenuation > 0.0) // Skip shadow map test if possible - attenuation *= shadowAttenuation(lightpos, lightcolorA, lightspot2.z); + { + // light.radius >= 1000000.0 is sunlight(?), skip attenuation + if(light.radius < 1000000.0 && (light.flags & LIGHTINFO_SHADOWMAPPED) != 0) + { + attenuation *= shadowAttenuation(light.pos.xyz, light.shadowIndex, light.softShadowRadius); + } + } if (attenuation <= 0.0) return vec2(0.0); @@ -42,63 +46,63 @@ vec3 ProcessMaterialLight(Material material, vec3 color) if (uLightIndex >= 0) { - ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + ivec4 lightRange = getLightRange(); if (lightRange.z > lightRange.x) { // modulated lights - for(int i=lightRange.x; i= 0) { - ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + ivec4 lightRange = getLightRange(); if (lightRange.w > lightRange.z) { vec4 addlight = vec4(0.0,0.0,0.0,0.0); // additive lights - for(int i=lightRange.z; i