#include "sparrowrenderer.h" #include "deferredpipeline.h" // renderer #include "texture.h" #include "scene.h" #include "mesh.h" #include "shader.h" #include "shadersource.h" #include "pbrmaterial.h" #include "camera.h" #include "skybox.h" #include "guimesh.h" // GLM #include // Stl #include // imgui #include // resource packs #include RESOURCE_PACK(shaders) GBuffer::GBuffer(int width, int height, GLuint renderBuffer) : FrameBuffer() { Texture* tex; // - 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_ATTACHMENT0); // - Albedo + Roughness tex = new Texture(GL_RGBA, GL_RGBA, width, height, GL_UNSIGNED_BYTE, GL_TEXTURE_RECTANGLE); tex->setFiltering(GL_NEAREST); tex->setUnit(ALBEDO); addTexture(tex, GL_COLOR_ATTACHMENT1); // - 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_ATTACHMENT2); // - Emission + Metallic tex = new Texture(GL_RGBA, GL_RGBA, width, height, GL_UNSIGNED_BYTE, GL_TEXTURE_RECTANGLE); tex->setFiltering(GL_NEAREST); tex->setUnit(EMISSION); addTexture(tex, GL_COLOR_ATTACHMENT3); addRenderBuffer(renderBuffer, GL_DEPTH_ATTACHMENT); addRenderBuffer(renderBuffer, GL_STENCIL_ATTACHMENT); initColorAttachments(); } void GBuffer::bindTextures() { for(int i=0; ibind(); } void GBuffer::unbindTextures() { for(int i=0; iunbind(); } LightingBuffer::LightingBuffer(int width, int height, GLuint renderBuffer) : 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); addRenderBuffer(renderBuffer, GL_DEPTH_ATTACHMENT); addRenderBuffer(renderBuffer, GL_STENCIL_ATTACHMENT); initColorAttachments(); } DeferredPipeline::DeferredPipeline() : m_camera(nullptr), m_skybox(nullptr), m_width(512), m_height(512), m_postEffectsShader(nullptr), m_depth_stencil_renderBuffer(0), m_gBuffer(nullptr), m_lightingBuffer(nullptr), m_renderTarget(nullptr), m_debugGuiEnabled(false), m_gammaCorrectEnabled(true), m_hdrTonemappingEnabled(false) { 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); m_mesh2DSource = new ShaderSource(); m_mesh2DSource->setSource(shaderMap["shaders/gui.vert.glsl"], ShaderSource::VERTEX); m_mesh2DSource->setSource(shaderMap["shaders/gui.frag.glsl"], ShaderSource::FRAGMENT); m_postEffectsShaders = new ShaderSource(); m_postEffectsShaders->setSource(shaderMap["shaders/posteffects.vert.glsl"], ShaderSource::VERTEX); m_postEffectsShaders->setSource(shaderMap["shaders/posteffects.frag.glsl"], ShaderSource::FRAGMENT); recompilePostEffectsShader(); m_debugShaders = new ShaderSource(); m_debugShaders->setSource(shaderMap["shaders/debug.vert.glsl"], ShaderSource::VERTEX); m_debugShaders->setSource(shaderMap["shaders/debug.frag.glsl"], ShaderSource::FRAGMENT); m_guiMesh = new GuiMesh(); m_guiMesh->initGL(); glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); } bool depthCompare(const GeometryNode* firstElem, const GeometryNode* secondElem) { return firstElem->mesh->getDepth() < secondElem->mesh->getDepth(); } void DeferredPipeline::renderGL(Scene *scene) { if(m_renderTarget == NULL) return; glViewport(0, 0, m_width, m_height); // GEOMETRY PASS std::vector mesh2D; m_gBuffer->bindFBO(); glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); glDepthFunc(GL_LESS); glDisable(GL_BLEND); glClearColor(0.0f, 0.0f, 0.0f, 1.f); glClearDepth(1.0f); glClearStencil(0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glStencilMask(0x01); glStencilFunc(GL_ALWAYS, 0x01, 0x01); // always draw fragments (ignore the stencil buffer values for now) // draw skybox if(m_skybox != nullptr) { glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); m_skybox->renderGL(m_camera); glClear(GL_DEPTH_BUFFER_BIT); } // loop on geometry for(SceneIterator* geometryIt = scene->getGeometry(); geometryIt->isValid(); geometryIt->next()) { GeometryNode* node = geometryIt->getItem(); unsigned int type = node->mesh->getFlags(); if(type & (1 << Mesh::MESH_2D)) { mesh2D.push_back(node); // TODO : not every frame continue; } Shader *shader = m_mesh3DShaders[node->mesh->getFlags()]; if(shader == NULL) { fprintf(stderr, "no shader to render this 3D geometry, please call refreshScene to generate the shader\n"); continue; } shader->bind(); 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); glEnable(GL_STENCIL_TEST); glBlendFunc(GL_ONE, GL_ONE); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); m_gBuffer->bindTextures(); // draw unlit fragments Shader *shader = m_lightShaders[1 << Light::UNLIT_FLAG]; if(shader != nullptr) { shader->bind(); shader->bindInteger(shader->getLocation("emissionBuffer"), GBuffer::EMISSION); glStencilFunc(GL_EQUAL, 1, 0x01); SparrowRenderer::drawQuad(); } // loop on light sources glStencilFunc(GL_EQUAL, 0, 0x01); for(SceneIterator* lightIt = scene->getLights(); lightIt->isValid(); lightIt->next()) { Light* light = lightIt->getItem(); Shader *shader = m_lightShaders[light->getFlags()]; if(shader == nullptr) { fprintf(stderr, "no shader to render this light, please call refreshScene to generate the shader\n"); continue; } shader->bind(); // bind GBuffer shader->bindInteger(shader->getLocation("positionBuffer"), GBuffer::POSITION); shader->bindInteger(shader->getLocation("albedoBuffer"), GBuffer::ALBEDO); shader->bindInteger(shader->getLocation("normalBuffer"), GBuffer::NORMAL); shader->bindInteger(shader->getLocation("emissionBuffer"), GBuffer::EMISSION); // bind light light->bindAttributes(shader, m_camera); // compute fragments SparrowRenderer::drawQuad(); } m_gBuffer->unbindTextures(); // POST EFFECTS PASS m_renderTarget->bindFBO(); glDisable(GL_BLEND); glDisable(GL_STENCIL_TEST); glStencilMask(0); m_lightingBuffer->getTexture(0)->bind(0); m_postEffectsShader->bind(); m_postEffectsShader->bindInteger(m_postEffectsShader->getLocation("lightBuffer"), 0); SparrowRenderer::drawQuad(); m_lightingBuffer->getTexture(0)->unbind(); // 2D PASS glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); std::sort(mesh2D.begin(), mesh2D.end(), depthCompare); for(GeometryNode* node : mesh2D) { Shader *shader = m_mesh2DShaders[node->mesh->getFlags()]; if(shader == NULL) { fprintf(stderr, "no shader to render this 2D geometry, please call refreshScene to generate the shader\n"); continue; } shader->bind(); shader->bindMat4(shader->getLocation("orthoMatrix"), m_orthoMatrix); shader->bindMat4(shader->getLocation("transformMatrix"), node->modelMatrix); // draw geometry node->mesh->draw(shader); } if(m_debugGuiEnabled) gui(); // IMGUI PASS m_guiMesh->drawGL(); } struct DebugCanvas { glm::ivec2 top_left; glm::ivec2 bottom_right; void gui() { if(ImGui::Button("Left")) { if(top_left.x != -1) top_left.x = -1; else bottom_right.x = 0; } if(ImGui::Button("Right")) { if(bottom_right.x != 1) bottom_right.x = 1; else top_left.x = 0; } if(ImGui::Button("Up")) { if(top_left.y != 1) top_left.y = 1; else bottom_right.y = 0; } if(ImGui::Button("Down")) { if(bottom_right.y != -1) bottom_right.y = -1; else top_left.y = 0; } } void drawCanvas() { Shader* shader = nullptr; shader->bind(); shader->bindVec2(shader->getLocation("origin"), (top_left+bottom_right)/2); shader->bindVec2(shader->getLocation("origin"), top_left-bottom_right); SparrowRenderer::drawQuad(); } }; void DeferredPipeline::gui() { ImGui::Begin("Rendering Pipeline", &m_debugGuiEnabled); if(ImGui::Checkbox("Gamma correction", &m_gammaCorrectEnabled)) recompilePostEffectsShader(); if(ImGui::Checkbox("HDR tonemapping", &m_hdrTonemappingEnabled)) recompilePostEffectsShader(); if(ImGui::Button("Add a debug canvas")) { } ImGui::End(); // debug render } void DeferredPipeline::recompilePostEffectsShader() { std::vector defines; if(m_gammaCorrectEnabled) defines.push_back("GAMMA_CORRECT"); if(m_hdrTonemappingEnabled) defines.push_back("HDR_TONEMAPPING"); if(m_postEffectsShader != nullptr) delete m_postEffectsShader; m_postEffectsShader = m_postEffectsShaders->compile(defines); } void DeferredPipeline::setSkybox(Texture* texture) { if(m_skybox != nullptr) delete m_skybox; m_skybox = nullptr; if(texture != nullptr) { m_skybox = new Skybox(texture); m_skybox->resizeGL(m_width, m_height); } } void DeferredPipeline::resizeGL(int w, int h) { // updating dimensions m_width = w; m_height = h; if(m_skybox != nullptr) m_skybox->resizeGL(w, h); if(m_depth_stencil_renderBuffer != 0) glDeleteRenderbuffers(1, &m_depth_stencil_renderBuffer); // rebuilding FrameBuffers if(m_gBuffer != NULL) { delete m_gBuffer; delete m_lightingBuffer; } glGenRenderbuffers(1, &m_depth_stencil_renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, m_depth_stencil_renderBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); m_gBuffer = new GBuffer(w, h, m_depth_stencil_renderBuffer); m_lightingBuffer = new LightingBuffer(w, h, m_depth_stencil_renderBuffer); m_orthoMatrix = glm::ortho(0.f, float(m_width), float(m_height), 0.f); m_guiMesh->resizeGL(w, h); } void DeferredPipeline::setSources(ShaderSource *gBufferSource, ShaderSource *lightingSource, ShaderSource *guiSource, Shader *postEffectsShader) { if(m_gBufferSource != NULL) delete m_gBufferSource; m_gBufferSource = gBufferSource; if(m_lightingSource != NULL) delete m_lightingSource; m_lightingSource = lightingSource; if(m_mesh2DSource != NULL) delete m_mesh2DSource; m_mesh2DSource = guiSource; if(m_postEffectsShader != NULL) delete m_postEffectsShader; m_postEffectsShader = postEffectsShader; } void DeferredPipeline::refreshScene(Scene *scene) { // delete previous shaders for(auto it : m_mesh3DShaders) delete it.second; m_mesh3DShaders.clear(); m_meshTypes.clear(); // gather all unique mesh flags scene->getMeshTypes(m_meshTypes); for(unsigned int type : m_meshTypes) { if(type & (1 << Mesh::MESH_2D)) m_mesh2DShaders[type] = m_mesh2DSource->compile(type, 0); else m_mesh3DShaders[type] = m_gBufferSource->compile(type, 0); } for(auto it : m_lightShaders) delete it.second; m_lightShaders.clear(); m_lightTypes.clear(); m_lightTypes.push_back(1 << Light::UNLIT_FLAG); scene->getLightTypes(m_lightTypes); for(unsigned int type : m_lightTypes) m_lightShaders[type] = m_lightingSource->compile(0, type); } glm::vec4 DeferredPipeline::pick(int x, int y) { m_gBuffer->setTarget(GL_READ_FRAMEBUFFER); m_gBuffer->bindFBO(); float values[4] = {1}; glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, values); GLenum err = glGetError(); while(err != GL_NO_ERROR) { switch(err) { case GL_INVALID_ENUM : printf("OpenGL error : GL_INVALID_ENUM\n"); break; case GL_INVALID_VALUE : printf("OpenGL error : GL_INVALID_VALUE\n"); break; case GL_INVALID_OPERATION : printf("OpenGL error : GL_INVALID_OPERATION\n"); break; default : printf("OpenGL error : other\n"); break; } err = glGetError(); } m_gBuffer->setTarget(); FrameBuffer::screen->bindFBO(); return glm::vec4(values[0], values[1], values[2], values[3]); }