mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 19:24:32 +02:00
Reimplement lighting model
This commit is contained in:
parent
19e318c3f0
commit
399861087d
5 changed files with 60 additions and 26 deletions
|
@ -15,22 +15,26 @@ using namespace model;
|
|||
Light::Light() :
|
||||
_flags(0),
|
||||
_schemaBuffer(),
|
||||
_transform() {
|
||||
_transform(),
|
||||
_maximumRadius(1.0f) {
|
||||
// only if created from nothing shall we create the Buffer to store the properties
|
||||
Schema schema;
|
||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||
updateLightRadius();
|
||||
}
|
||||
|
||||
Light::Light(const Light& light) :
|
||||
_flags(light._flags),
|
||||
_schemaBuffer(light._schemaBuffer),
|
||||
_transform(light._transform) {
|
||||
_transform(light._transform),
|
||||
_maximumRadius(1.0f) {
|
||||
}
|
||||
|
||||
Light& Light::operator= (const Light& light) {
|
||||
_flags = (light._flags);
|
||||
_schemaBuffer = (light._schemaBuffer);
|
||||
_transform = (light._transform);
|
||||
_maximumRadius = (light._maximumRadius);
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
@ -70,21 +74,35 @@ void Light::setAmbientIntensity(float intensity) {
|
|||
editSchema()._ambientIntensity = intensity;
|
||||
}
|
||||
|
||||
void Light::setSurfaceRadius(float radius) {
|
||||
if (radius <= 0.0f) {
|
||||
radius = 0.1f;
|
||||
}
|
||||
editSchema()._attenuation.x = radius;
|
||||
updateLightRadius();
|
||||
}
|
||||
void Light::setMaximumRadius(float radius) {
|
||||
if (radius <= 0.f) {
|
||||
radius = 1.0f;
|
||||
}
|
||||
editSchema()._attenuation.w = radius;
|
||||
editSchema()._attenuation.y = radius;
|
||||
updateLightRadius();
|
||||
}
|
||||
|
||||
void Light::updateLightRadius() {
|
||||
const float CUTOFF_INTENSITY_RATIO = 0.05f;
|
||||
float intensity = getIntensity() * std::max(std::max(getColor().x, getColor().y), getColor().z);
|
||||
float denom = sqrtf(intensity / CUTOFF_INTENSITY_RATIO) - 1.0f;
|
||||
float surfaceRadius = getMaximumRadius() / std::max(denom, 1.0f);
|
||||
float maximumDistance = getMaximumRadius() - getSurfaceRadius();
|
||||
|
||||
editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CUTOFF_INTENSITY_RATIO, getMaximumRadius());
|
||||
float denom = maximumDistance / getSurfaceRadius() + 1;
|
||||
|
||||
// The cutoff intensity biases the light towards the source.
|
||||
// If the source is small and the intensity high, many points may not be shaded.
|
||||
// If the intensity is >=1.0, the lighting attenuation equation gets borked (see Light.slh).
|
||||
// To maintain sanity, we cap it well before then.
|
||||
const float MAX_CUTOFF_INTENSITY = 0.01f; // intensity = maximumRadius = 1.0f, surfaceRadius = 0.1f
|
||||
float cutoffIntensity = std::min(intensity / (denom * denom), MAX_CUTOFF_INTENSITY);
|
||||
|
||||
editSchema()._attenuation.z = cutoffIntensity;
|
||||
}
|
||||
|
||||
#include <math.h>
|
||||
|
|
|
@ -74,8 +74,17 @@ public:
|
|||
|
||||
bool isRanged() const { return (getType() == POINT) || (getType() == SPOT ); }
|
||||
|
||||
// Surface Radius is the physical radius of the light sphere through wich the light energy shines
|
||||
// It's expressed in meters and should be small for realistic lights since its in theory about the
|
||||
// size of a light bulb filament (~1cm = 0.01m)
|
||||
void setSurfaceRadius(float radius);
|
||||
float getSurfaceRadius() const { return getSchema()._attenuation.x; }
|
||||
|
||||
// Maximum radius defines the maximum distance of reach of our light model
|
||||
// This is where our radial attenuation will reach 0 (even though in theory it should be infinity)
|
||||
// If MaximumRadius < SurfaceRadius then the radial attenuation is constant at 100% Intensity
|
||||
void setMaximumRadius(float radius);
|
||||
float getMaximumRadius() const { return getSchema()._attenuation.w; }
|
||||
float getMaximumRadius() const { return getSchema()._attenuation.y; }
|
||||
|
||||
// Spot properties
|
||||
bool isSpot() const { return getType() == SPOT; }
|
||||
|
@ -107,7 +116,7 @@ public:
|
|||
float _ambientIntensity{0.0f};
|
||||
Color _color{1.0f};
|
||||
float _intensity{1.0f};
|
||||
Vec4 _attenuation{1.0f};
|
||||
Vec4 _attenuation{0.1f, 1.0f, 0.0f, 0.0f};
|
||||
Vec4 _spot{0.0f, 0.0f, 0.0f, 0.0f};
|
||||
Vec4 _shadow{0.0f};
|
||||
|
||||
|
@ -122,6 +131,7 @@ protected:
|
|||
UniformBufferView _schemaBuffer;
|
||||
Transform _transform;
|
||||
gpu::SphericalHarmonics _ambientSphere;
|
||||
float _maximumRadius;
|
||||
|
||||
const Schema& getSchema() const { return _schemaBuffer.get<Schema>(); }
|
||||
Schema& editSchema() { return _schemaBuffer.edit<Schema>(); }
|
||||
|
|
|
@ -29,15 +29,7 @@ vec3 getLightColor(Light l) { return l._color.rgb; }
|
|||
float getLightIntensity(Light l) { return l._color.w; }
|
||||
float getLightAmbientIntensity(Light l) { return l._direction.w; }
|
||||
|
||||
float evalLightAttenuation(Light l, float r) {
|
||||
float d = max(r - l._attenuation.x, 0.0);
|
||||
float denom = d * l._attenuation.y + 1.0;
|
||||
float attenuation = 1.0 / (denom * denom);
|
||||
return max((attenuation - l._attenuation.z)/(1.0 - l._attenuation.z), 0.0);
|
||||
// return clamp(1.0/(l._attenuation.x + l._attenuation.y * r + l._attenuation.z * r * r), 0.0, 1.0);
|
||||
}
|
||||
|
||||
float getLightSpotAngleCos(Light l) {
|
||||
float getLightSpotAngleCos(Light l) {
|
||||
return l._spot.x;
|
||||
}
|
||||
|
||||
|
@ -49,15 +41,19 @@ float evalLightSpotAttenuation(Light l, float cosA) {
|
|||
return pow(cosA, l._spot.w);
|
||||
}
|
||||
|
||||
float getLightSquareRadius(Light l) {
|
||||
return l._attenuation.w * l._attenuation.w;
|
||||
}
|
||||
|
||||
float getLightRadius(Light l) {
|
||||
return l._attenuation.w;
|
||||
return l._attenuation.x;
|
||||
}
|
||||
|
||||
float getLightAttenuationCutoff(Light l) {
|
||||
float getLightSquareRadius(Light l) {
|
||||
return getLightRadius(l) * getLightRadius(l);
|
||||
}
|
||||
|
||||
float getLightCutoffRadius(Light l) {
|
||||
return l._attenuation.y;
|
||||
}
|
||||
|
||||
float getLightCutoffIntensity(Light l) {
|
||||
return l._attenuation.z;
|
||||
}
|
||||
|
||||
|
@ -65,6 +61,16 @@ float getLightShowContour(Light l) {
|
|||
return l._control.w;
|
||||
}
|
||||
|
||||
float evalLightAttenuation(Light l, float r) {
|
||||
float radius = getLightRadius(l);
|
||||
float cutoff = getLightCutoffIntensity(l);
|
||||
float d = max(r - radius, 0.0);
|
||||
float denom = d/radius + 1.0;
|
||||
float attenuation = min(1.0, 1.0 / (denom * denom));
|
||||
// Scale attenuation using cutoff from maximum radius
|
||||
return max(0, (attenuation - cutoff) / (1 - cutoff));
|
||||
}
|
||||
|
||||
<@if GPU_FEATURE_PROFILE == GPU_CORE @>
|
||||
uniform lightBuffer {
|
||||
Light light;
|
||||
|
|
|
@ -48,7 +48,7 @@ void main(void) {
|
|||
vec3 fragLightVec = getLightPosition(light) - fragPos.xyz;
|
||||
|
||||
// Kill if too far from the light center
|
||||
if (dot(fragLightVec, fragLightVec) > getLightSquareRadius(light)) {
|
||||
if (dot(fragLightVec, fragLightVec) > getLightCutoffRadius(light)) {
|
||||
discard;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ void main(void) {
|
|||
vec3 fragLightVec = getLightPosition(light) - fragPos.xyz;
|
||||
|
||||
// Kill if too far from the light center
|
||||
if (dot(fragLightVec, fragLightVec) > getLightSquareRadius(light)) {
|
||||
if (dot(fragLightVec, fragLightVec) > getLightCutoffRadius(light)) {
|
||||
discard;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue