improving on the poin light attenuatiion formula

This commit is contained in:
Sam Gateau 2015-01-28 00:09:15 -08:00
parent b9bbd7745f
commit b6836af071
6 changed files with 64 additions and 44 deletions

View file

@ -40,25 +40,19 @@ Light::~Light() {
void Light::setPosition(const Vec3& position) { void Light::setPosition(const Vec3& position) {
_transform.setTranslation(position); _transform.setTranslation(position);
_transform.getMatrix(editSchema()._transform); editSchema()._position = Vec4(position, 1.f);
} }
void Light::setOrientation(const glm::quat& orientation) { void Light::setOrientation(const glm::quat& orientation) {
_transform.setRotation(orientation); _transform.setRotation(orientation);
_transform.getMatrix(editSchema()._transform);
} }
void Light::setDirection(const Vec3& direction) { void Light::setDirection(const Vec3& direction) {
glm::mat3 rotation( editSchema()._direction = Vec4(direction, 0.f);
Vec3(1.0f, 0.0f, 0.0f),
Vec3(0.0f, 1.0f, 0.0f),
-direction);
setOrientation(glm::quat(rotation));
} }
const Vec3& Light::getDirection() const { const Vec3& Light::getDirection() const {
return Vec3(_transform.transform(Vec4(0.0f, 0.0f, 1.0f, 0.0f))); return Vec3(getSchema()._direction);
} }
void Light::setColor(const Color& color) { void Light::setColor(const Color& color) {
@ -69,11 +63,13 @@ void Light::setIntensity(float intensity) {
editSchema()._intensity = intensity; editSchema()._intensity = intensity;
} }
void Light::setAttenuationRadius(float radius) { void Light::setMaximumRadius(float radius) {
if (radius <= 0.f) { if (radius <= 0.f) {
radius = 1.0f; radius = 1.0f;
} }
editSchema()._attenuation = Vec4(1.0f, 2.0f/radius, 1.0f/(radius*radius), radius); float CutOffIntensityRatio = 0.01f;
float surfaceRadius = radius / (sqrt(1.0f / CutOffIntensityRatio) - 1.f);
editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, radius);
} }
void Light::setSpotCone(float angle) { void Light::setSpotCone(float angle) {

View file

@ -74,8 +74,8 @@ public:
bool isRanged() const { return (getType() == POINT) || (getType() == SPOT ); } bool isRanged() const { return (getType() == POINT) || (getType() == SPOT ); }
void setAttenuationRadius(float radius); void setMaximumRadius(float radius);
float getAttenuationRadius() const { return getSchema()._attenuation.w; } float getMaximumRadius() const { return getSchema()._attenuation.w; }
// Spot properties // Spot properties
bool isSpot() const { return getType() == SPOT; } bool isSpot() const { return getType() == SPOT; }
@ -87,7 +87,8 @@ public:
// Schema to access the attribute values of the light // Schema to access the attribute values of the light
class Schema { class Schema {
public: public:
glm::mat4 _transform; Vec4 _position;
Vec4 _direction;
Color _color; Color _color;
float _intensity; float _intensity;
Vec4 _attenuation; Vec4 _attenuation;
@ -97,10 +98,11 @@ public:
Vec4 _control; Vec4 _control;
Schema() : Schema() :
_transform(), _position(0.0f, 0.0f, 0.0f, 1.0f),
_direction(0.0f, 0.0f, -1.0f, 0.f),
_color(1.0f), _color(1.0f),
_intensity(1.0f), _intensity(1.0f),
_attenuation(1.0f, 2.0f, 1.0f, 1.0f), _attenuation(1.0f, 1.0f, 1.0f, 1.0f),
_spot(0.0f, 0.0f, 0.0f, 0.0f), _spot(0.0f, 0.0f, 0.0f, 0.0f),
_control(0.0f) _control(0.0f)
{} {}

View file

@ -275,7 +275,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
model::LightPointer lp = model::LightPointer(new model::Light()); model::LightPointer lp = model::LightPointer(new model::Light());
lp->setPosition(position); lp->setPosition(position);
lp->setAttenuationRadius(radius); lp->setMaximumRadius(radius);
lp->setColor(diffuse); lp->setColor(diffuse);
lp->setIntensity(1.0f); lp->setIntensity(1.0f);
lp->setType(model::Light::POINT); lp->setType(model::Light::POINT);
@ -297,7 +297,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
model::LightPointer ls = model::LightPointer(new model::Light()); model::LightPointer ls = model::LightPointer(new model::Light());
ls->setPosition(position); ls->setPosition(position);
ls->setDirection(direction); ls->setDirection(direction);
ls->setAttenuationRadius(radius); ls->setMaximumRadius(radius);
ls->setSpotCone(cutoff); ls->setSpotCone(cutoff);
ls->setColor(diffuse); ls->setColor(diffuse);
ls->setIntensity(1.0f); ls->setIntensity(1.0f);
@ -453,6 +453,8 @@ void DeferredLightingEffect::render() {
const glm::vec3& eyePoint = _viewState->getCurrentViewFrustum()->getPosition(); const glm::vec3& eyePoint = _viewState->getCurrentViewFrustum()->getPosition();
float nearRadius = glm::distance(eyePoint, _viewState->getCurrentViewFrustum()->getNearTopLeft()); float nearRadius = glm::distance(eyePoint, _viewState->getCurrentViewFrustum()->getNearTopLeft());
glm::mat4 invViewMat;
_viewState->getViewTransform().getMatrix(invViewMat);
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
@ -470,7 +472,9 @@ void DeferredLightingEffect::render() {
batch.setUniformBuffer(_pointLightLocations.lightBufferUnit, light->getSchemaBuffer()); batch.setUniformBuffer(_pointLightLocations.lightBufferUnit, light->getSchemaBuffer());
gpu::GLBackend::renderBatch(batch); gpu::GLBackend::renderBatch(batch);
} }
_spotLight.setUniformValue(_pointLightLocations.radius, light->getAttenuationRadius()); glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
// _pointLight.setUniformValue(_pointLightLocations.viewMat, reinterpret_cast< const GLfloat* >(&viewMat));
// _spotLight.setUniformValue(_pointLightLocations.radius, light->getAttenuationRadius());
/* /*
@ -485,7 +489,7 @@ void DeferredLightingEffect::render() {
*/ */
glPushMatrix(); glPushMatrix();
float expandedRadius = light->getAttenuationRadius() * (1.0f + SCALE_EXPANSION); float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) { if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) {
glLoadIdentity(); glLoadIdentity();
glTranslatef(0.0f, 0.0f, -1.0f); glTranslatef(0.0f, 0.0f, -1.0f);
@ -525,7 +529,7 @@ void DeferredLightingEffect::render() {
batch.setUniformBuffer(_spotLightLocations.lightBufferUnit, light->getSchemaBuffer()); batch.setUniformBuffer(_spotLightLocations.lightBufferUnit, light->getSchemaBuffer());
gpu::GLBackend::renderBatch(batch); gpu::GLBackend::renderBatch(batch);
} }
_spotLight.setUniformValue(_spotLightLocations.radius, light->getAttenuationRadius()); // _spotLight.setUniformValue(_spotLightLocations.radius, light->getAttenuationRadius());
/* /*
_spotLight.setUniformValue(_spotLightLocations.radius, light.radius); _spotLight.setUniformValue(_spotLightLocations.radius, light.radius);
@ -543,7 +547,7 @@ void DeferredLightingEffect::render() {
glPushMatrix(); glPushMatrix();
float expandedRadius = light->getAttenuationRadius() * (1.0f + SCALE_EXPANSION); float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
float edgeRadius = expandedRadius / glm::cos(light->getSpotConeAngle()); float edgeRadius = expandedRadius / glm::cos(light->getSpotConeAngle());
if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < edgeRadius + nearRadius) { if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < edgeRadius + nearRadius) {
glLoadIdentity(); glLoadIdentity();
@ -563,7 +567,7 @@ void DeferredLightingEffect::render() {
glm::quat spotRotation = rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), light->getDirection()); glm::quat spotRotation = rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), light->getDirection());
glm::vec3 axis = glm::axis(spotRotation); glm::vec3 axis = glm::axis(spotRotation);
glRotatef(glm::degrees(glm::angle(spotRotation)), axis.x, axis.y, axis.z); glRotatef(glm::degrees(glm::angle(spotRotation)), axis.x, axis.y, axis.z);
glTranslatef(0.0f, 0.0f, -light->getAttenuationRadius() * (1.0f + SCALE_EXPANSION * 0.5f)); glTranslatef(0.0f, 0.0f, -light->getMaximumRadius() * (1.0f + SCALE_EXPANSION * 0.5f));
geometryCache->renderCone(expandedRadius * glm::tan(light->getSpotConeAngle()), geometryCache->renderCone(expandedRadius * glm::tan(light->getSpotConeAngle()),
expandedRadius, 32, 1); expandedRadius, 32, 1);
} }
@ -649,6 +653,7 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit
locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale"); locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale");
locations.radius = program.uniformLocation("radius"); locations.radius = program.uniformLocation("radius");
locations.ambientSphere = program.uniformLocation("ambientSphere.L00"); locations.ambientSphere = program.uniformLocation("ambientSphere.L00");
locations.invViewMat = program.uniformLocation("invViewMat");
GLint loc = -1; GLint loc = -1;
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
@ -661,8 +666,8 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit
#elif defined(Q_OS_WIN) #elif defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "lightBuffer"); loc = glGetUniformBlockIndex(program.programId(), "lightBuffer");
if (loc >= 0) { if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, 1); glUniformBlockBinding(program.programId(), loc, 0);
locations.lightBufferUnit = 1; locations.lightBufferUnit = 0;
} else { } else {
locations.lightBufferUnit = -1; locations.lightBufferUnit = -1;
} }

View file

@ -105,6 +105,7 @@ private:
int radius; int radius;
int ambientSphere; int ambientSphere;
int lightBufferUnit; int lightBufferUnit;
int invViewMat;
}; };
static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations); static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations);

View file

@ -12,8 +12,8 @@
<@def LIGHT_SLH@> <@def LIGHT_SLH@>
struct Light { struct Light {
mat4 _transform; vec4 _position;
vec4 _direction;
vec4 _color; vec4 _color;
vec4 _attenuation; vec4 _attenuation;
vec4 _spot; vec4 _spot;
@ -23,14 +23,18 @@ struct Light {
vec4 _control; vec4 _control;
}; };
vec3 getLightPosition(Light l) { return l._transform[3].xyz; } vec3 getLightPosition(Light l) { return l._position.xyz; }
vec3 getLightDirection(Light l) { return -l._transform[2].xyz; } // direction is -Z axis vec3 getLightDirection(Light l) { return l._direction.xyz; } // direction is -Z axis
vec3 getLightColor(Light l) { return l._color.rgb; } vec3 getLightColor(Light l) { return l._color.rgb; }
float getLightIntensity(Light l) { return l._color.w; } float getLightIntensity(Light l) { return l._color.w; }
float evalLightAttenuation(Light l, float r) { float evalLightAttenuation(Light l, float r) {
return clamp(1.0/(l._attenuation.x + l._attenuation.y * r + l._attenuation.z * r * r), 0.0, 1.0); 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 evalLightSpotAttenuation(Light l, float cosA) { float evalLightSpotAttenuation(Light l, float cosA) {
@ -40,6 +44,9 @@ float evalLightSpotAttenuation(Light l, float cosA) {
return clamp(pow(cosA / l._spot.x, l._spot.w), 0.0, 1.0); return clamp(pow(cosA / l._spot.x, l._spot.w), 0.0, 1.0);
} }
float getLightSquareRadius(Light l) {
return l._attenuation.w * l._attenuation.w;
}
<@if GLPROFILE == PC_GL@> <@if GLPROFILE == PC_GL@>

View file

@ -21,6 +21,8 @@
// the radius (hard cutoff) of the light effect // the radius (hard cutoff) of the light effect
uniform float radius; uniform float radius;
uniform mat4 invViewMat;
void main(void) { void main(void) {
// get the depth and exit early if it doesn't pass the test // get the depth and exit early if it doesn't pass the test
vec2 texCoord = gl_TexCoord[0].st / gl_TexCoord[0].q; vec2 texCoord = gl_TexCoord[0].st / gl_TexCoord[0].q;
@ -32,18 +34,25 @@ void main(void) {
discard; discard;
} }
// compute the view space position using the depth vec4 wPos;
float z = near / (depth * depthScale - 1.0); wPos = invViewMat * frag.position;
vec4 position = vec4((depthTexCoordOffset + texCoord * depthTexCoordScale) * z, z, 1.0); Light light = getLight();
gl_FragColor = vec4(frag.normal, 0.0);
// get the normal from the map vec3 lightVector = getLightPosition(light) - wPos.xyz;
vec4 normal = texture2D(normalMap, texCoord); if (dot(lightVector, lightVector) > getLightSquareRadius(light)) {
vec4 normalizedNormal = normalize(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0)); discard;
}
// compute the base color based on OpenGL lighting model
vec4 lightVector = gl_LightSource[1].position - position;
float lightDistance = length(lightVector); float lightDistance = length(lightVector);
lightVector = lightVector / lightDistance; lightVector = lightVector / lightDistance;
float lightAttenuation = evalLightAttenuation(light, lightDistance);
gl_FragColor.xyz *= lightAttenuation;
/*
float diffuse = dot(frag.normal, lightVector);
float diffuse = dot(normalizedNormal, lightVector); float diffuse = dot(normalizedNormal, lightVector);
float facingLight = step(0.0, diffuse); float facingLight = step(0.0, diffuse);
vec4 baseColor = texture2D(diffuseMap, texCoord) * (gl_FrontLightProduct[1].ambient + vec4 baseColor = texture2D(diffuseMap, texCoord) * (gl_FrontLightProduct[1].ambient +
@ -59,6 +68,6 @@ void main(void) {
normalizedNormal)); normalizedNormal));
vec4 specularColor = texture2D(specularMap, texCoord); vec4 specularColor = texture2D(specularMap, texCoord);
gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0); gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0);
*/
gl_FragColor = vec4(frag.normal, 0.0); // gl_FragColor = vec4(frag.normal, 0.0);
} }