NazaraEngine/src/Nazara/Graphics/DeferredRenderTechnique.cpp

1340 lines
40 KiB
C++

// 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 <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/AbstractBackground.hpp>
#include <Nazara/Graphics/Camera.hpp>
#include <Nazara/Graphics/Drawable.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Material.hpp>
#include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/ShaderProgramManager.hpp>
#include <Nazara/Utility/BufferMapper.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <limits>
#include <memory>
#include <random>
#include <Nazara/Graphics/Debug.hpp>
namespace
{
NzShaderProgram* BuildClearProgram()
{
const char fragmentSource[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/ClearGBuffer.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/DirectionalLight.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/PointLight.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/SpotLight.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/BloomBright.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/GaussianBlur.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/BloomFinal.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/FXAA.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/SSAO.frag.h>
};
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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/SSAOFinal.frag.h>
};
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<NzShaderProgram> 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<NzShaderProgram> 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 <Nazara/Graphics/Resources/DeferredShading/Shaders/Blit.frag.h>
};
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<NzShaderProgram> 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<NzForwardRenderQueue*>(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<float> dis(-1.f, 1.f);
std::uniform_real_distribution<float> dis2(0.f, 1.f);
std::vector<NzVector3f> 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<float>(i) / kernelSize;
kernel[i] *= NzLerp(0.1f, 1.0f, scale * scale);
}
int noiseDataSize = 4 * 4;
std::vector<nzUInt8> noiseData(noiseDataSize*2);
for (int i = 0; i < noiseDataSize; ++i)
{
NzVector2f vec(dis(gen), dis(gen));
vec.Normalize();
noiseData[i*2 + 0] = static_cast<nzUInt8>((vec.x*0.5f + 0.5f) * 0xFF);
noiseData[i*2 + 1] = static_cast<nzUInt8>((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<NzStaticMesh*>(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<unsigned int>(viewerViewport.width) != m_GBufferSize.x || static_cast<unsigned int>(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<NzDeferredRenderQueue::StaticData>& 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<void(nzPrimitiveMode, unsigned int, unsigned int)> DrawFunc;
std::function<void(unsigned int, nzPrimitiveMode, unsigned int, unsigned int)> 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<NzVertexBuffer> mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount);
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(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;
}
}