Added ShaderManager (Experimental)

Former-commit-id: 327e373f2b932e31184e88c5f29bd5bd8fa3ba46
This commit is contained in:
Lynix
2013-07-15 00:23:04 +02:00
parent 68d5f9a8e6
commit 4352083c4b
23 changed files with 1367 additions and 955 deletions

View File

@@ -2,101 +2,23 @@
// 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 <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Graphics/ColorBackGround.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
#include <Nazara/Renderer/ShaderManager.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
{
NzShader* BuildShader()
{
const char* fragmentSource110 =
"#version 110\n"
"uniform vec4 Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Color;\n"
"}\n";
const char* fragmentSource140 =
"#version 140\n"
"out vec4 RenderTarget0;\n"
"uniform vec4 Color;\n"
"void main()\n"
"{\n"
" RenderTarget0 = Color;\n"
"}\n";
const char* vertexSource110 =
"#version 110\n"
"attribute vec2 VertexPosition;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(VertexPosition, 0.0, 1.0);\n"
"}\n";
const char* vertexSource140 =
"#version 140\n"
"in vec2 VertexPosition;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(VertexPosition, 0.0, 1.0);\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
std::unique_ptr<NzShader> shader(new NzShader(nzShaderLanguage_GLSL));
shader->SetPersistent(false);
bool useGLSL140 = (NzOpenGL::GetVersion() >= 310);
if (!shader->Load(nzShaderType_Fragment, (useGLSL140) ? fragmentSource140 : fragmentSource110))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->Load(nzShaderType_Vertex, (useGLSL140) ? vertexSource140 : vertexSource110))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Compile())
{
NazaraError("Failed to compile shader");
return nullptr;
}
return shader.release();
}
static NzShader* s_shader = nullptr;
static unsigned int s_colorLocation;
}
NzColorBackground::NzColorBackground(const NzColor& color) :
m_color(color)
{
if (!s_shader)
{
s_shader = BuildShader();
s_colorLocation = s_shader->GetUniformLocation("Color");
}
NzShaderManagerParams params;
params.target = nzShaderTarget_FullscreenQuad;
params.flags = 0;
params.fullscreenQuad.alphaMapping = false;
params.fullscreenQuad.alphaTest = false;
params.fullscreenQuad.diffuseMapping = false;
m_shader = s_shader;
}
NzColorBackground::~NzColorBackground()
{
if (m_shader.Reset())
s_shader = nullptr;
m_shader = NzShaderManager::Get(params);
}
void NzColorBackground::Draw(const NzScene* scene) const
@@ -105,7 +27,7 @@ void NzColorBackground::Draw(const NzScene* scene) const
static NzRenderStates states;
m_shader->SendColor(s_colorLocation, m_color);
m_shader->SendColor(m_shader->GetUniformLocation(nzShaderUniform_MaterialDiffuse), m_color);
NzRenderer::SetRenderStates(states);
NzRenderer::SetShader(m_shader);

View File

@@ -142,7 +142,14 @@ void NzForwardRenderQueue::AddModel(const NzModel* model)
if (pair2.second)
staticMesh->AddResourceListener(this, ResourceType_StaticMesh);
pair2.first->second.push_back(transformMatrix);
std::vector<StaticData>& staticDataContainer = pair2.first->second;
staticDataContainer.resize(staticDataContainer.size()+1);
StaticData& data = staticDataContainer.back();
data.aabb = staticMesh->GetAABB();
data.aabb.Transform(transformMatrix);
data.transformMatrix = transformMatrix;
}
break;
@@ -257,31 +264,22 @@ bool NzForwardRenderQueue::StaticMeshComparator::operator()(const NzStaticMesh*
return buffer1 < buffer2;
}
bool NzForwardRenderQueue::MaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2)
bool NzForwardRenderQueue::ModelMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2)
{
const NzShader* shader1 = mat1->GetCustomShader();
const NzShader* shader2 = mat2->GetCustomShader();
if (shader1)
///TODO: Comparaison des shaders
for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i)
{
if (shader2)
{
if (shader1 != shader2)
return shader1 < shader2;
}
else
return true;
}
else if (shader2)
return false;
else
{
nzUInt32 shaderFlags1 = mat1->GetShaderFlags();
nzUInt32 shaderFlags2 = mat2->GetShaderFlags();
const NzShader* shader1 = mat1->GetShader(nzShaderTarget_Model, i);
const NzShader* shader2 = mat2->GetShader(nzShaderTarget_Model, i);
if (shaderFlags1 != shaderFlags2)
return shaderFlags1 < shaderFlags2;
if (shader1 != shader2)
return shader1 < shader2;
}
const NzTexture* diffuseMap1 = mat1->GetDiffuseMap();
const NzTexture* diffuseMap2 = mat2->GetDiffuseMap();
if (diffuseMap1 != diffuseMap2)
return diffuseMap1 < diffuseMap2;
return mat1 < mat2;
}

View File

@@ -10,20 +10,107 @@
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Material.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
#include <Nazara/Utility/BufferMapper.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <limits>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
{
struct LightComparator
class LightManager
{
bool operator()(const NzLight* light1, const NzLight* light2)
{
return light1->GetPosition().SquaredDistance(pos) < light2->GetPosition().SquaredDistance(pos);
}
public:
LightManager() = default;
~LightManager() = default;
NzVector3f pos;
unsigned int FindClosestLights(const NzLight** lights, unsigned int lightCount, const NzSpheref& object)
{
for (Light& light : m_lights)
{
light.light = nullptr;
light.score = std::numeric_limits<unsigned int>::max(); // Nous jouons au Golf
}
for (unsigned int i = 0; i < lightCount; ++i)
{
const NzLight* light = *lights;
unsigned int score = std::numeric_limits<unsigned int>::max();
switch (light->GetLightType())
{
case nzLightType_Directional:
score = 0; // Lumière choisie d'office
break;
case nzLightType_Point:
{
NzSpheref lightSphere(light->GetPosition(), light->GetRadius());
if (lightSphere.Intersect(object))
score = static_cast<unsigned int>(light->GetPosition().SquaredDistance(object.GetPosition())*1000.f);
break;
}
case nzLightType_Spot:
{
NzSpheref lightSphere(light->GetPosition(), light->GetRadius());
///TODO: Attribuer bonus/malus selon l'angle du spot ?
if (lightSphere.Intersect(object))
score = static_cast<unsigned int>(light->GetPosition().SquaredDistance(object.GetPosition())*1000.f);
break;
}
}
if (score < m_lights[0].score)
{
unsigned int j;
for (j = 1; j < 3; ++j) ///TODO: Constante
{
if (score > m_lights[j].score)
break;
}
j--; // Position de la nouvelle lumière
// Décalage
std::memcpy(&m_lights[0], &m_lights[1], j*sizeof(Light));
m_lights[j].light = light;
m_lights[j].score = score;
}
lights++;
}
unsigned int i;
for (i = 0; i < 3; ++i) ///TODO: Constante
{
if (m_lights[i].light)
break;
}
return 3-i; ///TODO: Constante
}
const NzLight* GetLight(unsigned int i) const
{
///TODO: Constante
return m_lights[3-i-1].light; // Les lumières sont stockées dans l'ordre inverse (De la plus éloignée à la plus proche)
}
private:
struct Light
{
const NzLight* light;
unsigned int score;
};
Light m_lights[3]; ///TODO: Constante
};
}
@@ -46,15 +133,11 @@ void NzForwardRenderTechnique::Clear(const NzScene* scene)
void NzForwardRenderTechnique::Draw(const NzScene* scene)
{
///TODO: Regrouper les activations par méthode
LightComparator lightComparator;
LightManager lightManager;
const NzCamera* camera = scene->GetActiveCamera();
const NzShader* lastShader = nullptr;
// Externes à la boucle pour conserver leur valeurs si le shader ne change pas
unsigned int lightCount = 0;
int lightCountLocation = -1;
// Rendu des modèles opaques
for (auto& matIt : m_renderQueue.opaqueModels)
{
@@ -66,18 +149,13 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
const NzMaterial* material = matIt.first;
// On commence par récupérer le shader du matériau
const NzShader* shader;
if (material->HasCustomShader())
shader = material->GetCustomShader();
else
shader = NzShaderBuilder::Get(material->GetShaderFlags());
const NzShader* shader = material->GetShader(nzShaderTarget_Model, 0);
unsigned int lightCount = 0;
// Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même
if (shader != lastShader)
{
// On récupère l'information sur l'éclairage en même temps que la position de l'uniforme "LightCount"
lightCountLocation = shader->GetUniformLocation(nzShaderUniform_LightCount);
NzRenderer::SetShader(shader);
// Couleur ambiante de la scène
@@ -85,18 +163,11 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
// Position de la caméra
shader->SendVector(shader->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition());
lightCount = 0;
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
if (lightCountLocation != -1)
{
for (const NzLight* light : m_renderQueue.directionnalLights)
{
light->Apply(shader, lightCount++);
if (lightCount > NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT)
break; // Prévenons les bêtises des utilisateurs
}
}
lightCount = m_renderQueue.directionnalLights.size();
for (unsigned int i = 0; i < lightCount; ++i)
m_renderQueue.directionnalLights[i]->Enable(shader, i);
lastShader = shader;
}
@@ -116,8 +187,9 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
for (auto& subMeshIt : staticContainer)
{
const NzStaticMesh* mesh = subMeshIt.first;
std::vector<NzMatrix4f>& matrices = subMeshIt.second;
if (!matrices.empty())
std::vector<NzForwardRenderQueue::StaticData>& staticData = subMeshIt.second;
if (!staticData.empty())
{
const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer();
const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer();
@@ -140,28 +212,25 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
NzRenderer::SetIndexBuffer(indexBuffer);
NzRenderer::SetVertexBuffer(vertexBuffer);
for (const NzMatrix4f& matrix : matrices)
for (const NzForwardRenderQueue::StaticData& data : staticData)
{
// Calcul des lumières les plus proches
///TODO: LightManager ?
if (lightCountLocation != -1)
if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty())
{
std::vector<const NzLight*>& lights = m_renderQueue.lights;
unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), data.aabb.GetBoundingSphere());
count -= lightCount;
lightComparator.pos = matrix.GetTranslation();
std::sort(lights.begin(), lights.end(), lightComparator);
unsigned int max = std::min(std::min(NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT - lightCount, m_maxLightsPerObject), static_cast<unsigned int>(lights.size()));
for (unsigned int i = 0; i < max; ++i)
lights[i]->Apply(shader, lightCount++);
shader->SendInteger(lightCountLocation, lightCount);
for (unsigned int i = 0; i < count; ++i)
lightManager.GetLight(i)->Enable(shader, lightCount++);
}
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières
NzLight::Disable(shader, i);
NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix);
drawFunc(mesh->GetPrimitiveMode(), 0, indexCount);
}
matrices.clear();
staticData.clear();
}
}
}
@@ -175,18 +244,13 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
m_renderQueue.transparentSkeletalModels[pair.first].material;
// On commence par récupérer le shader du matériau
const NzShader* shader;
if (material->HasCustomShader())
shader = material->GetCustomShader();
else
shader = NzShaderBuilder::Get(material->GetShaderFlags());
const NzShader* shader = material->GetShader(nzShaderTarget_Model, 0);
unsigned int lightCount = 0;
// Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même
if (shader != lastShader)
{
// On récupère l'information sur l'éclairage en même temps que la position de l'uniforme "LightCount"
lightCountLocation = shader->GetUniformLocation(nzShaderUniform_LightCount);
NzRenderer::SetShader(shader);
// Couleur ambiante de la scène
@@ -194,18 +258,11 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
// Position de la caméra
shader->SendVector(shader->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition());
lightCount = 0;
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
if (lightCountLocation != -1)
{
for (const NzLight* light : m_renderQueue.directionnalLights)
{
light->Apply(shader, lightCount++);
if (lightCount > NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT)
break; // Prévenons les bêtises des utilisateurs
}
}
lightCount = m_renderQueue.directionnalLights.size();
for (unsigned int i = 0; i < lightCount; ++i)
m_renderQueue.directionnalLights[i]->Enable(shader, i);
lastShader = shader;
}
@@ -241,21 +298,18 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
NzRenderer::SetVertexBuffer(vertexBuffer);
// Calcul des lumières les plus proches
///TODO: LightManager ?
if (lightCountLocation != -1)
if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty())
{
std::vector<const NzLight*>& lights = m_renderQueue.lights;
unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), staticModel.aabb.GetBoundingSphere());
count -= lightCount;
lightComparator.pos = matrix.GetTranslation();
std::sort(lights.begin(), lights.end(), lightComparator);
unsigned int max = std::min(std::min(NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT - lightCount, m_maxLightsPerObject), static_cast<unsigned int>(lights.size()));
for (unsigned int i = 0; i < max; ++i)
lights[i]->Apply(shader, lightCount++);
shader->SendInteger(lightCountLocation, lightCount);
for (unsigned int i = 0; i < count; ++i)
lightManager.GetLight(i)->Enable(shader, lightCount++);
}
for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières
NzLight::Disable(shader, i);
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
drawFunc(mesh->GetPrimitiveMode(), 0, indexCount);
}
@@ -265,6 +319,51 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
}
}
// Les billboards
/*if (!m_renderQueue.billboards.empty())
{
//NzRenderer::SetIndexBuffer(m_billboardIndexBuffer);
NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity());
NzRenderer::SetShader(m_billboardShader);
NzRenderer::SetVertexBuffer(m_billboardVertexBuffer);
m_billboardShader->SendVector(s_cameraForwardLocation, camera->GetForward());
m_billboardShader->SendVector(s_cameraUpLocation, camera->GetUp());
m_billboardShader->SendVector(s_worldUpLocation, NzVector3f::Up());
// Couleur ambiante de la scène
m_billboardShader->SendColor(m_billboardShader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
// Position de la caméra
m_billboardShader->SendVector(m_billboardShader->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition());
lightCount = 0;
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
m_renderQueue.lights[0]->Apply(m_billboardShader, 0);
for (auto& matIt : m_renderQueue.billboards)
{
const NzMaterial* material = matIt.first;
auto& billboards = matIt.second;
material->Apply(m_billboardShader);
unsigned int billboardCount = billboards.size();
const NzForwardRenderQueue::BillboardData* data = &billboards[0];
while (billboardCount > 0)
{
unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboards);
billboardCount -= renderedBillboardCount;
m_billboardVertexBuffer->FillVertices(data, 0, renderedBillboardCount, true);
data += renderedBillboardCount;
NzRenderer::DrawPrimitives(nzPrimitiveMode_PointList, 0, renderedBillboardCount);
}
billboards.clear();
}
}*/
// Les autres drawables (Exemple: Terrain)
for (const NzDrawable* drawable : m_renderQueue.otherDrawables)
drawable->Draw();
@@ -282,5 +381,5 @@ NzAbstractRenderQueue* NzForwardRenderTechnique::GetRenderQueue()
void NzForwardRenderTechnique::SetMaxLightsPerObject(unsigned int lightCount)
{
m_maxLightsPerObject = lightCount;
m_maxLightsPerObject = lightCount; ///TODO: Vérifier par rapport à la constante
}

View File

@@ -38,7 +38,7 @@ void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const
renderQueue->AddLight(this);
}
void NzLight::Apply(const NzShader* shader, unsigned int lightUnit) const
void NzLight::Enable(const NzShader* shader, unsigned int lightUnit) const
{
/*
struct Light
@@ -66,6 +66,7 @@ void NzLight::Apply(const NzShader* shader, unsigned int lightUnit) const
-P3: float cosInnerAngle + float cosOuterAngle
*/
///TODO: Optimiser
int typeLocation = shader->GetUniformLocation("Lights[0].type");
int ambientLocation = shader->GetUniformLocation("Lights[0].ambient");
int diffuseLocation = shader->GetUniformLocation("Lights[0].diffuse");
@@ -217,6 +218,12 @@ NzLight& NzLight::operator=(const NzLight& light)
return *this;
}
void NzLight::Disable(const NzShader* shader, unsigned int lightUnit)
{
///TODO: Optimiser
shader->SendInteger(shader->GetUniformLocation("Lights[" + NzString::Number(lightUnit) + "].type"), -1);
}
void NzLight::Invalidate()
{
NzSceneNode::Invalidate();

View File

@@ -2,99 +2,22 @@
// 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 <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Graphics/TextureBackground.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
#include <Nazara/Renderer/ShaderManager.hpp>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
{
static NzShader* s_shader = nullptr;
static int s_textureLocation;
NzShader* BuildShader()
{
const char* fragmentSource110 =
"#version 110\n"
"varying vec2 vTexCoord;\n"
"uniform sampler2D Texture;\n"
"void main()\n"
"{\n"
" gl_FragColor = texture(Texture, vTexCoord);\n"
"}\n";
const char* fragmentSource140 =
"#version 140\n"
"in vec2 vTexCoord;\n"
"out vec4 RenderTarget0;\n"
"uniform sampler2D Texture;\n"
"void main()\n"
"{\n"
" RenderTarget0 = texture(Texture, vTexCoord);\n"
"}\n";
const char* vertexSource110 =
"#version 110\n"
"attribute vec2 VertexPosition;\n"
"varying vec2 vTexCoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(VertexPosition, 0.0, 1.0);\n"
" vTexCoord = vec2((VertexPosition.x + 1.0)*0.5, (VertexPosition.y + 1.0)*0.5);\n"
"}\n";
const char* vertexSource140 =
"#version 140\n"
"in vec2 VertexPosition;\n"
"out vec2 vTexCoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(VertexPosition, 0.0, 1.0);\n"
" vTexCoord = vec2((VertexPosition.x + 1.0)*0.5, (VertexPosition.y + 1.0)*0.5);\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
std::unique_ptr<NzShader> shader(new NzShader(nzShaderLanguage_GLSL));
shader->SetPersistent(false);
bool useGLSL140 = (NzOpenGL::GetVersion() >= 310);
if (!shader->Load(nzShaderType_Fragment, (useGLSL140) ? fragmentSource140 : fragmentSource110))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->Load(nzShaderType_Vertex, (useGLSL140) ? vertexSource140 : vertexSource110))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Compile())
{
NazaraError("Failed to compile shader");
return nullptr;
}
s_textureLocation = shader->GetUniformLocation("Texture");
return shader.release();
}
}
NzTextureBackground::NzTextureBackground()
{
if (!s_shader)
s_shader = BuildShader();
NzShaderManagerParams params;
params.target = nzShaderTarget_FullscreenQuad;
params.flags = 0;
params.fullscreenQuad.alphaMapping = false;
params.fullscreenQuad.alphaTest = false;
params.fullscreenQuad.diffuseMapping = true;
m_shader = s_shader;
m_shader = NzShaderManager::Get(params);
}
NzTextureBackground::NzTextureBackground(NzTexture* texture) :
@@ -103,19 +26,14 @@ NzTextureBackground()
m_texture = texture;
}
NzTextureBackground::~NzTextureBackground()
{
if (m_shader.Reset())
s_shader = nullptr;
}
void NzTextureBackground::Draw(const NzScene* scene) const
{
NazaraUnused(scene);
static NzRenderStates states;
m_shader->SendInteger(s_textureLocation, 0);
m_shader->SendColor(m_shader->GetUniformLocation(nzShaderUniform_MaterialDiffuse), NzColor::White);
m_shader->SendInteger(m_shader->GetUniformLocation(nzShaderUniform_MaterialDiffuseMap), 0);
NzRenderer::SetRenderStates(states);
NzRenderer::SetShader(m_shader);