mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
More accurate & controllable pointlight attenuation (#6160)
* modify light attenuation function * support for changing attenuation curve type + lots of docs * this is what i defaulted to typing in a prototype, so i guess it should just be this instead * Allow a continuous range of values between inverse and inversequadratic rather than two set curves * calc is slang for calculator * fix * oops committed it at 1 while testing i think, values are balanced for 0
This commit is contained in:
@@ -14,6 +14,8 @@ uniform highp vec2 lightCenter;
|
||||
uniform highp float lightRange;
|
||||
uniform highp float lightPower;
|
||||
uniform highp float lightSoftness;
|
||||
uniform highp float lightFalloff;
|
||||
uniform highp float lightCurveFactor;
|
||||
uniform highp float lightIndex;
|
||||
uniform sampler2D shadowMap;
|
||||
|
||||
@@ -47,8 +49,15 @@ void fragment()
|
||||
discard;
|
||||
}
|
||||
|
||||
highp float dist = dot(diff, diff) + LIGHTING_HEIGHT;
|
||||
highp float val = clamp((1.0 - clamp(sqrt(dist) / lightRange, 0.0, 1.0)) * (1.0 / (sqrt(dist + 1.0))), 0.0, 1.0);
|
||||
// this implementation of light attenuation primarily adapted from
|
||||
// https://lisyarus.github.io/blog/posts/point-light-attenuation.html
|
||||
highp float sqr_dist = dot(diff, diff) + LIGHTING_HEIGHT;
|
||||
|
||||
highp float s = clamp(sqrt(sqr_dist) / lightRange, 0.0, 1.0);
|
||||
highp float s2 = s * s;
|
||||
// controls curve by lerping between two variants (inverse-shape and inversequadratic-shape)
|
||||
highp float curveFactor = mix(s, s2, clamp(lightCurveFactor, 0.0, 1.0));
|
||||
highp float val = clamp(((1 - s2) * (1 - s2)) / (1 + lightFalloff * curveFactor), 0.0, 1.0);
|
||||
|
||||
val *= lightPower;
|
||||
val *= mask;
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Robust.Client.GameObjects
|
||||
component.Enabled = state.Enabled;
|
||||
component.Offset = state.Offset;
|
||||
component.Softness = state.Softness;
|
||||
component.Falloff = state.Falloff;
|
||||
component.CurveFactor = state.CurveFactor;
|
||||
component.CastShadows = state.CastShadows;
|
||||
component.Energy = state.Energy;
|
||||
component.Radius = state.Radius;
|
||||
|
||||
@@ -451,6 +451,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var lastPower = float.NaN;
|
||||
var lastColor = new Color(float.NaN, float.NaN, float.NaN, float.NaN);
|
||||
var lastSoftness = float.NaN;
|
||||
var lastFalloff = float.NaN;
|
||||
var lastCurveFactor = float.NaN;
|
||||
Texture? lastMask = null;
|
||||
|
||||
using (_prof.Group("Draw Lights"))
|
||||
@@ -504,6 +506,18 @@ namespace Robust.Client.Graphics.Clyde
|
||||
lightShader.SetUniformMaybe("lightSoftness", lastSoftness);
|
||||
}
|
||||
|
||||
if (!MathHelper.CloseToPercent(lastFalloff, component.Falloff))
|
||||
{
|
||||
lastFalloff = component.Falloff;
|
||||
lightShader.SetUniformMaybe("lightFalloff", lastFalloff);
|
||||
}
|
||||
|
||||
if (!MathHelper.CloseToPercent(lastCurveFactor, component.CurveFactor))
|
||||
{
|
||||
lastCurveFactor = component.CurveFactor;
|
||||
lightShader.SetUniformMaybe("lightCurveFactor", lastCurveFactor);
|
||||
}
|
||||
|
||||
lightShader.SetUniformMaybe("lightCenter", lightPos);
|
||||
lightShader.SetUniformMaybe("lightIndex",
|
||||
component.CastShadows ? (i + 0.5f) / ShadowTexture.Height : -1);
|
||||
|
||||
@@ -14,6 +14,10 @@ public sealed class PointLightComponentState : ComponentState
|
||||
|
||||
public float Softness;
|
||||
|
||||
public float Falloff;
|
||||
|
||||
public float CurveFactor;
|
||||
|
||||
public bool CastShadows;
|
||||
|
||||
public bool Enabled;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
@@ -33,6 +34,39 @@ namespace Robust.Shared.GameObjects
|
||||
[DataField("softness"), Animatable]
|
||||
public float Softness { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls how quickly the light falls off in power in its radius.
|
||||
/// A higher value means a stronger falloff.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value of 6.8 might seem suspect, but that's because this is a value which was introduced
|
||||
/// years after SS14 already standardized its light values using an older attenuation curve, and this was the value
|
||||
/// which, qualitatively, seemed about equivalent in brightness for the large majority of lights on the station
|
||||
/// compared to the old function.
|
||||
///
|
||||
/// See https://www.desmos.com/calculator/yjudaha0s6 for a demonstration of how this value affects the shape of the curve
|
||||
/// for different light radii and curve factors.
|
||||
/// </remarks>
|
||||
[DataField, Animatable]
|
||||
public float Falloff { get; set; } = 6.8f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls the shape of the curve used for point light attenuation.
|
||||
/// This value may vary between 0 and 1.
|
||||
/// A value of 0 gives a shape roughly equivalent to 1/1+distance (more or less realistic),
|
||||
/// while a value of 1 gives a shape roughly equivalent to 1+distance^2 (closer to a sphere-shaped light)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does not directly control the exponent of the denominator, though it might seem that way.
|
||||
/// Rather, it just lerps between an inverse-shaped curve and an inverse-quadratic-shaped curve.
|
||||
/// Values below 0 or above 1 are nonsensical.
|
||||
///
|
||||
/// See https://www.desmos.com/calculator/yjudaha0s6 for a demonstration of how this value affects the shape of the curve
|
||||
/// for different light radii and falloff values.
|
||||
/// </remarks>
|
||||
[DataField, Animatable]
|
||||
public float CurveFactor { get; set; } = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this pointlight should cast shadows
|
||||
/// </summary>
|
||||
|
||||
@@ -89,6 +89,24 @@ public abstract class SharedPointLightSystem : EntitySystem
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
|
||||
public void SetFalloff(EntityUid uid, float value, SharedPointLightComponent? comp = null)
|
||||
{
|
||||
if (!ResolveLight(uid, ref comp) || MathHelper.CloseToPercent(comp.Falloff, value))
|
||||
return;
|
||||
|
||||
comp.Falloff = value;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
|
||||
public void SetCurveFactor(EntityUid uid, float value, SharedPointLightComponent? comp = null)
|
||||
{
|
||||
if (!ResolveLight(uid, ref comp) || MathHelper.CloseToPercent(comp.CurveFactor, value))
|
||||
return;
|
||||
|
||||
comp.CurveFactor = value;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
|
||||
protected static void OnLightGetState(
|
||||
EntityUid uid,
|
||||
SharedPointLightComponent component,
|
||||
@@ -102,6 +120,8 @@ public abstract class SharedPointLightSystem : EntitySystem
|
||||
Offset = component.Offset,
|
||||
Radius = component.Radius,
|
||||
Softness = component.Softness,
|
||||
Falloff = component.Falloff,
|
||||
CurveFactor = component.CurveFactor,
|
||||
CastShadows = component.CastShadows,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user