// Copyright (C) 2013 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { NzShaderProgram* BuildClearProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec2 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 0.0, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } return program.release(); } NzShaderProgram* BuildDirectionalLightProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec2 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 0.0, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("GBuffer0"), 0); program->SendInteger(program->GetUniformLocation("GBuffer1"), 1); program->SendInteger(program->GetUniformLocation("GBuffer2"), 2); return program.release(); } NzShaderProgram* BuildPointLightProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "uniform mat4 WorldViewProjMatrix;\n" "void main()\n" "{\n" "\t" "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("GBuffer0"), 0); program->SendInteger(program->GetUniformLocation("GBuffer1"), 1); program->SendInteger(program->GetUniformLocation("GBuffer2"), 2); return program.release(); } NzShaderProgram* BuildSpotLightProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "uniform mat4 WorldViewProjMatrix;\n" "void main()\n" "{\n" "\t" "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("GBuffer0"), 0); program->SendInteger(program->GetUniformLocation("GBuffer1"), 1); program->SendInteger(program->GetUniformLocation("GBuffer2"), 2); return program.release(); } NzShaderProgram* BuildBloomBrightProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("ColorTexture"), 0); return program.release(); } NzShaderProgram* BuildGaussianBlurProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("ColorTexture"), 0); return program.release(); } NzShaderProgram* BuildBloomFinalProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("ColorTexture"), 0); program->SendInteger(program->GetUniformLocation("BloomTexture"), 1); return program.release(); } NzShaderProgram* BuildAAProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("GBuffer1"), 0); program->SendInteger(program->GetUniformLocation("NoiseTexture"), 1); return program.release(); } NzShaderProgram* BuildSSAOProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("GBuffer1"), 0); program->SendInteger(program->GetUniformLocation("NoiseTexture"), 1); return program.release(); } NzShaderProgram* BuildSSAOFinalProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("ColorTexture"), 0); program->SendInteger(program->GetUniformLocation("SSAOTexture"), 1); return program.release(); } // http://digitalerr0r.wordpress.com/2009/05/16/xna-shader-programming-tutorial-20-depth-of-field/ NzShaderProgram* BuildDepthOfFieldProgram() { const char* fragmentSource = "#version 140\n" "out vec4 RenderTarget0;\n" "uniform sampler2D BlurTexture;\n" "uniform sampler2D ColorTexture;\n" "uniform sampler2D GBuffer1;\n" "uniform vec2 InvTargetSize;" "\n" "float Distance = 30.0;\n" "float Range = 100.0;\n" "float Near = 0.1;\n" "float Far = (5000.0) / (5000.0 - 0.1);\n" //"float Far = 50.0;\n" "void main()\n" "{\n" "vec2 texCoord = gl_FragCoord.xy * InvTargetSize;\n" "// Get our original pixel from ColorMap\n" "vec3 color = textureLod(ColorTexture, texCoord, 0.0).rgb;\n" "// Get our bloom pixel from bloom texture\n" "vec3 blur = textureLod(BlurTexture, texCoord, 0.0).rgb;\n" "float depth = textureLod(GBuffer1, texCoord, 0.0).w;\n" "depth = (2.0 * 0.1) / (5000.0 + 0.1 - depth * (5000.0 - 0.1));" "depth = 1.0 - depth;\n" "float fSceneZ = ( -Near * Far ) / ( depth - Far);\n" "float blurFactor = clamp(abs(fSceneZ - Distance)/Range, 0.0, 1.0);\n" "RenderTarget0 = vec4(mix(color, blur, blurFactor), 1.0);\n" "}\n"; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, fragmentSource)) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("ColorTexture"), 0); program->SendInteger(program->GetUniformLocation("BlurTexture"), 1); program->SendInteger(program->GetUniformLocation("GBuffer1"), 2); return program.release(); } NzShaderProgram* BuildBlitProgram() { const char fragmentSource[] = { #include }; const char* vertexSource = "#version 140\n" "in vec3 VertexPosition;\n" "void main()\n" "{\n" "\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n" "}\n"; ///TODO: Remplacer ça par des ShaderNode std::unique_ptr program(new NzShaderProgram(nzShaderLanguage_GLSL)); program->SetPersistent(false); if (!program->LoadShader(nzShaderType_Fragment, NzString(fragmentSource, sizeof(fragmentSource)))) { NazaraError("Failed to load fragment shader"); return nullptr; } if (!program->LoadShader(nzShaderType_Vertex, vertexSource)) { NazaraError("Failed to load vertex shader"); return nullptr; } if (!program->Compile()) { NazaraError("Failed to compile program"); return nullptr; } program->SendInteger(program->GetUniformLocation("ColorTexture"), 0); return program.release(); } } NzDeferredRenderTechnique::NzDeferredRenderTechnique() : m_renderQueue(static_cast(m_forwardTechnique.GetRenderQueue())), m_GBufferSize(0, 0), m_texturesUpdated(false) { m_aaProgram = BuildAAProgram(); m_blitProgram = BuildBlitProgram(); m_bloomBrightProgram = BuildBloomBrightProgram(); m_bloomFinalProgram = BuildBloomFinalProgram(); m_clearProgram = BuildClearProgram(); m_clearStates.parameters[nzRendererParameter_DepthBuffer] = true; m_clearStates.parameters[nzRendererParameter_FaceCulling] = true; m_clearStates.parameters[nzRendererParameter_StencilTest] = true; m_clearStates.depthFunc = nzRendererComparison_Always; m_clearStates.frontFace.stencilCompare = nzRendererComparison_Always; m_clearStates.frontFace.stencilPass = nzStencilOperation_Zero; m_gaussianBlurProgram = BuildGaussianBlurProgram(); m_gaussianBlurProgramFilterLocation = m_gaussianBlurProgram->GetUniformLocation("Filter"); m_directionalLightProgram = BuildDirectionalLightProgram(); m_pointLightProgram = BuildPointLightProgram(); m_spotLightProgram = BuildSpotLightProgram(); m_depthOfFieldProgram = BuildDepthOfFieldProgram(); m_bilinearSampler.SetAnisotropyLevel(1); m_bilinearSampler.SetFilterMode(nzSamplerFilter_Bilinear); m_bilinearSampler.SetWrapMode(nzSamplerWrap_Clamp); m_pointSampler.SetAnisotropyLevel(1); m_pointSampler.SetFilterMode(nzSamplerFilter_Nearest); m_pointSampler.SetWrapMode(nzSamplerWrap_Clamp); m_ssaoSampler.SetAnisotropyLevel(1); m_ssaoSampler.SetFilterMode(nzSamplerFilter_Nearest); m_ssaoSampler.SetWrapMode(nzSamplerWrap_Repeat); /*const unsigned int kernelSize = 16; std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution dis(-1.f, 1.f); std::uniform_real_distribution dis2(0.f, 1.f); std::vector kernel(kernelSize); for (unsigned int i = 0; i < kernelSize; ++i) { kernel[i].Set(dis(gen), dis(gen), dis2(gen)); kernel[i].Normalize(); float scale = static_cast(i) / kernelSize; kernel[i] *= NzLerp(0.1f, 1.0f, scale * scale); } int noiseDataSize = 4 * 4; std::vector noiseData(noiseDataSize*2); for (int i = 0; i < noiseDataSize; ++i) { NzVector2f vec(dis(gen), dis(gen)); vec.Normalize(); noiseData[i*2 + 0] = static_cast((vec.x*0.5f + 0.5f) * 0xFF); noiseData[i*2 + 1] = static_cast((vec.y*0.5f + 0.5f) * 0xFF); } m_ssaoNoiseTexture = new NzTexture; m_ssaoNoiseTexture->SetPersistent(false); m_ssaoNoiseTexture->Create(nzImageType_2D, nzPixelFormat_RG8, 4, 4); m_ssaoNoiseTexture->Update(&noiseData[0]);*/ m_ssaoNoiseTexture = new NzTexture; m_ssaoNoiseTexture->SetPersistent(false); m_ssaoNoiseTexture->LoadFromFile("resources/noise.jpg"); m_ssaoProgram = BuildSSAOProgram(); m_ssaoProgram->SendInteger(m_ssaoProgram->GetUniformLocation("NoiseTextureSize"), m_ssaoNoiseTexture->GetWidth()); //m_ssaoProgram->SendInteger(m_ssaoProgram->GetUniformLocation("ssaoKernelSize"), kernelSize); //m_ssaoProgram->SendVectorArray(m_ssaoProgram->GetUniformLocation("ssaoKernelOffsets"), &kernel[0], kernelSize); m_ssaoFinalProgram = BuildSSAOFinalProgram(); m_sphere = new NzMesh; m_sphere->SetPersistent(false); m_sphere->CreateStatic(); m_sphereMesh = static_cast(m_sphere->BuildSubMesh(NzPrimitive::IcoSphere(1.f, 1))); m_bloomTextureA = new NzTexture; m_bloomTextureA->SetPersistent(false); m_bloomTextureB = new NzTexture; m_bloomTextureB->SetPersistent(false); m_dofTextureA = new NzTexture; m_dofTextureA->SetPersistent(false); m_dofTextureB = new NzTexture; m_dofTextureB->SetPersistent(false); m_ssaoTextureA = new NzTexture; m_ssaoTextureA->SetPersistent(false); m_ssaoTextureB = new NzTexture; m_ssaoTextureB->SetPersistent(false); m_workTextureA = new NzTexture; m_workTextureA->SetPersistent(false); m_workTextureB = new NzTexture; m_workTextureB->SetPersistent(false); for (unsigned int i = 0; i < 3; ++i) { m_GBuffer[i] = new NzTexture; m_GBuffer[i]->SetPersistent(false); } } NzDeferredRenderTechnique::~NzDeferredRenderTechnique() { } void NzDeferredRenderTechnique::Clear(const NzScene* scene) { NazaraUnused(scene); } bool NzDeferredRenderTechnique::Draw(const NzScene* scene) { NzAbstractViewer* viewer = scene->GetViewer(); NzRecti viewerViewport = NzRenderer::GetViewport(); if (static_cast(viewerViewport.width) != m_GBufferSize.x || static_cast(viewerViewport.height) != m_GBufferSize.y) { m_GBufferSize.Set(viewerViewport.width, viewerViewport.height); m_texturesUpdated = false; } if (!m_texturesUpdated && !UpdateTextures()) { NazaraError("Failed to update RTT"); return false; } NzRenderer::SetTarget(&m_geometryRTT); NzRenderer::SetViewport(NzRecti(0, 0, m_GBufferSize.x, m_GBufferSize.y)); /****************************Passe géométrique****************************/ m_geometryRTT.SetColorTargets({0, 1, 2}); // G-Buffer NzRenderer::SetRenderStates(m_clearStates); NzRenderer::SetShaderProgram(m_clearProgram); NzRenderer::DrawFullscreenQuad(); NzRenderer::SetMatrix(nzMatrixType_Projection, viewer->GetProjectionMatrix()); NzRenderer::SetMatrix(nzMatrixType_View, viewer->GetViewMatrix()); GeomPass(scene); /****************************Passe d'éclairage****************************/ m_geometryRTT.SetColorTarget(4); // workTextureA NzRenderer::SetTexture(0, m_GBuffer[0]); NzRenderer::SetTextureSampler(0, m_pointSampler); NzRenderer::SetTexture(1, m_GBuffer[1]); NzRenderer::SetTextureSampler(1, m_pointSampler); NzRenderer::SetTexture(2, m_GBuffer[2]); NzRenderer::SetTextureSampler(2, m_pointSampler); NzRenderer::SetClearColor(NzColor::Black); NzRenderer::Clear(nzRendererClear_Color); NzRenderStates lightStates; lightStates.dstBlend = nzBlendFunc_One; lightStates.srcBlend = nzBlendFunc_One; lightStates.parameters[nzRendererParameter_Blend] = true; lightStates.parameters[nzRendererParameter_DepthBuffer] = true; lightStates.parameters[nzRendererParameter_DepthWrite] = false; // Directional lights if (!m_renderQueue.directionalLights.empty()) { NzRenderer::SetRenderStates(lightStates); DirectionalLightPass(scene); } // Point lights/Spot lights if (!m_renderQueue.pointLights.empty() || !m_renderQueue.spotLights.empty()) { // http://www.altdevblogaday.com/2011/08/08/stencil-buffer-optimisation-for-deferred-lights/ lightStates.parameters[nzRendererParameter_StencilTest] = true; lightStates.faceCulling = nzFaceSide_Front; lightStates.backFace.stencilMask = 0xFF; lightStates.backFace.stencilReference = 0; lightStates.backFace.stencilFail = nzStencilOperation_Keep; lightStates.backFace.stencilPass = nzStencilOperation_Keep; lightStates.backFace.stencilZFail = nzStencilOperation_Invert; lightStates.frontFace.stencilMask = 0xFF; lightStates.frontFace.stencilReference = 0; lightStates.frontFace.stencilFail = nzStencilOperation_Keep; lightStates.frontFace.stencilPass = nzStencilOperation_Keep; lightStates.frontFace.stencilZFail = nzStencilOperation_Invert; NzRenderer::SetRenderStates(lightStates); if (!m_renderQueue.pointLights.empty()) PointLightPass(scene); if (!m_renderQueue.spotLights.empty()) SpotLightPass(scene); NzRenderer::Enable(nzRendererParameter_StencilTest, false); } /******************************Passe forward******************************/ NzAbstractBackground* background = scene->GetBackground(); if (background) background->Draw(scene); NzRenderer::SetMatrix(nzMatrixType_Projection, viewer->GetProjectionMatrix()); NzRenderer::SetMatrix(nzMatrixType_View, viewer->GetViewMatrix()); m_forwardTechnique.Draw(scene); NzRenderStates states; states.parameters[nzRendererParameter_DepthBuffer] = false; NzRenderer::SetRenderStates(states); /****************************SSAO***************************/ NzRenderer::SetShaderProgram(m_ssaoProgram); NzRenderer::SetTarget(&m_ssaoRTT); NzRenderer::SetViewport(NzRecti(0, 0, m_GBufferSize.x/4, m_GBufferSize.y/4)); m_ssaoRTT.SetColorTarget(0); // ssaoTextureA NzRenderer::SetTexture(0, m_GBuffer[1]); NzRenderer::SetTexture(1, m_ssaoNoiseTexture); NzRenderer::SetTextureSampler(0, m_pointSampler); NzRenderer::SetTextureSampler(1, m_ssaoSampler); NzRenderer::DrawFullscreenQuad(); NzRenderer::SetShaderProgram(m_gaussianBlurProgram); const unsigned int ssaoBlurPass = 2; for (unsigned int i = 0; i < ssaoBlurPass; ++i) { m_ssaoRTT.SetColorTarget(1); // ssaoTextureB m_gaussianBlurProgram->SendVector(m_gaussianBlurProgramFilterLocation, NzVector2f(1.f, 0.f)); NzRenderer::SetTexture(0, m_ssaoTextureA); NzRenderer::DrawFullscreenQuad(); m_ssaoRTT.SetColorTarget(0); // ssaoTextureA m_gaussianBlurProgram->SendVector(m_gaussianBlurProgramFilterLocation, NzVector2f(0.f, 1.f)); NzRenderer::SetTexture(0, m_ssaoTextureB); NzRenderer::DrawFullscreenQuad(); } NzRenderer::SetTarget(&m_geometryRTT); NzRenderer::SetViewport(NzRecti(0, 0, m_GBufferSize.x, m_GBufferSize.y)); m_geometryRTT.SetColorTarget(5); // workTextureB NzRenderer::SetShaderProgram(m_ssaoFinalProgram); NzRenderer::SetTexture(0, m_workTextureA); NzRenderer::SetTexture(1, m_ssaoTextureA); NzRenderer::SetTextureSampler(0, m_pointSampler); NzRenderer::SetTextureSampler(1, m_bilinearSampler); NzRenderer::DrawFullscreenQuad(); /****************************AA***************************/ NzRenderer::SetShaderProgram(m_aaProgram); m_geometryRTT.SetColorTarget(4); // workTextureA NzRenderer::SetTexture(0, m_workTextureB); NzRenderer::SetTextureSampler(0, m_pointSampler); NzRenderer::DrawFullscreenQuad(); /****************************Bloom***************************/ NzRenderer::SetTextureSampler(0, m_bilinearSampler); NzRenderer::SetTextureSampler(1, m_bilinearSampler); m_geometryRTT.SetColorTarget(5); // workTextureB NzRenderer::SetShaderProgram(m_bloomBrightProgram); NzRenderer::SetTexture(0, m_workTextureA); NzRenderer::DrawFullscreenQuad(); NzRenderer::SetTarget(&m_bloomRTT); NzRenderer::SetViewport(NzRecti(0, 0, m_GBufferSize.x/8, m_GBufferSize.y/8)); NzRenderer::SetShaderProgram(m_gaussianBlurProgram); const unsigned int bloomBlurPass = 5; for (unsigned int i = 0; i < bloomBlurPass; ++i) { m_bloomRTT.SetColorTarget(0); // bloomTextureA m_gaussianBlurProgram->SendVector(m_gaussianBlurProgramFilterLocation, NzVector2f(1.f, 0.f)); NzRenderer::SetTexture(0, (i == 0) ? m_workTextureB : m_bloomTextureB); NzRenderer::DrawFullscreenQuad(); m_bloomRTT.SetColorTarget(1); // bloomTextureB m_gaussianBlurProgram->SendVector(m_gaussianBlurProgramFilterLocation, NzVector2f(0.f, 1.f)); NzRenderer::SetTexture(0, m_bloomTextureA); NzRenderer::DrawFullscreenQuad(); } NzRenderer::SetTarget(&m_geometryRTT); NzRenderer::SetViewport(NzRecti(0, 0, m_GBufferSize.x, m_GBufferSize.y)); m_geometryRTT.SetColorTarget(5); // workTextureB NzRenderer::SetShaderProgram(m_bloomFinalProgram); NzRenderer::SetTexture(0, m_workTextureA); NzRenderer::SetTexture(1, m_bloomTextureB); NzRenderer::DrawFullscreenQuad(); /****************************Depth-of-Field***************************/ /* NzRenderer::SetTextureSampler(0, m_pointSampler); NzRenderer::SetTextureSampler(1, m_bilinearSampler); NzRenderer::SetTextureSampler(2, m_pointSampler); NzRenderer::SetTarget(&m_dofRTT); NzRenderer::SetViewport(NzRecti(0, 0, m_GBufferSize.x/4, m_GBufferSize.y/4)); NzRenderer::SetShaderProgram(m_gaussianBlurProgram); const unsigned int dofBlurPass = 2; for (unsigned int i = 0; i < dofBlurPass; ++i) { m_dofRTT.SetColorTarget(0); // dofTextureA m_gaussianBlurProgram->SendVector(m_gaussianBlurProgramFilterLocation, NzVector2f(1.f, 0.f)); NzRenderer::SetTexture(0, (i == 0) ? m_workTextureB : m_dofTextureB); NzRenderer::DrawFullscreenQuad(); m_dofRTT.SetColorTarget(1); // dofTextureB m_gaussianBlurProgram->SendVector(m_gaussianBlurProgramFilterLocation, NzVector2f(0.f, 1.f)); NzRenderer::SetTexture(0, m_dofTextureA); NzRenderer::DrawFullscreenQuad(); } NzRenderer::SetTarget(&m_geometryRTT); NzRenderer::SetViewport(NzRecti(0, 0, m_GBufferSize.x, m_GBufferSize.y)); m_geometryRTT.SetColorTarget(4); // workTextureA NzRenderer::SetShaderProgram(m_depthOfFieldProgram); NzRenderer::SetTexture(0, m_workTextureB); NzRenderer::SetTexture(1, m_dofTextureB); NzRenderer::SetTexture(2, m_GBuffer[1]); NzRenderer::DrawFullscreenQuad(); */ /*******************************Passe finale******************************/ scene->GetViewer()->ApplyView(); NzRenderer::SetRenderStates(states); NzRenderer::SetShaderProgram(m_blitProgram); NzRenderer::SetTexture(0, m_workTextureB); NzRenderer::SetTextureSampler(0, m_pointSampler); NzRenderer::DrawFullscreenQuad(); return true; } 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]; } NzAbstractRenderQueue* NzDeferredRenderTechnique::GetRenderQueue() { return &m_renderQueue; } nzRenderTechniqueType NzDeferredRenderTechnique::GetType() const { return nzRenderTechniqueType_DeferredShading; } NzTexture* NzDeferredRenderTechnique::GetWorkTexture(unsigned int i) const { #if NAZARA_GRAPHICS_SAFE if (i >= 2) { NazaraError("GBuffer texture index out of range (" + NzString::Number(i) + " >= 3)"); return nullptr; } #endif return (i == 0) ? m_workTextureA : m_workTextureB; } bool NzDeferredRenderTechnique::IsSupported() { // On ne va pas s'embêter à écrire un Deferred Renderer qui ne passe pas par le MRT, ce serait lent et inutile (OpenGL 2 garanti cette fonctionnalité en plus) return NzRenderer::HasCapability(nzRendererCap_RenderTexture) && NzRenderer::HasCapability(nzRendererCap_MultipleRenderTargets) && NzRenderer::GetMaxColorAttachments() >= 6 && //FIXME: Repasser à quatre NzRenderer::GetMaxRenderTargets() >= 4 && NzTexture::IsFormatSupported(nzPixelFormat_RGBA32F); } void NzDeferredRenderTechnique::GeomPass(const NzScene* scene) { NzAbstractViewer* viewer = scene->GetViewer(); const NzShaderProgram* lastProgram = nullptr; for (auto& matIt : m_renderQueue.opaqueModels) { bool& used = std::get<0>(matIt.second); if (used) { bool& renderQueueInstancing = std::get<1>(matIt.second); NzDeferredRenderQueue::BatchedSkeletalMeshContainer& skeletalContainer = std::get<2>(matIt.second); NzDeferredRenderQueue::BatchedStaticMeshContainer& staticContainer = std::get<3>(matIt.second); if (!skeletalContainer.empty() || !staticContainer.empty()) { const NzMaterial* material = matIt.first; // Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active // Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches // (Le deferred shading n'a pas ce problème) bool instancing = m_instancingEnabled && renderQueueInstancing; // On commence par récupérer le programme du matériau nzUInt32 flags = nzShaderFlags_Deferred; if (instancing) flags |= nzShaderFlags_Instancing; const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, flags); // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas if (program != lastProgram) { NzRenderer::SetShaderProgram(program); // Couleur ambiante de la scène program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); // Position de la caméra program->SendVector(program->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition()); lastProgram = program; } material->Apply(program); // Meshs squelettiques /*if (!skeletalContainer.empty()) { NzRenderer::SetVertexBuffer(m_skinningBuffer); // Vertex buffer commun for (auto& subMeshIt : container) { ///TODO } }*/ // Meshs statiques for (auto& subMeshIt : staticContainer) { const NzStaticMesh* mesh = subMeshIt.first; std::vector& staticData = subMeshIt.second; if (!staticData.empty()) { const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer(); const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer(); // Gestion du draw call avant la boucle de rendu std::function DrawFunc; std::function InstancedDrawFunc; unsigned int indexCount; if (indexBuffer) { DrawFunc = NzRenderer::DrawIndexedPrimitives; InstancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced; indexCount = indexBuffer->GetIndexCount(); } else { DrawFunc = NzRenderer::DrawPrimitives; InstancedDrawFunc = NzRenderer::DrawPrimitivesInstanced; indexCount = vertexBuffer->GetVertexCount(); } NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetVertexBuffer(vertexBuffer); nzPrimitiveMode primitiveMode = mesh->GetPrimitiveMode(); if (instancing) { NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4)); unsigned int stride = instanceBuffer->GetStride(); const NzDeferredRenderQueue::StaticData* data = &staticData[0]; unsigned int instanceCount = staticData.size(); unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); while (instanceCount > 0) { unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); instanceCount -= renderedInstanceCount; NzBufferMapper mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount); nzUInt8* ptr = reinterpret_cast(mapper.GetPointer()); for (unsigned int i = 0; i < renderedInstanceCount; ++i) { std::memcpy(ptr, data->transformMatrix, sizeof(float)*16); data++; ptr += stride; } mapper.Unmap(); InstancedDrawFunc(renderedInstanceCount, primitiveMode, 0, indexCount); } } else { for (const NzDeferredRenderQueue::StaticData& data : staticData) { NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix); DrawFunc(primitiveMode, 0, indexCount); } } staticData.clear(); } } } // Et on remet à zéro les données renderQueueInstancing = false; used = false; } } } void NzDeferredRenderTechnique::DirectionalLightPass(const NzScene* scene) { NzRenderer::SetShaderProgram(m_directionalLightProgram); m_directionalLightProgram->SendColor(m_directionalLightProgram->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); m_directionalLightProgram->SendVector(m_directionalLightProgram->GetUniformLocation(nzShaderUniform_EyePosition), scene->GetViewer()->GetEyePosition()); for (const NzLight* light : m_renderQueue.directionalLights) { light->Enable(m_directionalLightProgram, 0); NzRenderer::DrawFullscreenQuad(); } } void NzDeferredRenderTechnique::PointLightPass(const NzScene* scene) { NzRenderer::SetShaderProgram(m_pointLightProgram); m_pointLightProgram->SendColor(m_pointLightProgram->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); m_pointLightProgram->SendVector(m_pointLightProgram->GetUniformLocation(nzShaderUniform_EyePosition), scene->GetViewer()->GetEyePosition()); const NzIndexBuffer* indexBuffer = m_sphereMesh->GetIndexBuffer(); NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetVertexBuffer(m_sphereMesh->GetVertexBuffer()); NzMatrix4f lightMatrix; lightMatrix.MakeIdentity(); for (const NzLight* light : m_renderQueue.pointLights) { light->Enable(m_pointLightProgram, 0); lightMatrix.SetScale(NzVector3f(light->GetRadius()*1.1f)); lightMatrix.SetTranslation(light->GetPosition()); NzRenderer::SetMatrix(nzMatrixType_World, lightMatrix); // Rendu de la sphère dans le stencil buffer NzRenderer::Enable(nzRendererParameter_ColorWrite, false); NzRenderer::Enable(nzRendererParameter_DepthBuffer, true); NzRenderer::Enable(nzRendererParameter_FaceCulling, false); NzRenderer::SetStencilCompareFunction(nzRendererComparison_Always); NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount()); // Rendu de la sphère comme zone d'effet NzRenderer::Enable(nzRendererParameter_ColorWrite, true); NzRenderer::Enable(nzRendererParameter_DepthBuffer, false); NzRenderer::Enable(nzRendererParameter_FaceCulling, true); NzRenderer::SetFaceCulling(nzFaceSide_Front); NzRenderer::SetStencilCompareFunction(nzRendererComparison_NotEqual, nzFaceSide_Back); NzRenderer::SetStencilPassOperation(nzStencilOperation_Zero, nzFaceSide_Back); NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount()); } } void NzDeferredRenderTechnique::SpotLightPass(const NzScene* scene) { NzRenderer::SetShaderProgram(m_spotLightProgram); m_spotLightProgram->SendColor(m_spotLightProgram->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); m_spotLightProgram->SendVector(m_spotLightProgram->GetUniformLocation(nzShaderUniform_EyePosition), scene->GetViewer()->GetEyePosition()); const NzIndexBuffer* indexBuffer = m_sphereMesh->GetIndexBuffer(); NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetVertexBuffer(m_sphereMesh->GetVertexBuffer()); for (const NzLight* light : m_renderQueue.spotLights) { light->Enable(m_spotLightProgram, 0); NzMatrix4f lightMatrix; lightMatrix.MakeIdentity(); lightMatrix.SetScale(NzVector3f(light->GetRadius()*1.1f)); lightMatrix.SetTranslation(light->GetPosition()); NzRenderer::SetMatrix(nzMatrixType_World, lightMatrix); // Rendu de la sphère dans le stencil buffer NzRenderer::Enable(nzRendererParameter_ColorWrite, false); NzRenderer::Enable(nzRendererParameter_DepthBuffer, true); NzRenderer::Enable(nzRendererParameter_FaceCulling, false); NzRenderer::SetStencilCompareFunction(nzRendererComparison_Always); NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount()); // Rendu de la sphère comme zone d'effet NzRenderer::Enable(nzRendererParameter_ColorWrite, true); NzRenderer::Enable(nzRendererParameter_DepthBuffer, false); NzRenderer::Enable(nzRendererParameter_FaceCulling, true); NzRenderer::SetFaceCulling(nzFaceSide_Front); NzRenderer::SetStencilCompareFunction(nzRendererComparison_NotEqual, nzFaceSide_Back); NzRenderer::SetStencilPassOperation(nzStencilOperation_Zero, nzFaceSide_Back); NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount()); } } bool NzDeferredRenderTechnique::UpdateTextures() const { /* G-Buffer: Texture0: Diffuse Color + Flags Texture1: Normal map + Depth Texture2: Specular value + Shininess Texture3: N/A */ try { NzErrorFlags errFlags(nzErrorFlag_ThrowException); unsigned int width = m_GBufferSize.x; unsigned int height = m_GBufferSize.y; m_GBuffer[0]->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height); m_GBuffer[1]->Create(nzImageType_2D, nzPixelFormat_RGBA32F, width, height); m_GBuffer[2]->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height); m_bloomTextureA->Create(nzImageType_2D, nzPixelFormat_RGBA8, width/8, height/8); m_bloomTextureB->Create(nzImageType_2D, nzPixelFormat_RGBA8, width/8, height/8); m_dofTextureA->Create(nzImageType_2D, nzPixelFormat_RGB8, width/4, height/4); m_dofTextureB->Create(nzImageType_2D, nzPixelFormat_RGB8, width/4, height/4); m_ssaoTextureA->Create(nzImageType_2D, nzPixelFormat_R8, width/4, height/4); m_ssaoTextureB->Create(nzImageType_2D, nzPixelFormat_R8, width/4, height/4); m_workTextureA->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height); m_workTextureB->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height); m_geometryRTT.Create(true); // Texture 0 : Diffuse Color + Flags m_geometryRTT.AttachTexture(nzAttachmentPoint_Color, 0, m_GBuffer[0]); // Texture 1 : Normal map + Depth m_geometryRTT.AttachTexture(nzAttachmentPoint_Color, 1, m_GBuffer[1]); // Texture 2 : Specular value + Shininess m_geometryRTT.AttachTexture(nzAttachmentPoint_Color, 2, m_GBuffer[2]); // Texture 3 : Emission map ? // Texture 4 : Target A m_geometryRTT.AttachTexture(nzAttachmentPoint_Color, 4, m_workTextureA); // Texture 5 : Target B m_geometryRTT.AttachTexture(nzAttachmentPoint_Color, 5, m_workTextureB); // Depth/stencil buffer m_geometryRTT.AttachBuffer(nzAttachmentPoint_DepthStencil, 0, nzPixelFormat_Depth24Stencil8, width, height); m_geometryRTT.Unlock(); m_bloomRTT.Create(true); m_bloomRTT.AttachTexture(nzAttachmentPoint_Color, 0, m_bloomTextureA); m_bloomRTT.AttachTexture(nzAttachmentPoint_Color, 1, m_bloomTextureB); m_bloomRTT.Unlock(); m_dofRTT.Create(true); m_dofRTT.AttachTexture(nzAttachmentPoint_Color, 0, m_dofTextureA); m_dofRTT.AttachTexture(nzAttachmentPoint_Color, 1, m_dofTextureB); m_dofRTT.Unlock(); m_ssaoRTT.Create(true); m_ssaoRTT.AttachTexture(nzAttachmentPoint_Color, 0, m_ssaoTextureA); m_ssaoRTT.AttachTexture(nzAttachmentPoint_Color, 1, m_ssaoTextureB); m_ssaoRTT.Unlock(); if (!m_bloomRTT.IsComplete() || !m_dofRTT.IsComplete() || !m_geometryRTT.IsComplete() || !m_ssaoRTT.IsComplete()) { NazaraError("Incomplete RTT"); return false; } m_texturesUpdated = true; return true; } catch (const std::exception& e) { NazaraError("Failed to create work RTT/G-Buffer"); return false; } }