diff --git a/src/doomdata.h b/src/doomdata.h index a9f118a73..c59c6af01 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -384,7 +384,7 @@ struct FMapThing int friendlyseeblocks; FName arg0str; double SoftShadowRadius; - double LightStrength; + double LightLinearity; bool LightNoShadowMap; bool LightDontLightActors; bool LightDontLightMap; diff --git a/src/maploader/udmf.cpp b/src/maploader/udmf.cpp index 15f6227ca..d2a415340 100644 --- a/src/maploader/udmf.cpp +++ b/src/maploader/udmf.cpp @@ -796,8 +796,8 @@ public: th->SoftShadowRadius = CheckFloat(key); break; - case NAME_light_strength: - th->LightStrength = CheckFloat(key); + case NAME_light_linearity: + th->LightLinearity = CheckFloat(key); break; case NAME_light_noshadowmap: diff --git a/src/namedef_custom.h b/src/namedef_custom.h index a9af0a077..661de6665 100644 --- a/src/namedef_custom.h +++ b/src/namedef_custom.h @@ -321,7 +321,7 @@ xx(DynamicLight) xx(SpotInnerAngle) xx(SpotOuterAngle) xx(SoftShadowRadius) -xx(LightStrength) +xx(LightLinearity) xx(lightflags) xx(lighttype) xx(InternalDynamicLight) @@ -871,7 +871,7 @@ xx(lm_suncolor) // Light keywords xx(light_softshadowradius) -xx(light_strength) +xx(light_linearity) xx(light_noshadowmap) xx(light_dontlightactors) xx(light_dontlightmap) diff --git a/src/playsim/a_dynlight.cpp b/src/playsim/a_dynlight.cpp index 78e8b1f1e..4a4cb46fd 100644 --- a/src/playsim/a_dynlight.cpp +++ b/src/playsim/a_dynlight.cpp @@ -99,7 +99,7 @@ static FDynamicLight *GetLight(FLevelLocals *Level) } -static inline constexpr float calcStrength(float radius) +float LightCalcStrength(float radius) { radius *= 2; // radius in shaders is actualy diameter, so multiply it here to match return std::min(1500.0f, (radius * radius) / 10); @@ -122,12 +122,10 @@ void AttachLight(AActor *self) light->pLightFlags = (LightFlags*)&self->IntVar(NAME_lightflags); light->pArgs = self->args; light->pSoftShadowRadius = &self->FloatVar(NAME_SoftShadowRadius); - light->pStrength = &self->FloatVar(NAME_LightStrength); + light->pLinearity = &self->FloatVar(NAME_LightLinearity); - if(*light->pStrength <= 0) - { - *light->pStrength = -calcStrength(light->m_currentRadius); - } + light->lightStrength = LightCalcStrength(light->pArgs[LIGHT_INTENSITY]); + light->specialf1 = DAngle::fromDeg(double(self->SpawnAngle)).Normalized360().Degrees(); light->Sector = self->Sector; @@ -250,10 +248,7 @@ void FDynamicLight::Activate() } if (m_currentRadius <= 0) m_currentRadius = 1; - if(pStrength && *pStrength <= 0) - { - *pStrength = -calcStrength(m_currentRadius); - } + lightStrength = LightCalcStrength(m_currentRadius); } @@ -377,9 +372,9 @@ void FDynamicLight::Tick() if (m_currentRadius <= 0) m_currentRadius = 1; - if(pStrength && *pStrength <= 0 && oldradius != m_currentRadius) + if(oldradius != m_currentRadius) { - *pStrength = -calcStrength(m_currentRadius); + lightStrength = LightCalcStrength(m_currentRadius); } UpdateLocation(); } diff --git a/src/playsim/a_dynlight.h b/src/playsim/a_dynlight.h index 4180e8ebb..12c3c3ffe 100644 --- a/src/playsim/a_dynlight.h +++ b/src/playsim/a_dynlight.h @@ -131,7 +131,7 @@ protected: DAngle m_spotOuterAngle = DAngle::fromDeg(25.0); DAngle m_pitch = nullAngle; double SoftShadowRadius = 5.0; - double Strength = 0.0; + double Linearity = 0.0; friend FSerializer &Serialize(FSerializer &arc, const char *key, FLightDefaults &value, FLightDefaults *def); }; @@ -225,7 +225,8 @@ struct FDynamicLight bool IsActive() const { return m_active; } float GetRadius() const { return (IsActive() ? m_currentRadius * 2.f : 0.f); } float GetSoftShadowRadius() const { return (float)(*pSoftShadowRadius); } - float GetStrength() const { return std::abs((float)*pStrength); } + float GetLinearity() const { return (float)(*pLinearity); } + float GetStrength() const { return lightStrength; } int GetRed() const { return pArgs[LIGHT_RED]; } int GetGreen() const { return pArgs[LIGHT_GREEN]; } int GetBlue() const { return pArgs[LIGHT_BLUE]; } @@ -274,7 +275,7 @@ public: const LightFlags *pLightFlags; const double* pSoftShadowRadius; // Softshadows. Physical size of the light source - double* pStrength; // Softshadows. Physical size of the light source + const double* pLinearity; double specialf1; FDynamicLight *next, *prev; @@ -296,7 +297,7 @@ public: bool swapped; bool explicitpitch; - double tmpStrength; + float lightStrength; // Locations in the level mesh light list. Ends with index = 0 or all entries used enum { max_levelmesh_entries = 4 }; diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 9345e699a..6a8bae1a7 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -6214,9 +6214,9 @@ AActor *FLevelLocals::SpawnMapThing (FMapThing *mthing, int position) mobj->FloatVar(NAME_SoftShadowRadius) = 5.0; } - if (mthing->LightStrength > 0.0) + if (mthing->LightLinearity > 0.0) { - mobj->FloatVar(NAME_LightStrength) = mthing->LightStrength; + mobj->FloatVar(NAME_LightLinearity) = mthing->LightLinearity; } if (mthing->LightNoShadowMap) diff --git a/src/r_data/a_dynlightdata.cpp b/src/r_data/a_dynlightdata.cpp index cd043fc49..e12febf58 100644 --- a/src/r_data/a_dynlightdata.cpp +++ b/src/r_data/a_dynlightdata.cpp @@ -118,6 +118,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, TDeletingArraylighttype; @@ -128,15 +130,8 @@ void FLightDefaults::ApplyProperties(FDynamicLight * light) const light->pArgs = m_Args; light->pSoftShadowRadius = &SoftShadowRadius; - if(Strength > 0.0) - { - light->pStrength = const_cast(&Strength); // casting const to non-const, but positive strength values will not be modified - } - else - { - light->tmpStrength = 0.0; - light->pStrength = &light->tmpStrength; - } + light->pLinearity = &Linearity; + light->lightStrength = LightCalcStrength(light->pArgs[LIGHT_INTENSITY]); light->pLightFlags = &m_lightFlags; if (m_lightFlags & LF_SPOT) diff --git a/src/rendering/hwrenderer/hw_dynlightdata.cpp b/src/rendering/hwrenderer/hw_dynlightdata.cpp index adf2c57df..325241402 100644 --- a/src/rendering/hwrenderer/hw_dynlightdata.cpp +++ b/src/rendering/hwrenderer/hw_dynlightdata.cpp @@ -120,7 +120,7 @@ void AddLightToList(FDynLightData &dld, int group, FDynamicLight * light, bool f // Store attenuate flag in the sign bit of the float. if (light->IsAttenuated() || forceAttenuate) shadowIndex = -shadowIndex; - float lightType = 0.0f; + bool lightType = false; float spotInnerAngle = 0.0f; float spotOuterAngle = 0.0f; float spotDirX = 0.0f; @@ -128,7 +128,7 @@ void AddLightToList(FDynLightData &dld, int group, FDynamicLight * light, bool f float spotDirZ = 0.0f; if (light->IsSpot()) { - lightType = 1.0f; + lightType = true; spotInnerAngle = (float)light->pSpotInnerAngle->Cos(); spotOuterAngle = (float)light->pSpotOuterAngle->Cos(); @@ -139,15 +139,19 @@ void AddLightToList(FDynLightData &dld, int group, FDynamicLight * light, bool f spotDirY = float(-negPitch.Sin()); spotDirZ = float(-Angle.Sin() * xzLen); } + + float softShadowRadius = light->GetSoftShadowRadius(); + float linearity = light->GetLinearity(); + float 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] = radius; + data[3] = lightType ? -radius : radius; data[4] = r; data[5] = g; data[6] = b; @@ -155,7 +159,7 @@ void AddLightToList(FDynLightData &dld, int group, FDynamicLight * light, bool f data[8] = spotDirX; data[9] = spotDirY; data[10] = spotDirZ; - data[11] = lightType; + data[11] = std::clamp(linearity, 0.0f, 1.0f); data[12] = spotInnerAngle; data[13] = spotOuterAngle; data[14] = softShadowRadius; @@ -172,7 +176,6 @@ void AddSunLightToList(FDynLightData& dld, float x, float y, float z, const FVec z += sundir.Z * dist; int i = 0; - float lightType = 0.0f; float spotInnerAngle = 0.0f; float spotOuterAngle = 0.0f; float spotDirX = 0.0f; @@ -194,7 +197,7 @@ void AddSunLightToList(FDynLightData& dld, float x, float y, float z, const FVec data[8] = spotDirX; data[9] = spotDirY; data[10] = spotDirZ; - data[11] = lightType; + data[11] = 0.0f; // unused data[12] = spotInnerAngle; data[13] = spotOuterAngle; data[14] = 0.0f; // unused diff --git a/wadsrc/static/shaders/scene/lightmodel_normal.glsl b/wadsrc/static/shaders/scene/lightmodel_normal.glsl index 03df92b54..266fef963 100644 --- a/wadsrc/static/shaders/scene/lightmodel_normal.glsl +++ b/wadsrc/static/shaders/scene/lightmodel_normal.glsl @@ -4,19 +4,21 @@ vec3 lightContribution(int i, vec3 normal) vec4 lightcolor = lights[i+1]; vec4 lightspot1 = lights[i+2]; vec4 lightspot2 = lights[i+3]; + + float radius = abs(lightpos.w); float lightdistance = distance(lightpos.xyz, pixelpos.xyz); - if (lightpos.w < lightdistance) + if (radius < lightdistance) return vec3(0.0); // Early out lights touching surface but not this fragment vec3 lightdir = normalize(lightpos.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, lightpos.w, lightspot2.w); + float attenuation = distanceAttenuation(lightdistance, radius, lightspot2.w, lightspot1.w); - if (lightspot1.w == 1.0) - attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); + 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 { diff --git a/wadsrc/static/shaders/scene/lightmodel_pbr.glsl b/wadsrc/static/shaders/scene/lightmodel_pbr.glsl index 112cfa4e5..ed3db3e4a 100644 --- a/wadsrc/static/shaders/scene/lightmodel_pbr.glsl +++ b/wadsrc/static/shaders/scene/lightmodel_pbr.glsl @@ -79,9 +79,9 @@ vec3 ProcessMaterialLight(Material material, vec3 ambientLight) vec3 L = normalize(lightpos.xyz - worldpos); vec3 H = normalize(V + L); - float attenuation = distanceAttenuation(distance(lightpos.xyz, pixelpos.xyz), lightpos.w, lightspot2.w); - if (lightspot1.w == 1.0) - attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); + float attenuation = distanceAttenuation(distance(lightpos.xyz, pixelpos.xyz), abs(lightpos.w), lightspot2.w, lightspot1.w); + 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) attenuation *= clamp(dot(N, L), 0.0, 1.0); // Sign bit is the attenuated light flag @@ -119,9 +119,9 @@ vec3 ProcessMaterialLight(Material material, vec3 ambientLight) vec3 L = normalize(lightpos.xyz - worldpos); vec3 H = normalize(V + L); - float attenuation = distanceAttenuation(distance(lightpos.xyz, pixelpos.xyz), lightpos.w, lightspot2.w); - if (lightspot1.w == 1.0) - attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); + float attenuation = distanceAttenuation(distance(lightpos.xyz, pixelpos.xyz), abs(lightpos.w), lightspot2.w, lightspot1.w); + 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) attenuation *= clamp(dot(N, L), 0.0, 1.0); // Sign bit is the attenuated light flag diff --git a/wadsrc/static/shaders/scene/lightmodel_shared.glsl b/wadsrc/static/shaders/scene/lightmodel_shared.glsl index 296466a7a..4b570d2ad 100644 --- a/wadsrc/static/shaders/scene/lightmodel_shared.glsl +++ b/wadsrc/static/shaders/scene/lightmodel_shared.glsl @@ -1,15 +1,15 @@ #ifdef LIGHT_ATTENUATION_INVERSE_SQUARE -float distanceAttenuation(float dist, float radius, float strength) +float distanceAttenuation(float dist, float radius, float strength, float linearity) { float a = dist / radius; float b = clamp(1.0 - a * a * a * a, 0.0, 1.0); - return (b * b) / (dist * dist + 1.0) * strength; + return mix((b * b) / (dist * dist + 1.0) * strength, clamp((radius - dist) / radius, 0.0, 1.0), linearity); } #else //elif defined(LIGHT_ATTENUATION_LINEAR) -#define distanceAttenuation(dist, radius, strength) clamp((radius - dist) / radius, 0.0, 1.0) +#define distanceAttenuation(dist, radius, strength, linearity) clamp((radius - dist) / radius, 0.0, 1.0) #endif diff --git a/wadsrc/static/shaders/scene/lightmodel_specular.glsl b/wadsrc/static/shaders/scene/lightmodel_specular.glsl index bd1914549..4c4b943f9 100644 --- a/wadsrc/static/shaders/scene/lightmodel_specular.glsl +++ b/wadsrc/static/shaders/scene/lightmodel_specular.glsl @@ -3,15 +3,17 @@ vec2 lightAttenuation(int i, vec3 normal, vec3 viewdir, float lightcolorA, float 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 (lightpos.w < lightdistance) + if (radius < lightdistance) return vec2(0.0); // Early out lights touching surface but not this fragment - float attenuation = distanceAttenuation(lightdistance, lightpos.w, lightspot2.w); + float attenuation = distanceAttenuation(lightdistance, radius, lightspot2.w, lightspot1.w); - if (lightspot1.w == 1.0) - attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); + if (lightpos.w < 0.0) + attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); // Sign bit is the spotlight flag vec3 lightdir = normalize(lightpos.xyz - pixelpos.xyz); diff --git a/wadsrc/static/zscript/actors/shared/dynlights.zs b/wadsrc/static/zscript/actors/shared/dynlights.zs index f4ca60556..2cd64c09c 100644 --- a/wadsrc/static/zscript/actors/shared/dynlights.zs +++ b/wadsrc/static/zscript/actors/shared/dynlights.zs @@ -3,7 +3,7 @@ class DynamicLight : Actor double SpotInnerAngle; double SpotOuterAngle; double SoftShadowRadius; - double LightStrength; // used for Inverse-square falloff lights + double LightLinearity; // used for Inverse-square falloff lights private int lighttype; private int lightflags;