Skip to content

Commit

Permalink
Use linear light attenuation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Flone-dnb committed Nov 17, 2023
1 parent 3028046 commit b63cbfd
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 70 deletions.
29 changes: 13 additions & 16 deletions res/engine/shaders/include/Lighting.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@ struct PointLight{
/** Light intensity, valid values range is [0.0F; 1.0F]. */
float intensity;

/**
* Distance where the light intensity is half the maximal intensity,
* valid values range is [0.01F; +inf].
*/
float halfDistance;
/** Lit distance. */
float distance;

#hlsl float2 pad1; // to pack as in Vulkan
};
Expand Down Expand Up @@ -81,11 +78,8 @@ struct Spotlight{
/** Light intensity, valid values range is [0.0F; 1.0F]. */
float intensity;

/**
* Distance where the light intensity is half the maximal intensity,
* valid values range is [0.001F; +inf].
*/
float halfDistance;
/** Lit distance. */
float distance;

/**
* Cosine of the spotlight's inner cone angle (cutoff).
Expand Down Expand Up @@ -188,13 +182,16 @@ vec3 calculateColorFromLightSource(
*
* @param distanceToLightSource Distance between pixel/fragment and the light source.
* @param lightIntensity Light intensity, valid values range is [0.0F; 1.0F].
* @param lightHalfDistance Distance where the light intensity is half the maximal intensity, valid values range is [0.001F; +inf].
* @param lightDistance Lit distance.
*
* @return Factor in range [0.0; 1.0] where 0.0 means "no light is received" and 1.0 means "full light is received".
*/
float calculateLightAttenuation(float distanceToLightSource, float lightIntensity, float lightHalfDistance){
float distanceToLightDivHalfRadius = distanceToLightSource / lightHalfDistance;
return lightIntensity / (1.0F + distanceToLightDivHalfRadius * distanceToLightDivHalfRadius);
float calculateLightAttenuation(float distanceToLightSource, float lightIntensity, float lightDistance){
// Use linear attenuation because it allows us to do sphere/cone intersection tests in light culling
// more "efficient" in terms of light radius/distance to lit area. For example with linear attenuation if we have lit
// distance 30 almost all this distance will be somewhat lit while with "inverse squared distance" function (and similar)
// almost half of this distance will have pretty much no light.
return clamp((lightDistance - distanceToLightSource) / lightDistance, 0.0F, 1.0F) * lightIntensity;
}

/**
Expand All @@ -221,7 +218,7 @@ vec3 calculateColorFromPointLight(
// Calculate light attenuation.
float fragmentDistanceToLight = length(lightSource.position.xyz - fragmentPosition);
vec3 attenuatedLightColor = lightSource.color.rgb
* calculateLightAttenuation(fragmentDistanceToLight, lightSource.intensity, lightSource.halfDistance);
* calculateLightAttenuation(fragmentDistanceToLight, lightSource.intensity, lightSource.distance);

return calculateColorFromLightSource(
attenuatedLightColor,
Expand Down Expand Up @@ -293,7 +290,7 @@ vec3 calculateColorFromSpotlight(
// Calculate light attenuation.
float fragmentDistanceToLight = length(lightSource.position.xyz - fragmentPosition);
vec3 attenuatedLightColor = lightSource.color.rgb
* calculateLightAttenuation(fragmentDistanceToLight, lightSource.intensity, lightSource.halfDistance);
* calculateLightAttenuation(fragmentDistanceToLight, lightSource.intensity, lightSource.distance);

// Calculate angle between light direction and direction from fragment to light source
// to see if this fragment is inside of the light cone or not.
Expand Down
14 changes: 4 additions & 10 deletions src/engine_lib/private/game/nodes/light/PointLightNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace ne {
mtxShaderData.second.shaderData.position = glm::vec4(getWorldLocation(), 1.0F);
mtxShaderData.second.shaderData.color = glm::vec4(color, 1.0F);
mtxShaderData.second.shaderData.intensity = intensity;
mtxShaderData.second.shaderData.halfDistance = halfDistance;
mtxShaderData.second.shaderData.distance = distance;
#if defined(DEBUG)
static_assert(sizeof(PointLightShaderData) == 48, "consider copying new parameters here");
#endif
Expand Down Expand Up @@ -84,14 +84,11 @@ namespace ne {
markShaderDataToBeCopiedToGpu();
}

void PointLightNode::setLightHalfDistance(float halfDistance) {
void PointLightNode::setLightDistance(float distance) {
std::scoped_lock guard(mtxShaderData.first);

// Save new parameter.
this->halfDistance = std::max(halfDistance, minimumHalfDistance);

// Update shader data.
mtxShaderData.second.shaderData.halfDistance = this->halfDistance;
mtxShaderData.second.shaderData.distance = distance;

// Mark updated shader data to be later copied to the GPU resource.
markShaderDataToBeCopiedToGpu();
Expand All @@ -103,9 +100,6 @@ namespace ne {
// Make sure our intensity is in range [0.0; 1.0].
intensity = std::clamp(intensity, 0.0F, 1.0F);

// Make sure our half distance is not zero or negative.
halfDistance = std::max(halfDistance, minimumHalfDistance);

#if defined(DEBUG)
static_assert(sizeof(PointLightShaderData) == 48, "consider clamping new parameters here");
#endif
Expand Down Expand Up @@ -135,7 +129,7 @@ namespace ne {

float PointLightNode::getLightIntensity() const { return intensity; }

float PointLightNode::getLightHalfDistance() const { return halfDistance; }
float PointLightNode::getLightDistance() const { return distance; }

void PointLightNode::onWorldLocationRotationScaleChanged() {
SpatialNode::onWorldLocationRotationScaleChanged();
Expand Down
14 changes: 4 additions & 10 deletions src/engine_lib/private/game/nodes/light/SpotlightNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace ne {
mtxShaderData.second.shaderData.direction = glm::vec4(getWorldForwardDirection(), 0.0F);
mtxShaderData.second.shaderData.color = glm::vec4(color, 1.0F);
mtxShaderData.second.shaderData.intensity = intensity;
mtxShaderData.second.shaderData.halfDistance = halfDistance;
mtxShaderData.second.shaderData.distance = distance;
mtxShaderData.second.shaderData.cosInnerConeAngle =
glm::cos(glm::radians(innerConeAngle / 2.0F)); // NOLINT
mtxShaderData.second.shaderData.cosOuterConeAngle =
Expand Down Expand Up @@ -89,14 +89,11 @@ namespace ne {
markShaderDataToBeCopiedToGpu();
}

void SpotlightNode::setLightHalfDistance(float halfDistance) {
void SpotlightNode::setLightDistance(float distance) {
std::scoped_lock guard(mtxShaderData.first);

// Save new parameter.
this->halfDistance = std::max(halfDistance, minimumHalfDistance);

// Update shader data.
mtxShaderData.second.shaderData.halfDistance = this->halfDistance;
mtxShaderData.second.shaderData.distance = distance;

// Mark updated shader data to be later copied to the GPU resource.
markShaderDataToBeCopiedToGpu();
Expand Down Expand Up @@ -143,9 +140,6 @@ namespace ne {
// Make sure our intensity is in range [0.0; 1.0].
intensity = std::clamp(intensity, 0.0F, 1.0F);

// Make sure our half distance is not zero or negative.
halfDistance = std::max(halfDistance, minimumHalfDistance);

// Make sure our cutoff angle is in range [0.0; 180.0].
innerConeAngle = std::clamp(innerConeAngle, 0.0F, 180.0F); // NOLINT
outerConeAngle = std::clamp(outerConeAngle, innerConeAngle, 180.0F); // NOLINT
Expand Down Expand Up @@ -179,7 +173,7 @@ namespace ne {

float SpotlightNode::getLightIntensity() const { return intensity; }

float SpotlightNode::getLightHalfDistance() const { return halfDistance; }
float SpotlightNode::getLightDistance() const { return distance; }

void SpotlightNode::onWorldLocationRotationScaleChanged() {
SpatialNode::onWorldLocationRotationScaleChanged();
Expand Down
27 changes: 10 additions & 17 deletions src/engine_lib/public/game/nodes/light/PointLightNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ namespace ne RNAMESPACE() {
void setLightIntensity(float intensity);

/**
* Sets distance where the light intensity is half the maximum intensity (see @ref setLightIntensity).
* Sets lit distance (i.e. attenuation radius).
*
* @param halfDistance Distance where intensity is half the maximum in range is [0.0; +inf]
* (will be clamped of outside of the range).
* @param distance Lit distance.
*/
void setLightHalfDistance(float halfDistance);
void setLightDistance(float distance);

/**
* Returns color of this light source.
Expand All @@ -60,11 +59,11 @@ namespace ne RNAMESPACE() {
float getLightIntensity() const;

/**
* Returns distance where the light intensity is half the maximum intensity.
* Returns lit distance.
*
* @return Distance in range [0.0; +inf].
* @return Distance.
*/
float getLightHalfDistance() const;
float getLightDistance() const;

protected:
/**
Expand Down Expand Up @@ -124,8 +123,8 @@ namespace ne RNAMESPACE() {
/** Light intensity. */
alignas(iVkScalarAlignment) float intensity = 1.0F;

/** Distance where intensity is half the maximum. */
alignas(iVkScalarAlignment) float halfDistance = 5.0F; // NOLINT
/** Lit distance. */
alignas(iVkScalarAlignment) float distance = 1.0F;
};

/** Groups data related to shaders. */
Expand Down Expand Up @@ -170,15 +169,9 @@ namespace ne RNAMESPACE() {
RPROPERTY(Serialize)
float intensity = 1.0F;

/**
* Distance where the light intensity is half the maximal intensity,
* valid values range is [@ref minimumHalfDistance; +inf].
*/
/** Lit distance. */
RPROPERTY(Serialize)
float halfDistance = 5.0F; // NOLINT: seems like a pretty good default value

/** Minimum value for @ref halfDistance to avoid division by zero in shaders. */
static constexpr float minimumHalfDistance = 0.001F; // NOLINT
float distance = 10.0F; // NOLINT: seems like a pretty good default value

ne_PointLightNode_GENERATED
};
Expand Down
27 changes: 10 additions & 17 deletions src/engine_lib/public/game/nodes/light/SpotlightNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ namespace ne RNAMESPACE() {
void setLightIntensity(float intensity);

/**
* Sets distance where the light intensity is half the maximum intensity (see @ref setLightIntensity).
* Sets lit distance (i.e. attenuation distance).
*
* @param halfDistance Distance where intensity is half the maximum in range is [0.0; +inf]
* (will be clamped of outside of the range).
* @param distance Lit distance.
*/
void setLightHalfDistance(float halfDistance);
void setLightDistance(float distance);

/**
* Sets angle of spotlight's inner cone (cone that will have hard light edges),
Expand Down Expand Up @@ -78,11 +77,11 @@ namespace ne RNAMESPACE() {
float getLightIntensity() const;

/**
* Returns distance where the light intensity is half the maximum intensity.
* Returns lit distance.
*
* @return Distance in range [0.0; +inf].
* @return Distance.
*/
float getLightHalfDistance() const;
float getLightDistance() const;

/**
* Returns light cutoff angle of the inner cone (hard light edge).
Expand Down Expand Up @@ -159,8 +158,8 @@ namespace ne RNAMESPACE() {
/** Light intensity. */
alignas(iVkScalarAlignment) float intensity = 1.0F;

/** Distance where intensity is half the maximum. */
alignas(iVkScalarAlignment) float halfDistance = 5.0F; // NOLINT
/** Lit distance. */
alignas(iVkScalarAlignment) float distance = 1.0F;

/**
* Cosine of the spotlight's inner cone angle (cutoff).
Expand Down Expand Up @@ -221,12 +220,9 @@ namespace ne RNAMESPACE() {
RPROPERTY(Serialize)
float intensity = 1.0F;

/**
* Distance where the light intensity is half the maximal intensity,
* valid values range is [@ref minimumHalfDistance; +inf].
*/
/** Lit distance. */
RPROPERTY(Serialize)
float halfDistance = 5.0F; // NOLINT: seems like a pretty good default value
float distance = 10.0F; // NOLINT: seems like a pretty good default value

/**
* Light cutoff angle (in degrees) of the inner cone (hard light edge).
Expand All @@ -242,9 +238,6 @@ namespace ne RNAMESPACE() {
RPROPERTY(Serialize)
float outerConeAngle = 120.0F;

/** Minimum value for @ref halfDistance to avoid division by zero in shaders. */
static constexpr float minimumHalfDistance = 0.001F; // NOLINT

ne_SpotlightNode_GENERATED
};
}
Expand Down

0 comments on commit b63cbfd

Please sign in to comment.