From 01973cea0b8d43d027d22668710acb2f790768f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anselme=20FRAN=C3=87OIS?= Date: Fri, 24 Jun 2016 18:33:53 +0200 Subject: [PATCH] added deferred pipeline --- shaders/gbuffer.frag.glsl | 83 +++++++++++++ shaders/gbuffer.vert.glsl | 49 ++++++++ shaders/lighting.frag.glsl | 131 +++++++++++++++++++ shaders/lighting.vert.glsl | 8 ++ shaders/posteffects.frag.glsl | 17 +++ shaders/posteffects.vert.glsl | 7 ++ src/deferredpipeline.cpp | 228 ++++++++++++++++++++++++++++++++++ src/deferredpipeline.h | 61 +++++++++ 8 files changed, 584 insertions(+) create mode 100644 shaders/gbuffer.frag.glsl create mode 100644 shaders/gbuffer.vert.glsl create mode 100644 shaders/lighting.frag.glsl create mode 100644 shaders/lighting.vert.glsl create mode 100644 shaders/posteffects.frag.glsl create mode 100644 shaders/posteffects.vert.glsl create mode 100644 src/deferredpipeline.cpp create mode 100644 src/deferredpipeline.h diff --git a/shaders/gbuffer.frag.glsl b/shaders/gbuffer.frag.glsl new file mode 100644 index 0000000..1ddf6fa --- /dev/null +++ b/shaders/gbuffer.frag.glsl @@ -0,0 +1,83 @@ +// - Normal buffer +layout (location = 0) out vec3 outNormal; +// - Color + objectId buffer +layout (location = 1) out vec4 outColor; +// - Specular color + Specular exponent buffer +layout (location = 2) out vec4 outSpecular; +// - Position in view space +layout (location = 3) out vec4 outPosition; + +uniform float materialNs; + +// variables used for picking +uniform unsigned int object_identifier; +#ifdef INSTANCED +flat in int instanceId; +#endif + +#ifdef ALPHA_MASK +uniform sampler2D alphaMask; +#endif + +#ifdef DIFFUSE_TEXTURE +uniform sampler2D diffuseTexture; +#else +uniform vec3 materialKd; +#endif + +#ifdef SPECULAR_TEXTURE +uniform sampler2D specularTexture; +#else +uniform vec3 materialKs; +#endif + +in vec4 posInView; +#ifdef NORMAL_MAP +uniform sampler2D normalMap; + +in vec3 varTangent; +in vec3 varBinormal; +#endif +in vec3 varNormal; + +in vec2 varTexCoord; + +void main() +{ +#ifdef ALPHA_MASK + if(texture(alphaMask, varTexCoord).r < 0.5) + discard; +#endif + +#ifdef NORMAL_MAP + vec3 normalTexel = normalize(texture(normalMap, varTexCoord).xyz -0.5); + vec3 tangent = normalize(varTangent); + vec3 binormal = normalize(varBinormal); + vec3 normal = normalize(varNormal); + mat3 tangentSpace = mat3(vec3(tangent.x, binormal.x, normal.x), + vec3(tangent.y, binormal.y, normal.y), + vec3(tangent.z, binormal.z, normal.z)); + + outNormal = normalize(normalTexel * tangentSpace); +#else + outNormal = normalize(varNormal); +#endif + +#ifdef DIFFUSE_TEXTURE + outColor.rgb = texture(diffuseTexture, varTexCoord).rgb; +#else + outColor.rgb = materialKd; +#endif + + outColor.a = float(object_identifier)/255; + +#ifdef SPECULAR_TEXTURE + outSpecular.rgb = texture(specularTexture, varTexCoord).rgb; +#else + outSpecular.rgb = materialKs; +#endif + + outSpecular.w = float(materialNs)/255; + + outPosition = posInView; +} diff --git a/shaders/gbuffer.vert.glsl b/shaders/gbuffer.vert.glsl new file mode 100644 index 0000000..9ca3967 --- /dev/null +++ b/shaders/gbuffer.vert.glsl @@ -0,0 +1,49 @@ +uniform mat4 projectionMatrix; +uniform mat4 modelViewMatrix; +uniform mat3 normalMatrix; + +out vec4 posInView; +#ifdef NORMAL_MAP +out vec3 varTangent; +out vec3 varBinormal; +#endif +out vec3 varNormal; + +out vec2 varTexCoord; + +layout(location = 0)in vec3 inPosition; +layout(location = 2)in vec2 inTexCoord; +layout(location = 1)in vec3 inNormal; +#ifdef NORMAL_MAP +layout(location = 3)in vec3 inTangent; +layout(location = 4)in vec3 inBinormal; +#endif + +#ifdef INSTANCED +layout(location = 5)in vec3 inInstanceOffset; + +flat out int instanceId; +#endif + +void main(void) { + // computing normals +#ifdef NORMAL_MAP + varTangent = normalize(normalMatrix*inTangent); + varBinormal = normalize(normalMatrix*inBinormal); +#endif + varNormal = normalize(normalMatrix*inNormal); + + //computing UVs + varTexCoord = inTexCoord.xy; + + // computing positions +#ifdef INSTANCED + instanceId = gl_InstanceID; + vec4 pos = vec4(inPosition + inInstanceOffset, 1.0); +#else + vec4 pos = vec4(inPosition, 1.0); +#endif + + posInView = modelViewMatrix*pos; + gl_Position = projectionMatrix * posInView; +} diff --git a/shaders/lighting.frag.glsl b/shaders/lighting.frag.glsl new file mode 100644 index 0000000..2f15600 --- /dev/null +++ b/shaders/lighting.frag.glsl @@ -0,0 +1,131 @@ +// G-BUFFER + +// - Normal buffer +uniform sampler2DRect normalBuffer; +// - Color + objectId buffer +uniform sampler2DRect colorBuffer; +// - Specular color + Specular exponent buffer +uniform sampler2DRect specularBuffer; +// - Position in view space +uniform sampler2DRect positionBuffer; +// - depth buffer +uniform sampler2DRect depthBuffer; + +// LIGHT ATTRIBUTES + +uniform vec3 lightColor; + +#ifdef SHADOWMAP +uniform sampler2DShadow shadowMap; +uniform mat4 viewToLightMatrix; +#endif + +#if defined POINT_LIGHT +uniform vec3 pointLight; +uniform float attenuation; +#elif defined DIRECTIONNAL_LIGHT +uniform vec3 dirLight; +#elif defined SPOT_LIGHT +uniform vec3 pointLight; +uniform float attenuation; +uniform vec3 dirLight; +uniform float cutoff; +#endif + +// FRAGMENT POSITIONNING + +in vec2 screenPos; + +uniform mat4 inverseProjectionMatrix; + +// OUTPUT LIGHT + +layout(location = 0)out vec4 outColor; + +// FUNCTIONS + +const float CELL_SHADING = 0.3333; + +vec3 phongLighting(in vec3 kd, in vec3 ks, in float ns, in vec3 color, in vec3 normal, in vec3 lightDir, in vec3 halfVec, in vec3 viewDir){ + float diffuseComponent = max(dot(normal, lightDir), 0); + float specularComponent = max(dot(halfVec, normal), 0); + return color*diffuseComponent*(kd+ks*pow(specularComponent, ns)); +} + +vec3 CookTorranceSpecularHighlight(in vec3 ks, in float ns, in vec3 normal, in vec3 lightDir, in vec3 halfVec, in vec3 viewDir){ + float HN = dot(halfVec, normal); + float VN = dot(viewDir, normal); + float VH = dot(viewDir, halfVec); + float LN = dot(lightDir, normal); + + HN = max(HN, 0); + VN = max(VN, 0); + LN = max(LN, 0); + + // GGX normal distribution : + float roughness = 2/(ns+2); + float denom = mix(1, roughness, HN*HN); + float D = roughness/(3.1416*denom*denom); + //D = pow(HN, ns); + + // Fresnel term with Schlick's approximation + vec3 F = mix(ks, vec3(1), pow(1 - VH, 5)); + + VH = max(VH, 0); + + // Geometric attenuation + float G = min(1, min(2*HN*VN/VH, 2*HN*LN/VH)); + + return D * F * G; +} + +vec3 testLighting(in vec3 kd, in vec3 ks, in float ns, in vec3 color, in vec3 normal, in vec3 lightDir, in vec3 halfVec, in vec3 viewDir){ + float diffuseComponent = max(dot(normal, lightDir), 0); + return color*diffuseComponent*(kd+ks*CookTorranceSpecularHighlight(ks, ns, normal, lightDir, halfVec, viewDir)); +} + +float computeShadow(sampler2D shadowmap, vec3 shadow){ + float lightFragDepth = texture(shadowmap, shadow.xy).r; + return lightFragDepth < shadow.z ? 0 : 1; +} + +// MAIN PROGRAM + +void main(void) { + ivec2 texCoord = ivec2(gl_FragCoord.xy); + vec3 normal = texelFetch(normalBuffer, texCoord).xyz; + vec4 diffuseTexel = texelFetch(colorBuffer, texCoord); + vec3 diffuse = diffuseTexel.rgb; + vec4 specularTexel = texelFetch(specularBuffer, texCoord); + vec3 specular = specularTexel.rgb; + float shininess = specularTexel.w*255; + vec4 fragPos = texelFetch(positionBuffer, texCoord); + float depth = texelFetch(depthBuffer, texCoord).r; + +#ifdef SHADOWMAP + vec4 fragInLightSpace = viewToLightMatrix * fragPos; + fragInLightSpace.z = fragInLightSpace.z - 0.002; + float shadow = texture(shadowMap, fragInLightSpace.xyz/fragInLightSpace.w); +#else + float shadow = 1; +#endif + + float att = 1; +#ifdef POINT_LIGHT + vec3 dirLight = pointLight - fragPos.xyz; + float dist = length(dirLight); + if(dist > attenuation) + att = 0; + att = 1 - dist/attenuation; + dirLight = normalize(dirLight); +#endif + +#ifdef AMBIENT_LIGHT + outColor = vec4(diffuse*lightColor, 1); +#else + vec3 viewDir = normalize(-fragPos.xyz); + vec3 halfVec = normalize(viewDir + dirLight); + vec3 light = testLighting(diffuse, specular, shininess, lightColor, normal, dirLight, halfVec, viewDir); + outColor = vec4(mix(vec3(0.0), light*shadow, att*att), 1); +#endif +} diff --git a/shaders/lighting.vert.glsl b/shaders/lighting.vert.glsl new file mode 100644 index 0000000..3824d90 --- /dev/null +++ b/shaders/lighting.vert.glsl @@ -0,0 +1,8 @@ +layout(location = 0)in vec2 inPosition; + +out vec2 screenPos; + +void main(void) { + gl_Position = vec4(inPosition, 0.0, 1.0); + screenPos = inPosition; +} diff --git a/shaders/posteffects.frag.glsl b/shaders/posteffects.frag.glsl new file mode 100644 index 0000000..b4d6561 --- /dev/null +++ b/shaders/posteffects.frag.glsl @@ -0,0 +1,17 @@ +#version 330 core + +// LIGHT BUFFER + +uniform sampler2DRect lightBuffer; + +// OUTPUT LIGHT + +layout(location = 0)out vec4 outColor; + +// MAIN PROGRAM + +void main(void) { + ivec2 texCoord = ivec2(gl_FragCoord.xy); + vec3 color = texelFetch(lightBuffer, texCoord).xyz; + outColor = vec4(color, 1.0); +} diff --git a/shaders/posteffects.vert.glsl b/shaders/posteffects.vert.glsl new file mode 100644 index 0000000..9ef9406 --- /dev/null +++ b/shaders/posteffects.vert.glsl @@ -0,0 +1,7 @@ +#version 330 core + +layout(location = 0)in vec2 inPosition; + +void main(void) { + gl_Position = vec4(inPosition, 0.0, 1.0); +} diff --git a/src/deferredpipeline.cpp b/src/deferredpipeline.cpp new file mode 100644 index 0000000..555149a --- /dev/null +++ b/src/deferredpipeline.cpp @@ -0,0 +1,228 @@ +#include +#include "deferredpipeline.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RESOURCE_PACK(shaders) + +GBuffer::GBuffer(int width, int height) : FrameBuffer() +{ + Texture* tex; + // - Normal buffer + tex = new Texture(GL_RGB, GL_RGB16F, width, height, GL_FLOAT, GL_TEXTURE_RECTANGLE); + tex->setFiltering(GL_NEAREST); + tex->setUnit(NORMAL); + addTexture(tex, GL_COLOR_ATTACHMENT0); + + // - Color + objectId buffer + tex = new Texture(GL_RGBA, GL_RGBA, width, height, GL_UNSIGNED_BYTE, GL_TEXTURE_RECTANGLE); + tex->setFiltering(GL_NEAREST); + tex->setUnit(DIFFUSE); + addTexture(tex, GL_COLOR_ATTACHMENT1); + + // - Specular color + Specular exponent buffer + tex = new Texture(GL_RGBA, GL_RGBA, width, height, GL_UNSIGNED_BYTE, GL_TEXTURE_RECTANGLE); + tex->setFiltering(GL_NEAREST); + tex->setUnit(SPECULAR); + addTexture(tex, GL_COLOR_ATTACHMENT2); + + // - Position buffer + tex = new Texture(GL_RGBA, GL_RGBA16F, width, height, GL_FLOAT, GL_TEXTURE_RECTANGLE); + tex->setFiltering(GL_NEAREST); + tex->setUnit(POSITION); + addTexture(tex, GL_COLOR_ATTACHMENT3); + + // - depth buffer + tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, width, height, GL_FLOAT, GL_TEXTURE_RECTANGLE); + tex->setFiltering(GL_NEAREST); + tex->setUnit(DEPTH); + addTexture(tex, GL_DEPTH_ATTACHMENT); + + initColorAttachments(); +} + +void GBuffer::bindTextures() +{ + for(int i=0; ibind(); +} + +void GBuffer::unbindTextures() +{ + for(int i=0; iunbind(); +} + +LightingBuffer::LightingBuffer(int width, int height) : FrameBuffer() +{ + // colors are encoded in float to allow tonemapping + Texture* tex = new Texture(GL_RGBA, GL_RGBA16F, width, height, GL_FLOAT, GL_TEXTURE_RECTANGLE); + tex->setFiltering(GL_NEAREST); + addTexture(tex, GL_COLOR_ATTACHMENT0); + + initColorAttachments(); +} + +DeferredPipeline::DeferredPipeline() : + m_camera(NULL), + m_width(512), + m_height(512), + m_gBuffer(NULL), + m_lightingBuffer(NULL), + m_renderTarget(NULL) +{ + Resource::ResourceMap shaderMap; + Resource::getResourcePack_shaders(shaderMap); + m_gBufferSource = new ShaderSource(); + m_gBufferSource->setSource(shaderMap["shaders/gbuffer.vert.glsl"], ShaderSource::VERTEX); + m_gBufferSource->setSource(shaderMap["shaders/gbuffer.frag.glsl"], ShaderSource::FRAGMENT); + m_lightingSource = new ShaderSource(); + m_lightingSource->setSource(shaderMap["shaders/lighting.vert.glsl"], ShaderSource::VERTEX); + m_lightingSource->setSource(shaderMap["shaders/lighting.frag.glsl"], ShaderSource::FRAGMENT); + std::string vertSource = shaderMap["shaders/posteffects.vert.glsl"]; + std::string fragSource = shaderMap["shaders/posteffects.frag.glsl"]; + m_postEffectsShader = new Shader(vertSource, fragSource); +} + +void DeferredPipeline::renderGL(Scene *scene) +{ + if(m_renderTarget == NULL) + return; + + // GEOMETRY PASS + m_gBuffer->bindFBO(); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDisable(GL_BLEND); + glClearColor(0.0f, 0.0f, 0.0f, 1.f); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // loop on geometry + unsigned int id = 2; + for(SceneIterator* geometryIt = scene->getGeometry(); + geometryIt->isValid(); geometryIt->next()) + { + + GeometryNode* node = geometryIt->getItem(); + Shader *shader = m_meshShaders[node->mesh->getFlags()]; + if(shader == NULL) + { + fprintf(stderr, "no shader to render this geometry, please use refreshScene to generate the shader\n"); + continue; + } + shader->bind(); + + shader->bindUnsignedInteger(shader->getLocation("object_identifier"), id); + if(node->mesh->instances_offsets.empty()) + ++id; + else + id += node->mesh->instances_offsets.size(); + + glm::mat4 modelViewMatrix = m_camera->getViewMatrix() * node->modelMatrix; + glm::mat4 normalMatrix = glm::transpose(glm::inverse(modelViewMatrix)); + shader->bindMat4(shader->getLocation("projectionMatrix"), m_camera->getProjectionMatrix()); + shader->bindMat4(shader->getLocation("modelViewMatrix"), modelViewMatrix); + shader->bindMat3(shader->getLocation("normalMatrix"), glm::mat3(normalMatrix)); + // draw geometry + node->mesh->draw(shader); + } + + // LIGHTING PASS + m_lightingBuffer->bindFBO(); + glDepthFunc(GL_LEQUAL); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_gBuffer->bindTextures(); + for(SceneIterator* lightIt = scene->getLights(); + lightIt->isValid(); lightIt->next()) + { + Light* light = lightIt->getItem(); + Shader *shader = m_lightShaders[light->getFlags()]; + if(shader == NULL) + { + fprintf(stderr, "no shader to render this light, please use refreshScene to generate the shader\n"); + continue; + } + shader->bind(); + + // bind GBuffer + shader->bindInteger(shader->getLocation("normalBuffer"), GBuffer::NORMAL); + shader->bindInteger(shader->getLocation("colorBuffer"), GBuffer::DIFFUSE); + shader->bindInteger(shader->getLocation("specularBuffer"), GBuffer::SPECULAR); + shader->bindInteger(shader->getLocation("positionBuffer"), GBuffer::POSITION); + shader->bindInteger(shader->getLocation("depthBuffer"), GBuffer::DEPTH); + + // bind light + light->bindAttributes(shader, m_camera); + + // compute fragments + SparrowRenderer::drawQuad(); + } + m_gBuffer->unbindTextures(); + + // post effects pass + m_renderTarget->bindFBO(); + glDisable(GL_BLEND); + m_lightingBuffer->getTexture(0)->bind(0); + m_postEffectsShader->bind(); + m_postEffectsShader->bindInteger(m_postEffectsShader->getLocation("lightBuffer"), 0); + // TODO : send uniforms + SparrowRenderer::drawQuad(); + m_lightingBuffer->getTexture(0)->unbind(); +} + +void DeferredPipeline::resizeGL(int w, int h) +{ + // updating dimensions + m_width = w; + m_height = h; + + // rebuilding FrameBuffers + if(m_gBuffer != NULL) + { + delete m_gBuffer; + delete m_lightingBuffer; + } + m_gBuffer = new GBuffer(w, h); + m_lightingBuffer = new LightingBuffer(w, h); +} + +void setSources(ShaderSource *gBufferSource, ShaderSource *lightingSource, Shader *postEffectsShader) +{ + if(m_gBufferSource != NULL) + delete m_gBufferSource; + m_gBufferSource = gBufferSource; + if(m_lightingSource != NULL) + delete m_lightingSource; + m_lightingSource = lightingSource; + if(m_postEffectsShader != NULL) + delete m_postEffectsShader; + m_postEffectsShader = postEffectsShader; +} + +void DeferredPipeline::refreshScene(Scene *scene) +{ + for(auto it : m_meshShaders) + delete it.second; + m_meshShaders.clear(); + m_meshTypes.clear(); + scene->getMeshTypes(m_meshTypes); + for(unsigned int type : m_meshTypes) + m_meshShaders[type] = m_gBufferSource->compile(type, 0); + + for(auto it : m_lightShaders) + delete it.second; + m_lightShaders.clear(); + m_lightTypes.clear(); + scene->getLightTypes(m_lightTypes); + for(unsigned int type : m_lightTypes) + m_lightShaders[type] = m_lightingSource->compile(0, type); +} diff --git a/src/deferredpipeline.h b/src/deferredpipeline.h new file mode 100644 index 0000000..66ab8b9 --- /dev/null +++ b/src/deferredpipeline.h @@ -0,0 +1,61 @@ +#ifndef DEFERREDPIPELINE_H +#define DEFERREDPIPELINE_H + +#include +#include +#include + +class Shader; +class Camera; + +class GBuffer : public FrameBuffer +{ +public: + enum Buffers { NORMAL, DIFFUSE, SPECULAR, POSITION, DEPTH, NB_BUFFERS }; + GBuffer(int width, int height); + void bindTextures(); + void unbindTextures(); +}; + +class LightingBuffer : public FrameBuffer +{ +public: + LightingBuffer(int width, int height); +}; + +class DeferredPipeline : public Pipeline +{ + Camera *m_camera; + + int m_width; + int m_height; + + // shaders + std::vector m_meshTypes; + std::vector m_lightTypes; + + std::unordered_map m_meshShaders; + std::unordered_map m_lightShaders; + + ShaderSource *m_gBufferSource; + ShaderSource *m_lightingSource; + Shader *m_postEffectsShader; + + // framebuffers + GBuffer *m_gBuffer; + LightingBuffer *m_lightingBuffer; + const FrameBuffer *m_renderTarget; + +public: + DeferredPipeline(); + + void setCamera(Camera *camera) { m_camera = camera; } + void setSources(ShaderSource *gBufferSource, ShaderSource *lightingSource, Shader *postEffectsShader); + void setRenderTarget(const FrameBuffer *fbo) { m_renderTarget = fbo; } + void refreshScene(Scene *scene); + + virtual void renderGL(Scene *scene); + virtual void resizeGL(int w, int h); +}; + +#endif // DEFERREDPIPELINE_H