From b9ae005e53da18080f0055c379215f9596f807f9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 18 Sep 2014 16:12:46 -0700 Subject: [PATCH] Working on the actual deferred lights. --- .../resources/shaders/deferred_light.vert | 17 +++ interface/resources/shaders/point_light.frag | 69 ++++++++++ interface/resources/shaders/spot_light.frag | 71 +++++++++++ .../src/renderer/DeferredLightingEffect.cpp | 119 ++++++++++++++++-- .../src/renderer/DeferredLightingEffect.h | 39 ++++++ interface/src/renderer/Model.cpp | 12 +- interface/src/renderer/Model.h | 3 - 7 files changed, 308 insertions(+), 22 deletions(-) create mode 100644 interface/resources/shaders/deferred_light.vert create mode 100644 interface/resources/shaders/point_light.frag create mode 100644 interface/resources/shaders/spot_light.frag diff --git a/interface/resources/shaders/deferred_light.vert b/interface/resources/shaders/deferred_light.vert new file mode 100644 index 0000000000..88076165a7 --- /dev/null +++ b/interface/resources/shaders/deferred_light.vert @@ -0,0 +1,17 @@ +#version 120 + +// +// deferred_light.vert +// vertex shader +// +// Created by Andrzej Kapolka on 9/18/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +void main(void) { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = gl_Vertex; +} diff --git a/interface/resources/shaders/point_light.frag b/interface/resources/shaders/point_light.frag new file mode 100644 index 0000000000..31de0fe7d2 --- /dev/null +++ b/interface/resources/shaders/point_light.frag @@ -0,0 +1,69 @@ +#version 120 + +// +// spot_light.frag +// fragment shader +// +// Created by Andrzej Kapolka on 9/18/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the normal texture +uniform sampler2D normalMap; + +// the specular texture +uniform sampler2D specularMap; + +// the depth texture +uniform sampler2D depthMap; + +// the distance to the near clip plane +uniform float near; + +// scale factor for depth: (far - near) / far +uniform float depthScale; + +// offset for depth texture coordinates +uniform vec2 depthTexCoordOffset; + +// scale for depth texture coordinates +uniform vec2 depthTexCoordScale; + +// the radius (hard cutoff) of the light effect +uniform float radius; + +void main(void) { + // compute the view space position using the depth + float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0); + vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0); + + // get the normal from the map + vec4 normal = texture2D(normalMap, gl_TexCoord[0].st); + vec4 normalizedNormal = normalize(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0)); + + // compute the base color based on OpenGL lighting model + vec4 lightVector = gl_LightSource[1].position - position; + float lightDistance = length(lightVector); + lightVector = lightVector / lightDistance; + float diffuse = dot(normalizedNormal, lightVector); + float facingLight = step(0.0, diffuse); + vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightProduct[1].ambient + + gl_FrontLightProduct[1].diffuse * (diffuse * facingLight)); + + // compute attenuation based on distance, etc. + float attenuation = step(lightDistance, radius) / dot(vec3(gl_LightSource[1].constantAttenuation, + gl_LightSource[1].linearAttenuation, gl_LightSource[1].quadraticAttenuation), + vec3(1.0, lightDistance, lightDistance * lightDistance)); + + // add base to specular, modulate by attenuation + float specular = facingLight * max(0.0, dot(normalize(lightVector - normalize(vec4(position.xyz, 0.0))), + normalizedNormal)); + vec4 specularColor = texture2D(specularMap, gl_TexCoord[0].st); + gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0); +} diff --git a/interface/resources/shaders/spot_light.frag b/interface/resources/shaders/spot_light.frag new file mode 100644 index 0000000000..33469be0c7 --- /dev/null +++ b/interface/resources/shaders/spot_light.frag @@ -0,0 +1,71 @@ +#version 120 + +// +// spot_light.frag +// fragment shader +// +// Created by Andrzej Kapolka on 9/18/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the normal texture +uniform sampler2D normalMap; + +// the specular texture +uniform sampler2D specularMap; + +// the depth texture +uniform sampler2D depthMap; + +// the distance to the near clip plane +uniform float near; + +// scale factor for depth: (far - near) / far +uniform float depthScale; + +// offset for depth texture coordinates +uniform vec2 depthTexCoordOffset; + +// scale for depth texture coordinates +uniform vec2 depthTexCoordScale; + +// the radius (hard cutoff) of the light effect +uniform float radius; + +void main(void) { + // compute the view space position using the depth + float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0); + vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0); + + // get the normal from the map + vec4 normal = texture2D(normalMap, gl_TexCoord[0].st); + vec4 normalizedNormal = normalize(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0)); + + // compute the base color based on OpenGL lighting model + vec4 lightVector = gl_LightSource[1].position - position; + float lightDistance = length(lightVector); + lightVector = lightVector / lightDistance; + float diffuse = dot(normalizedNormal, lightVector); + float facingLight = step(0.0, diffuse); + vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightProduct[1].ambient + + gl_FrontLightProduct[1].diffuse * (diffuse * facingLight)); + + // compute attenuation based on spot angle, distance, etc. + float cosSpotAngle = max(-dot(lightVector.xyz, gl_LightSource[1].spotDirection), 0.0); + float attenuation = step(lightDistance, radius) * step(gl_LightSource[1].spotCosCutoff, cosSpotAngle) * + pow(cosSpotAngle, gl_LightSource[1].spotExponent) / dot(vec3(gl_LightSource[1].constantAttenuation, + gl_LightSource[1].linearAttenuation, gl_LightSource[1].quadraticAttenuation), + vec3(1.0, lightDistance, lightDistance * lightDistance)); + + // add base to specular, modulate by attenuation + float specular = facingLight * max(0.0, dot(normalize(lightVector - normalize(vec4(position.xyz, 0.0))), + normalizedNormal)); + vec4 specularColor = texture2D(specularMap, gl_TexCoord[0].st); + gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0); +} diff --git a/interface/src/renderer/DeferredLightingEffect.cpp b/interface/src/renderer/DeferredLightingEffect.cpp index 48fbf30898..198cb50f13 100644 --- a/interface/src/renderer/DeferredLightingEffect.cpp +++ b/interface/src/renderer/DeferredLightingEffect.cpp @@ -32,6 +32,8 @@ void DeferredLightingEffect::init() { _directionalLightShadowMapLocations); loadLightProgram("shaders/directional_light_cascaded_shadow_map.frag", _directionalLightCascadedShadowMap, _directionalLightCascadedShadowMapLocations); + loadLightProgram("shaders/point_light.frag", _pointLight, _pointLightLocations); + loadLightProgram("shaders/spot_light.frag", _spotLight, _spotLightLocations); } void DeferredLightingEffect::bindSimpleProgram() { @@ -71,6 +73,42 @@ void DeferredLightingEffect::renderWireCube(float size) { releaseSimpleProgram(); } +void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& ambient, + const glm::vec3& diffuse, const glm::vec3& specular, float constantAttenuation, + float linearAttenuation, float quadraticAttenuation) { + addSpotLight(position, radius, ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation); +} + +void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& ambient, + const glm::vec3& diffuse, const glm::vec3& specular, float constantAttenuation, float linearAttenuation, + float quadraticAttenuation, const glm::vec3& direction, float exponent, float cutoff) { + if (exponent == 0.0f && cutoff == PI) { + PointLight light; + light.position = glm::vec4(position, 1.0f); + light.radius = radius; + light.ambient = glm::vec4(ambient, 1.0f); + light.diffuse = glm::vec4(diffuse, 1.0f); + light.specular = glm::vec4(specular, 1.0f); + light.constantAttenuation = constantAttenuation; + light.linearAttenuation = linearAttenuation; + _pointLights.append(light); + + } else { + SpotLight light; + light.position = glm::vec4(position, 1.0f); + light.radius = radius; + light.ambient = glm::vec4(ambient, 1.0f); + light.diffuse = glm::vec4(diffuse, 1.0f); + light.specular = glm::vec4(specular, 1.0f); + light.constantAttenuation = constantAttenuation; + light.linearAttenuation = linearAttenuation; + light.direction = direction; + light.exponent = exponent; + light.cutoff = cutoff; + _spotLights.append(light); + } +} + void DeferredLightingEffect::prepare() { // clear the normal and specular buffers Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, false); @@ -86,13 +124,6 @@ void DeferredLightingEffect::prepare() { void DeferredLightingEffect::render() { // perform deferred lighting, rendering to free fbo - glPushMatrix(); - glLoadIdentity(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glDisable(GL_BLEND); @@ -162,12 +193,14 @@ void DeferredLightingEffect::render() { Application::getInstance()->computeOffAxisFrustum( left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); program->setUniformValue(locations->nearLocation, nearVal); - program->setUniformValue(locations->depthScale, (farVal - nearVal) / farVal); + float depthScale = (farVal - nearVal) / farVal; + program->setUniformValue(locations->depthScale, depthScale); float nearScale = -1.0f / nearVal; float depthTexCoordScaleS = (right - left) * nearScale / sWidth; float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight; - program->setUniformValue(locations->depthTexCoordOffset, left * nearScale - sMin * depthTexCoordScaleS, - bottom * nearScale - tMin * depthTexCoordScaleT); + float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS; + float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT; + program->setUniformValue(locations->depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); program->setUniformValue(locations->depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight); @@ -179,6 +212,61 @@ void DeferredLightingEffect::render() { glActiveTexture(GL_TEXTURE3); } + // additive blending + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + + if (!_pointLights.isEmpty()) { + _pointLight.bind(); + _pointLight.setUniformValue(_pointLightLocations.nearLocation, nearVal); + _pointLight.setUniformValue(_pointLightLocations.depthScale, depthScale); + _pointLight.setUniformValue(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); + _pointLight.setUniformValue(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); + + foreach (const PointLight& light, _pointLights) { + _pointLight.setUniformValue(_pointLightLocations.radius, light.radius); + glLightfv(GL_LIGHT1, GL_AMBIENT, (const GLfloat*)&light.ambient); + glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); + glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); + glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); + glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, light.constantAttenuation); + glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, light.linearAttenuation); + glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); + + renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight); + } + _pointLights.clear(); + + _pointLight.release(); + } + + if (!_spotLights.isEmpty()) { + _spotLight.bind(); + _spotLight.setUniformValue(_spotLightLocations.nearLocation, nearVal); + _spotLight.setUniformValue(_spotLightLocations.depthScale, depthScale); + _spotLight.setUniformValue(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); + _spotLight.setUniformValue(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); + + foreach (const SpotLight& light, _spotLights) { + _spotLight.setUniformValue(_spotLightLocations.radius, light.radius); + glLightfv(GL_LIGHT1, GL_AMBIENT, (const GLfloat*)&light.ambient); + glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); + glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); + glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); + glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, light.constantAttenuation); + glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, light.linearAttenuation); + glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); + glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, (const GLfloat*)&light.direction); + glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, light.exponent); + glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, glm::degrees(light.cutoff)); + + renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight); + } + _spotLights.clear(); + + _spotLight.release(); + } + glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE2); @@ -193,7 +281,7 @@ void DeferredLightingEffect::render() { freeFBO->release(); // now transfer the lit region to the primary fbo - glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glColorMask(true, true, true, false); primaryFBO->bind(); @@ -201,6 +289,13 @@ void DeferredLightingEffect::render() { glBindTexture(GL_TEXTURE_2D, freeFBO->texture()); glEnable(GL_TEXTURE_2D); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight); glBindTexture(GL_TEXTURE_2D, 0); @@ -225,6 +320,7 @@ void DeferredLightingEffect::render() { } void DeferredLightingEffect::loadLightProgram(const char* name, ProgramObject& program, LightLocations& locations) { + program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/deferred_light.vert"); program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + name); program.link(); @@ -240,5 +336,6 @@ void DeferredLightingEffect::loadLightProgram(const char* name, ProgramObject& p locations.depthScale = program.uniformLocation("depthScale"); locations.depthTexCoordOffset = program.uniformLocation("depthTexCoordOffset"); locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale"); + locations.radius = program.uniformLocation("radius"); program.release(); } diff --git a/interface/src/renderer/DeferredLightingEffect.h b/interface/src/renderer/DeferredLightingEffect.h index 1953afd4c3..6eb8947689 100644 --- a/interface/src/renderer/DeferredLightingEffect.h +++ b/interface/src/renderer/DeferredLightingEffect.h @@ -14,6 +14,8 @@ #include +#include + #include "ProgramObject.h" class PostLightingRenderable; @@ -46,6 +48,17 @@ public: //// Renders a wireframe cube with the simple program. void renderWireCube(float size); + /// Adds a point light to render for the current frame. + void addPointLight(const glm::vec3& position, float radius, const glm::vec3& ambient = glm::vec3(0.0f, 0.0f, 0.0f), + const glm::vec3& diffuse = glm::vec3(1.0f, 1.0f, 1.0f), const glm::vec3& specular = glm::vec3(1.0f, 1.0f, 1.0f), + float constantAttenuation = 1.0f, float linearAttenuation = 0.0f, float quadraticAttenuation = 0.0f); + + /// Adds a spot light to render for the current frame. + void addSpotLight(const glm::vec3& position, float radius, const glm::vec3& ambient = glm::vec3(0.0f, 0.0f, 0.0f), + const glm::vec3& diffuse = glm::vec3(1.0f, 1.0f, 1.0f), const glm::vec3& specular = glm::vec3(1.0f, 1.0f, 1.0f), + float constantAttenuation = 1.0f, float linearAttenuation = 0.0f, float quadraticAttenuation = 0.0f, + const glm::vec3& direction = glm::vec3(0.0f, 0.0f, -1.0f), float exponent = 0.0f, float cutoff = PI); + /// Adds an object to render after performing the deferred lighting for the current frame (e.g., a translucent object). void addPostLightingRenderable(PostLightingRenderable* renderable) { _postLightingRenderables.append(renderable); } @@ -62,6 +75,7 @@ private: int depthScale; int depthTexCoordOffset; int depthTexCoordScale; + int radius; }; static void loadLightProgram(const char* name, ProgramObject& program, LightLocations& locations); @@ -75,7 +89,32 @@ private: LightLocations _directionalLightShadowMapLocations; ProgramObject _directionalLightCascadedShadowMap; LightLocations _directionalLightCascadedShadowMapLocations; + ProgramObject _pointLight; + LightLocations _pointLightLocations; + ProgramObject _spotLight; + LightLocations _spotLightLocations; + class PointLight { + public: + glm::vec4 position; + float radius; + glm::vec4 ambient; + glm::vec4 diffuse; + glm::vec4 specular; + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + }; + + class SpotLight : public PointLight { + public: + glm::vec3 direction; + float exponent; + float cutoff; + }; + + QVector _pointLights; + QVector _spotLights; QVector _postLightingRenderables; }; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index a4f09d12c3..edbbcb04cb 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -414,14 +414,10 @@ bool Model::render(float alpha, RenderMode mode) { glCullFace(GL_FRONT); } else if (mode == DEFAULT_RENDER_MODE) { - // update the local lights - for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { - if (i < _localLights.size()) { - _localLightDirections[i] = glm::normalize(Application::getInstance()->getUntranslatedViewMatrix() * - glm::vec4(_rotation * _localLights.at(i).direction, 0.0f)); - } else { - _localLightColors[i] = glm::vec4(); - } + // add the local lights + foreach (const LocalLight& light, _localLights) { + Application::getInstance()->getDeferredLightingEffect()->addSpotLight(glm::vec3(), 1.0f, glm::vec3(), + light.color, light.color, 1.0f, 0.0f, 0.0f); } } } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 381e373449..a9bc775d0a 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -284,9 +284,6 @@ private: QList _runningAnimations; - glm::vec4 _localLightColors[MAX_LOCAL_LIGHTS]; - glm::vec4 _localLightDirections[MAX_LOCAL_LIGHTS]; - QVector _blendedBlendshapeCoefficients; int _blendNumber; int _appliedBlendNumber;