// Copyright (C) 2015 Jérôme Leclercq // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp #ifndef NAZARA_RENDERER_OPENGL #define NAZARA_RENDERER_OPENGL // Nécessaire pour inclure les headers OpenGL #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { const nzUInt8 r_fragmentSource_BloomBright[] = { #include }; const nzUInt8 r_fragmentSource_BloomFinal[] = { #include }; const nzUInt8 r_fragmentSource_DirectionalLight[] = { #include }; const nzUInt8 r_fragmentSource_FXAA[] = { #include }; const nzUInt8 r_fragmentSource_GBufferClear[] = { #include }; const nzUInt8 r_fragmentSource_GaussianBlur[] = { #include }; const nzUInt8 r_fragmentSource_PointSpotLight[] = { #include }; unsigned int RenderPassPriority[] = { 6, // nzRenderPassType_AA 4, // nzRenderPassType_Bloom 7, // nzRenderPassType_DOF 0xFF, // nzRenderPassType_Final 5, // nzRenderPassType_Fog 2, // nzRenderPassType_Forward 1, // nzRenderPassType_Lighting 0, // nzRenderPassType_Geometry 3, // nzRenderPassType_SSAO }; static_assert(sizeof(RenderPassPriority)/sizeof(unsigned int) == nzRenderPassType_Max+1, "Render pass priority array is incomplete"); inline NzShaderRef RegisterDeferredShader(const NzString& name, const nzUInt8* fragmentSource, unsigned int fragmentSourceLength, const NzShaderStage& vertexStage, NzString* err) { NzErrorFlags errFlags(nzErrorFlag_Silent | nzErrorFlag_ThrowExceptionDisabled); NzShaderRef shader = NzShader::New(); if (!shader->Create()) { err->Set("Failed to create shader: " + NzError::GetLastError()); return nullptr; } if (!shader->AttachStageFromSource(nzShaderStage_Fragment, reinterpret_cast(fragmentSource), fragmentSourceLength)) { err->Set("Failed to attach fragment stage: " + NzError::GetLastError()); return nullptr; } shader->AttachStage(nzShaderStage_Vertex, vertexStage); if (!shader->Link()) { err->Set("Failed to link shader: " + NzError::GetLastError()); return nullptr; } NzShaderLibrary::Register(name, shader); return shader; } } NzDeferredRenderTechnique::NzDeferredRenderTechnique() : m_renderQueue(static_cast(m_forwardTechnique.GetRenderQueue())), m_GBufferSize(0U) { m_depthStencilBuffer = NzRenderBuffer::New(); for (unsigned int i = 0; i < 2; ++i) m_workTextures[i] = NzTexture::New(); for (unsigned int i = 0; i < 3; ++i) m_GBuffer[i] = NzTexture::New(); try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); ResetPass(nzRenderPassType_Final, 0); ResetPass(nzRenderPassType_Geometry, 0); ResetPass(nzRenderPassType_Lighting, 0); } catch (const std::exception& e) { NzErrorFlags errFlags(nzErrorFlag_ThrowExceptionDisabled); NazaraError("Failed to add geometry and/or phong lighting pass: " + NzString(e.what())); throw; } try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); ResetPass(nzRenderPassType_AA, 0); } catch (const std::exception& e) { NazaraWarning("Failed to add FXAA pass: " + NzString(e.what())); } try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); ResetPass(nzRenderPassType_Bloom, 0); } catch (const std::exception& e) { NazaraWarning("Failed to add bloom pass: " + NzString(e.what())); } try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); NzDeferredRenderPass* dofPass = ResetPass(nzRenderPassType_DOF, 0); dofPass->Enable(false); } catch (const std::exception& e) { NazaraWarning("Failed to add DOF pass: " + NzString(e.what())); } try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); NzDeferredRenderPass* fogPass = ResetPass(nzRenderPassType_Fog, 0); fogPass->Enable(false); } catch (const std::exception& e) { NazaraWarning("Failed to add fog pass: " + NzString(e.what())); } try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); ResetPass(nzRenderPassType_Forward, 0); } catch (const std::exception& e) { NazaraWarning("Failed to add forward pass: " + NzString(e.what())); } try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); ResetPass(nzRenderPassType_SSAO, 0); } catch (const std::exception& e) { NazaraWarning("Failed to add SSAO pass: " + NzString(e.what())); } } NzDeferredRenderTechnique::~NzDeferredRenderTechnique() = default; bool NzDeferredRenderTechnique::Draw(const NzSceneData& sceneData) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); NzRecti viewerViewport = sceneData.viewer->GetViewport(); NzVector2ui viewportDimensions(viewerViewport.width, viewerViewport.height); if (viewportDimensions != m_GBufferSize) { if (!Resize(viewportDimensions)) { NazaraError("Failed to update RTT"); return false; } } unsigned int sceneTexture = 0; unsigned int workTexture = 1; for (auto& passIt : m_passes) { for (auto& passIt2 : passIt.second) { const NzDeferredRenderPass* pass = passIt2.second.get(); if (pass->IsEnabled()) { if (pass->Process(sceneData, workTexture, sceneTexture)) std::swap(workTexture, sceneTexture); } } } return true; } void NzDeferredRenderTechnique::EnablePass(nzRenderPassType renderPass, int position, bool enable) { auto it = m_passes.find(renderPass); if (it != m_passes.end()) { auto it2 = it->second.find(position); if (it2 != it->second.end()) it2->second->Enable(enable); } } NzRenderBuffer* NzDeferredRenderTechnique::GetDepthStencilBuffer() const { return m_depthStencilBuffer; } NzTexture* NzDeferredRenderTechnique::GetGBuffer(unsigned int i) const { #if NAZARA_GRAPHICS_SAFE if (i >= 3) { NazaraError("GBuffer texture index out of range (" + NzString::Number(i) + " >= 3)"); return nullptr; } #endif return m_GBuffer[i]; } NzRenderTexture* NzDeferredRenderTechnique::GetGBufferRTT() const { return &m_GBufferRTT; } const NzForwardRenderTechnique* NzDeferredRenderTechnique::GetForwardTechnique() const { return &m_forwardTechnique; } NzDeferredRenderPass* NzDeferredRenderTechnique::GetPass(nzRenderPassType renderPass, int position) { auto it = m_passes.find(renderPass); if (it != m_passes.end()) { auto it2 = it->second.find(position); if (it2 != it->second.end()) return it2->second.get(); } return nullptr; } NzAbstractRenderQueue* NzDeferredRenderTechnique::GetRenderQueue() { return &m_renderQueue; } nzRenderTechniqueType NzDeferredRenderTechnique::GetType() const { return nzRenderTechniqueType_DeferredShading; } NzRenderTexture* NzDeferredRenderTechnique::GetWorkRTT() const { return &m_workRTT; } NzTexture* NzDeferredRenderTechnique::GetWorkTexture(unsigned int i) const { #if NAZARA_GRAPHICS_SAFE if (i >= 2) { NazaraError("Work texture index out of range (" + NzString::Number(i) + " >= 2)"); return nullptr; } #endif return m_workTextures[i]; } bool NzDeferredRenderTechnique::IsPassEnabled(nzRenderPassType renderPass, int position) { auto it = m_passes.find(renderPass); if (it != m_passes.end()) { auto it2 = it->second.find(position); if (it2 != it->second.end()) return it2->second->IsEnabled(); } return false; } NzDeferredRenderPass* NzDeferredRenderTechnique::ResetPass(nzRenderPassType renderPass, int position) { std::unique_ptr smartPtr; // Nous évite un leak en cas d'exception switch (renderPass) { case nzRenderPassType_AA: smartPtr.reset(new NzDeferredFXAAPass); break; case nzRenderPassType_Bloom: smartPtr.reset(new NzDeferredBloomPass); break; case nzRenderPassType_DOF: smartPtr.reset(new NzDeferredDOFPass); break; case nzRenderPassType_Final: smartPtr.reset(new NzDeferredFinalPass); break; case nzRenderPassType_Fog: smartPtr.reset(new NzDeferredFogPass); break; case nzRenderPassType_Forward: smartPtr.reset(new NzDeferredForwardPass); break; case nzRenderPassType_Geometry: smartPtr.reset(new NzDeferredGeometryPass); break; case nzRenderPassType_Lighting: smartPtr.reset(new NzDeferredPhongLightingPass); break; case nzRenderPassType_SSAO: //smartPtr.reset(new NzDeferredSSAOPass); break; } NzDeferredRenderPass* oldPass = GetPass(renderPass, position); if (oldPass && !oldPass->IsEnabled()) smartPtr->Enable(false); SetPass(renderPass, position, smartPtr.get()); return smartPtr.release(); } void NzDeferredRenderTechnique::SetPass(nzRenderPassType relativeTo, int position, NzDeferredRenderPass* pass) { if (pass) { pass->Initialize(this); if (m_GBufferSize != NzVector2ui(0U)) pass->Resize(m_GBufferSize); m_passes[relativeTo][position].reset(pass); } else m_passes[relativeTo].erase(position); } bool NzDeferredRenderTechnique::IsSupported() { // On ne va pas s'embêter à écrire un Deferred Renderer qui ne passe pas par le MRT, ce serait trop lent pour servir... return NzOpenGL::GetGLSLVersion() >= 140 && // On ne va pas s'embêter non plus avec le mode de compatibilité NzRenderer::HasCapability(nzRendererCap_RenderTexture) && NzRenderer::HasCapability(nzRendererCap_MultipleRenderTargets) && NzRenderer::GetMaxColorAttachments() >= 4 && NzRenderer::GetMaxRenderTargets() >= 4; } bool NzDeferredRenderTechnique::Resize(const NzVector2ui& dimensions) const { try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); for (auto& passIt : m_passes) for (auto& passIt2 : passIt.second) passIt2.second->Resize(dimensions); m_GBufferSize = dimensions; return true; } catch (const std::exception& e) { NazaraError("Failed to create work RTT/G-Buffer: " + NzString(e.what())); return false; } } bool NzDeferredRenderTechnique::Initialize() { const char vertexSource_Basic[] = "#version 140\n" "in vec3 VertexPosition;\n" "uniform mat4 WorldViewProjMatrix;\n" "void main()\n" "{\n" "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n" "}\n"; const char vertexSource_PostProcess[] = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "gl_Position = vec4(VertexPosition, 1.0);" "}\n"; NzShaderStage basicVertexStage(nzShaderStage_Vertex); if (!basicVertexStage.IsValid()) { NazaraError("Failed to create basic vertex shader"); return false; } basicVertexStage.SetSource(vertexSource_Basic, sizeof(vertexSource_Basic)); if (!basicVertexStage.Compile()) { NazaraError("Failed to compile basic vertex shader"); return false; } NzShaderStage ppVertexStage(nzShaderStage_Vertex); if (!ppVertexStage.IsValid()) { NazaraError("Failed to create vertex shader"); return false; } ppVertexStage.SetSource(vertexSource_PostProcess, sizeof(vertexSource_PostProcess)); if (!ppVertexStage.Compile()) { NazaraError("Failed to compile vertex shader"); return false; } NzString error; NzShader* shader; // Shaders critiques (Nécessaires pour le Deferred Shading minimal) shader = RegisterDeferredShader("DeferredGBufferClear", r_fragmentSource_GBufferClear, sizeof(r_fragmentSource_GBufferClear), ppVertexStage, &error); if (!shader) { NazaraError("Failed to register critical shader: " + error); return false; } shader = RegisterDeferredShader("DeferredDirectionnalLight", r_fragmentSource_DirectionalLight, sizeof(r_fragmentSource_DirectionalLight), ppVertexStage, &error); if (!shader) { NazaraError("Failed to register critical shader: " + error); return false; } shader->SendInteger(shader->GetUniformLocation("GBuffer0"), 0); shader->SendInteger(shader->GetUniformLocation("GBuffer1"), 1); shader->SendInteger(shader->GetUniformLocation("GBuffer2"), 2); shader = RegisterDeferredShader("DeferredPointSpotLight", r_fragmentSource_PointSpotLight, sizeof(r_fragmentSource_PointSpotLight), basicVertexStage, &error); if (!shader) { NazaraError("Failed to register critical shader: " + error); return false; } shader->SendInteger(shader->GetUniformLocation("GBuffer0"), 0); shader->SendInteger(shader->GetUniformLocation("GBuffer1"), 1); shader->SendInteger(shader->GetUniformLocation("GBuffer2"), 2); // Shaders optionnels (S'ils ne sont pas présents, le rendu minimal sera quand même assuré) shader = RegisterDeferredShader("DeferredBloomBright", r_fragmentSource_BloomBright, sizeof(r_fragmentSource_BloomBright), ppVertexStage, &error); if (shader) shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0); else { NazaraWarning("Failed to register bloom (bright pass) shader, certain features will not work: " + error); } shader = RegisterDeferredShader("DeferredBloomFinal", r_fragmentSource_BloomFinal, sizeof(r_fragmentSource_BloomFinal), ppVertexStage, &error); if (shader) { shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0); shader->SendInteger(shader->GetUniformLocation("BloomTexture"), 1); } else { NazaraWarning("Failed to register bloom (final pass) shader, certain features will not work: " + error); } shader = RegisterDeferredShader("DeferredFXAA", r_fragmentSource_FXAA, sizeof(r_fragmentSource_FXAA), ppVertexStage, &error); if (shader) shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0); else { NazaraWarning("Failed to register FXAA shader, certain features will not work: " + error); } shader = RegisterDeferredShader("DeferredGaussianBlur", r_fragmentSource_GaussianBlur, sizeof(r_fragmentSource_GaussianBlur), ppVertexStage, &error); if (shader) shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0); else { NazaraWarning("Failed to register gaussian blur shader, certain features will not work: " + error); } return true; } void NzDeferredRenderTechnique::Uninitialize() { NzShaderLibrary::Unregister("DeferredGBufferClear"); NzShaderLibrary::Unregister("DeferredDirectionnalLight"); NzShaderLibrary::Unregister("DeferredPointSpotLight"); NzShaderLibrary::Unregister("DeferredBloomBright"); NzShaderLibrary::Unregister("DeferredBloomFinal"); NzShaderLibrary::Unregister("DeferredFXAA"); NzShaderLibrary::Unregister("DeferredGaussianBlur"); } bool NzDeferredRenderTechnique::RenderPassComparator::operator()(nzRenderPassType pass1, nzRenderPassType pass2) { return RenderPassPriority[pass1] < RenderPassPriority[pass2]; }