Reimplement lighting model

This commit is contained in:
Zach Pomerantz 2016-02-10 19:50:31 -08:00
parent 19e318c3f0
commit 399861087d
5 changed files with 60 additions and 26 deletions

View file

@ -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>

View file

@ -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>(); }

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}