Graphics/ForwardShading: Remake light selection

Former-commit-id: f7e761411e88513d1772b55f09ac4bd3a1d065a7
This commit is contained in:
Lynix 2015-05-31 22:13:49 +02:00
parent eba7cfc538
commit 4a87481311
5 changed files with 234 additions and 320 deletions

View File

@ -11,7 +11,6 @@
#include <Nazara/Graphics/AbstractRenderTechnique.hpp> #include <Nazara/Graphics/AbstractRenderTechnique.hpp>
#include <Nazara/Graphics/ForwardRenderQueue.hpp> #include <Nazara/Graphics/ForwardRenderQueue.hpp>
#include <Nazara/Graphics/Light.hpp> #include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/LightManager.hpp>
#include <Nazara/Utility/IndexBuffer.hpp> #include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/VertexBuffer.hpp> #include <Nazara/Utility/VertexBuffer.hpp>
@ -21,8 +20,8 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
NzForwardRenderTechnique(); NzForwardRenderTechnique();
~NzForwardRenderTechnique() = default; ~NzForwardRenderTechnique() = default;
void Clear(const NzScene* scene) const; void Clear(const NzScene* scene) const override;
bool Draw(const NzScene* scene) const; bool Draw(const NzScene* scene) const override;
unsigned int GetMaxLightPassPerObject() const; unsigned int GetMaxLightPassPerObject() const;
NzAbstractRenderQueue* GetRenderQueue() override; NzAbstractRenderQueue* GetRenderQueue() override;
@ -36,11 +35,27 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
private: private:
struct ShaderUniforms; struct ShaderUniforms;
bool ChooseLights(const NzSpheref& object, bool includeDirectionalLights = true) const;
void DrawBasicSprites(const NzScene* scene) const; void DrawBasicSprites(const NzScene* scene) const;
void DrawBillboards(const NzScene* scene) const; void DrawBillboards(const NzScene* scene) const;
void DrawOpaqueModels(const NzScene* scene) const; void DrawOpaqueModels(const NzScene* scene) const;
void DrawTransparentModels(const NzScene* scene) const; void DrawTransparentModels(const NzScene* scene) const;
const ShaderUniforms* GetShaderUniforms(const NzShader* shader) const; const ShaderUniforms* GetShaderUniforms(const NzShader* shader) const;
void SendLightUniforms(const NzShader* shader, const NzLightUniforms& uniforms, unsigned int uniformOffset, unsigned int index) const;
static float ComputeDirectionalLightScore(const NzSpheref& object, const NzAbstractRenderQueue::DirectionalLight& light);
static float ComputePointLightScore(const NzSpheref& object, const NzAbstractRenderQueue::PointLight& light);
static float ComputeSpotLightScore(const NzSpheref& object, const NzAbstractRenderQueue::SpotLight& light);
static bool IsDirectionalLightSuitable(const NzSpheref& object, const NzAbstractRenderQueue::DirectionalLight& light);
static bool IsPointLightSuitable(const NzSpheref& object, const NzAbstractRenderQueue::PointLight& light);
static bool IsSpotLightSuitable(const NzSpheref& object, const NzAbstractRenderQueue::SpotLight& light);
struct LightIndex
{
nzLightType type;
float score;
unsigned int index;
};
struct ShaderUniforms struct ShaderUniforms
{ {
@ -58,10 +73,9 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
}; };
mutable std::unordered_map<const NzShader*, ShaderUniforms> m_shaderUniforms; mutable std::unordered_map<const NzShader*, ShaderUniforms> m_shaderUniforms;
mutable std::vector<LightIndex> m_lights;
NzBuffer m_vertexBuffer; NzBuffer m_vertexBuffer;
mutable NzForwardRenderQueue m_renderQueue; mutable NzForwardRenderQueue m_renderQueue;
mutable NzLightManager m_directionalLights;
mutable NzLightManager m_lights;
NzVertexBuffer m_billboardPointBuffer; NzVertexBuffer m_billboardPointBuffer;
NzVertexBuffer m_spriteBuffer; NzVertexBuffer m_spriteBuffer;
unsigned int m_maxLightPassPerObject; unsigned int m_maxLightPassPerObject;
@ -72,4 +86,6 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
static NzVertexDeclaration s_billboardVertexDeclaration; static NzVertexDeclaration s_billboardVertexDeclaration;
}; };
#include <Nazara/Graphics/ForwardRenderTechnique.inl>
#endif // NAZARA_FORWARDRENDERTECHNIQUE_HPP #endif // NAZARA_FORWARDRENDERTECHNIQUE_HPP

View File

@ -0,0 +1,87 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
inline void NzForwardRenderTechnique::SendLightUniforms(const NzShader* shader, const NzLightUniforms& uniforms, unsigned int uniformOffset, unsigned int index) const
{
if (index < m_lights.size())
{
const LightIndex& lightIndex = m_lights[index];
shader->SendInteger(uniforms.locations.type + uniformOffset, lightIndex.type); //< Sends the light type
switch (lightIndex.type)
{
case nzLightType_Directional:
{
const auto& light = m_renderQueue.directionalLights[lightIndex.index];
shader->SendColor(uniforms.locations.color + uniformOffset, light.color);
shader->SendVector(uniforms.locations.factors + uniformOffset, NzVector2f(light.ambientFactor, light.diffuseFactor));
shader->SendVector(uniforms.locations.parameters1 + uniformOffset, NzVector4f(light.direction));
break;
}
case nzLightType_Point:
{
const auto& light = m_renderQueue.pointLights[lightIndex.index];
shader->SendColor(uniforms.locations.color + uniformOffset, light.color);
shader->SendVector(uniforms.locations.factors + uniformOffset, NzVector2f(light.ambientFactor, light.diffuseFactor));
shader->SendVector(uniforms.locations.parameters1 + uniformOffset, NzVector4f(light.position, light.attenuation));
shader->SendVector(uniforms.locations.parameters2 + uniformOffset, NzVector4f(0.f, 0.f, 0.f, light.invRadius));
break;
}
case nzLightType_Spot:
{
const auto& light = m_renderQueue.spotLights[lightIndex.index];
shader->SendColor(uniforms.locations.color + uniformOffset, light.color);
shader->SendVector(uniforms.locations.factors + uniformOffset, NzVector2f(light.ambientFactor, light.diffuseFactor));
shader->SendVector(uniforms.locations.parameters1 + uniformOffset, NzVector4f(light.position, light.attenuation));
shader->SendVector(uniforms.locations.parameters2 + uniformOffset, NzVector4f(light.direction, light.invRadius));
shader->SendVector(uniforms.locations.parameters3 + uniformOffset, NzVector2f(light.innerAngleCosine, light.outerAngleCosine));
break;
}
}
}
else
shader->SendInteger(uniforms.locations.type + uniformOffset, -1); //< Disable the light in the shader
}
inline float NzForwardRenderTechnique::ComputeDirectionalLightScore(const NzSpheref& object, const NzAbstractRenderQueue::DirectionalLight& light)
{
///TODO: Compute a score depending on the light luminosity
return 0.f;
}
inline float NzForwardRenderTechnique::ComputePointLightScore(const NzSpheref& object, const NzAbstractRenderQueue::PointLight& light)
{
///TODO: Compute a score depending on the light luminosity
return object.SquaredDistance(light.position);
}
inline float NzForwardRenderTechnique::ComputeSpotLightScore(const NzSpheref& object, const NzAbstractRenderQueue::SpotLight& light)
{
///TODO: Compute a score depending on the light luminosity and spot direction
return object.SquaredDistance(light.position);
}
inline bool NzForwardRenderTechnique::IsDirectionalLightSuitable(const NzSpheref& object, const NzAbstractRenderQueue::DirectionalLight& light)
{
// Directional light are always suitables
return true;
}
inline bool NzForwardRenderTechnique::IsPointLightSuitable(const NzSpheref& object, const NzAbstractRenderQueue::PointLight& light)
{
// If the object is too far away from this point light, there is not way it could light it
return object.SquaredDistance(light.position) <= light.radius * light.radius;
}
inline bool NzForwardRenderTechnique::IsSpotLightSuitable(const NzSpheref& object, const NzAbstractRenderQueue::SpotLight& light)
{
///TODO: Exclude spot lights based on their direction and outer angle?
return object.SquaredDistance(light.position) <= light.radius * light.radius;
}

View File

@ -1,48 +0,0 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_LIGHTMANAGER_HPP
#define NAZARA_LIGHTMANAGER_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Math/Vector3.hpp>
class NzLight;
class NAZARA_API NzLightManager
{
public:
NzLightManager();
NzLightManager(const NzLight** lights, unsigned int lightCount);
~NzLightManager() = default;
void AddLights(const NzLight** lights, unsigned int lightCount);
void Clear();
unsigned int ComputeClosestLights(const NzVector3f& position, float squaredRadius, unsigned int maxResults);
const NzLight* GetLight(unsigned int index) const;
unsigned int GetLightCount() const;
const NzLight* GetResult(unsigned int i) const;
bool IsEmpty() const;
void SetLights(const NzLight** lights, unsigned int lightCount);
private:
struct Light
{
const NzLight* light;
unsigned int score;
};
std::vector<std::pair<const NzLight**, unsigned int>> m_lights;
std::vector<Light> m_results;
unsigned int m_lightCount;
};
#endif // NAZARA_LIGHTMANAGER_HPP

View File

@ -63,8 +63,6 @@ void NzForwardRenderTechnique::Clear(const NzScene* scene) const
bool NzForwardRenderTechnique::Draw(const NzScene* scene) const bool NzForwardRenderTechnique::Draw(const NzScene* scene) const
{ {
m_directionalLights.SetLights(&m_renderQueue.directionalLights[0], m_renderQueue.directionalLights.size());
m_lights.SetLights(&m_renderQueue.lights[0], m_renderQueue.lights.size());
m_renderQueue.Sort(scene->GetViewer()); m_renderQueue.Sort(scene->GetViewer());
if (!m_renderQueue.opaqueModels.empty()) if (!m_renderQueue.opaqueModels.empty())
@ -106,6 +104,108 @@ void NzForwardRenderTechnique::SetMaxLightPassPerObject(unsigned int passCount)
m_maxLightPassPerObject = passCount; m_maxLightPassPerObject = passCount;
} }
bool NzForwardRenderTechnique::Initialize()
{
try
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
s_quadIndexBuffer.Reset(false, s_maxQuads*6, nzDataStorage_Hardware, nzBufferUsage_Static);
NzBufferMapper<NzIndexBuffer> mapper(s_quadIndexBuffer, nzBufferAccess_WriteOnly);
nzUInt16* indices = static_cast<nzUInt16*>(mapper.GetPointer());
for (unsigned int i = 0; i < s_maxQuads; ++i)
{
*indices++ = i*4 + 0;
*indices++ = i*4 + 2;
*indices++ = i*4 + 1;
*indices++ = i*4 + 2;
*indices++ = i*4 + 3;
*indices++ = i*4 + 1;
}
mapper.Unmap(); // Inutile de garder le buffer ouvert plus longtemps
// Quad buffer (utilisé pour l'instancing de billboard et de sprites)
//Note: Les UV sont calculés dans le shader
s_quadVertexBuffer.Reset(NzVertexDeclaration::Get(nzVertexLayout_XY), 4, nzDataStorage_Hardware, nzBufferUsage_Static);
float vertices[2*4] = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
};
s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices));
// Déclaration lors du rendu des billboards par sommet
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Color, nzComponentType_Color, NzOffsetOf(BillboardPoint, color));
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(BillboardPoint, position));
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(BillboardPoint, uv));
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Userdata0, nzComponentType_Float4, NzOffsetOf(BillboardPoint, size)); // Englobe sincos
// Declaration utilisée lors du rendu des billboards par instancing
// L'avantage ici est la copie directe (std::memcpy) des données de la RenderQueue vers le buffer GPU
s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData0, nzComponentType_Float3, NzOffsetOf(NzForwardRenderQueue::BillboardData, center));
s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData1, nzComponentType_Float4, NzOffsetOf(NzForwardRenderQueue::BillboardData, size)); // Englobe sincos
s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData2, nzComponentType_Color, NzOffsetOf(NzForwardRenderQueue::BillboardData, color));
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + NzString(e.what()));
return false;
}
return true;
}
void NzForwardRenderTechnique::Uninitialize()
{
s_quadIndexBuffer.Reset();
s_quadVertexBuffer.Reset();
}
bool NzForwardRenderTechnique::ChooseLights(const NzSpheref& object, bool includeDirectionalLights) const
{
m_lights.clear();
// First step: add all the lights into a common list and compute their score, exlucing those who have no chance of lighting the object
// (Those who are too far away).
if (includeDirectionalLights)
{
for (unsigned int i = 0; i < m_renderQueue.directionalLights.size(); ++i)
{
const auto& light = m_renderQueue.directionalLights[i];
if (IsDirectionalLightSuitable(object, light))
m_lights.push_back({nzLightType_Directional, ComputeDirectionalLightScore(object, light), i});
}
}
for (unsigned int i = 0; i < m_renderQueue.pointLights.size(); ++i)
{
const auto& light = m_renderQueue.pointLights[i];
if (IsPointLightSuitable(object, light))
m_lights.push_back({nzLightType_Point, ComputePointLightScore(object, light), i});
}
for (unsigned int i = 0; i < m_renderQueue.spotLights.size(); ++i)
{
const auto& light = m_renderQueue.spotLights[i];
if (IsSpotLightSuitable(object, light))
m_lights.push_back({nzLightType_Spot, ComputeSpotLightScore(object, light), i});
}
// Then, sort the lights according to their score
std::sort(m_lights.begin(), m_lights.end(), [](const LightIndex& light1, const LightIndex& light2)
{
return light1.score < light2.score;
});
}
void NzForwardRenderTechnique::DrawBasicSprites(const NzScene* scene) const void NzForwardRenderTechnique::DrawBasicSprites(const NzScene* scene) const
{ {
NzAbstractViewer* viewer = scene->GetViewer(); NzAbstractViewer* viewer = scene->GetViewer();
@ -211,70 +311,6 @@ void NzForwardRenderTechnique::DrawBasicSprites(const NzScene* scene) const
} }
} }
bool NzForwardRenderTechnique::Initialize()
{
try
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
s_quadIndexBuffer.Reset(false, s_maxQuads*6, nzDataStorage_Hardware, nzBufferUsage_Static);
NzBufferMapper<NzIndexBuffer> mapper(s_quadIndexBuffer, nzBufferAccess_WriteOnly);
nzUInt16* indices = static_cast<nzUInt16*>(mapper.GetPointer());
for (unsigned int i = 0; i < s_maxQuads; ++i)
{
*indices++ = i*4 + 0;
*indices++ = i*4 + 2;
*indices++ = i*4 + 1;
*indices++ = i*4 + 2;
*indices++ = i*4 + 3;
*indices++ = i*4 + 1;
}
mapper.Unmap(); // Inutile de garder le buffer ouvert plus longtemps
// Quad buffer (utilisé pour l'instancing de billboard et de sprites)
//Note: Les UV sont calculés dans le shader
s_quadVertexBuffer.Reset(NzVertexDeclaration::Get(nzVertexLayout_XY), 4, nzDataStorage_Hardware, nzBufferUsage_Static);
float vertices[2*4] = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
};
s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices));
// Déclaration lors du rendu des billboards par sommet
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Color, nzComponentType_Color, NzOffsetOf(BillboardPoint, color));
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(BillboardPoint, position));
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(BillboardPoint, uv));
s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Userdata0, nzComponentType_Float4, NzOffsetOf(BillboardPoint, size)); // Englobe sincos
// Declaration utilisée lors du rendu des billboards par instancing
// L'avantage ici est la copie directe (std::memcpy) des données de la RenderQueue vers le buffer GPU
s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData0, nzComponentType_Float3, NzOffsetOf(NzForwardRenderQueue::BillboardData, center));
s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData1, nzComponentType_Float4, NzOffsetOf(NzForwardRenderQueue::BillboardData, size)); // Englobe sincos
s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData2, nzComponentType_Color, NzOffsetOf(NzForwardRenderQueue::BillboardData, color));
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + NzString(e.what()));
return false;
}
return true;
}
void NzForwardRenderTechnique::Uninitialize()
{
s_quadIndexBuffer.Reset();
s_quadVertexBuffer.Reset();
}
void NzForwardRenderTechnique::DrawBillboards(const NzScene* scene) const void NzForwardRenderTechnique::DrawBillboards(const NzScene* scene) const
{ {
NzAbstractViewer* viewer = scene->GetViewer(); NzAbstractViewer* viewer = scene->GetViewer();
@ -437,7 +473,8 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
// Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active // 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 // 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) // (Le deferred shading n'a pas ce problème)
bool instancing = m_instancingEnabled && (!material->IsLightingEnabled() || m_lights.IsEmpty()) && matEntry.instancingEnabled; bool noPointSpotLight = m_renderQueue.pointLights.empty() && m_renderQueue.spotLights.empty();
bool instancing = m_instancingEnabled && (!material->IsLightingEnabled() || noPointSpotLight) && matEntry.instancingEnabled;
// On commence par appliquer du matériau (et récupérer le shader ainsi activé) // On commence par appliquer du matériau (et récupérer le shader ainsi activé)
const NzShader* shader = material->Apply((instancing) ? nzShaderFlags_Instancing : 0); const NzShader* shader = material->Apply((instancing) ? nzShaderFlags_Instancing : 0);
@ -499,7 +536,7 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
// Avec l'instancing, impossible de sélectionner les lumières pour chaque objet // Avec l'instancing, impossible de sélectionner les lumières pour chaque objet
// Du coup, il n'est activé que pour les lumières directionnelles // Du coup, il n'est activé que pour les lumières directionnelles
unsigned int lightCount = m_directionalLights.GetLightCount(); unsigned int lightCount = m_renderQueue.directionalLights.size();
unsigned int lightIndex = 0; unsigned int lightIndex = 0;
nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc(); nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc();
@ -522,11 +559,9 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
NzRenderer::SetDepthFunc(nzRendererComparison_Equal); NzRenderer::SetDepthFunc(nzRendererComparison_Equal);
} }
for (unsigned int i = 0; i < renderedLightCount; ++i) // Sends the uniforms
m_directionalLights.GetLight(lightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
SendLightUniforms(shader, shaderUniforms->lightUniforms, i*shaderUniforms->lightOffset, lightIndex++);
for (unsigned int i = renderedLightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
} }
const NzMatrix4f* instanceMatrices = &instances[0]; const NzMatrix4f* instanceMatrices = &instances[0];
@ -558,20 +593,19 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
{ {
for (const NzMatrix4f& matrix : instances) for (const NzMatrix4f& matrix : instances)
{ {
unsigned int directionalLightCount = m_directionalLights.GetLightCount(); // Choose the lights depending on an object position and apparent radius
unsigned int otherLightCount = m_lights.ComputeClosestLights(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius, m_maxLightPassPerObject*NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - directionalLightCount); ChooseLights(NzSpheref(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius));
unsigned int lightCount = directionalLightCount + otherLightCount;
unsigned int lightCount = m_lights.size();
NzRenderer::SetMatrix(nzMatrixType_World, matrix); NzRenderer::SetMatrix(nzMatrixType_World, matrix);
unsigned int directionalLightIndex = 0; unsigned int lightIndex = 0;
unsigned int otherLightIndex = 0;
nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc(); // Dans le cas où nous aurions à le changer nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc(); // Dans le cas où nous aurions à le changer
unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1;
for (unsigned int pass = 0; pass < passCount; ++pass) for (unsigned int pass = 0; pass < passCount; ++pass)
{ {
unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); lightCount -= std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
lightCount -= renderedLightCount;
if (pass == 1) if (pass == 1)
{ {
@ -584,18 +618,9 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
NzRenderer::SetDepthFunc(nzRendererComparison_Equal); NzRenderer::SetDepthFunc(nzRendererComparison_Equal);
} }
// On active les lumières de cette passe-ci // Sends the light uniforms to the shader
for (unsigned int i = 0; i < renderedLightCount; ++i) for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
{ SendLightUniforms(shader, shaderUniforms->lightUniforms, lightIndex++, shaderUniforms->lightOffset*i);
if (directionalLightIndex >= directionalLightCount)
m_lights.GetResult(otherLightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
else
m_directionalLights.GetLight(directionalLightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
}
// On désactive l'éventuel surplus
for (unsigned int i = renderedLightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
// Et on passe à l'affichage // Et on passe à l'affichage
drawFunc(meshData.primitiveMode, 0, indexCount); drawFunc(meshData.primitiveMode, 0, indexCount);
@ -660,9 +685,10 @@ void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
if (shaderUniforms->hasLightUniforms) if (shaderUniforms->hasLightUniforms)
{ {
lightCount = std::min(m_directionalLights.GetLightCount(), NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); lightCount = std::min(m_renderQueue.directionalLights.size(), NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
for (unsigned int i = 0; i < lightCount; ++i) for (unsigned int i = 0; i < lightCount; ++i)
m_directionalLights.GetLight(i)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); SendLightUniforms(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset * i, i);
} }
lastShader = shader; lastShader = shader;
@ -693,22 +719,15 @@ void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const
NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetIndexBuffer(indexBuffer);
NzRenderer::SetVertexBuffer(vertexBuffer); NzRenderer::SetVertexBuffer(vertexBuffer);
if (shaderUniforms->hasLightUniforms) if (shaderUniforms->hasLightUniforms && lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS)
{ {
// Calcul des lumières les plus proches // Compute the closest lights
if (lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS && !m_lights.IsEmpty()) NzVector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition();
{ float radius = modelData.squaredBoundingSphere.radius;
NzVector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition(); ChooseLights(NzSpheref(position, radius), false);
float radius = modelData.squaredBoundingSphere.radius;
unsigned int closestLightCount = m_lights.ComputeClosestLights(position, radius, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS);
unsigned int count = std::min(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - lightCount, closestLightCount);
for (unsigned int i = 0; i < count; ++i)
m_lights.GetResult(i)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*(lightCount++));
}
for (unsigned int i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) for (unsigned int i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); SendLightUniforms(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i, i);
} }
NzRenderer::SetMatrix(nzMatrixType_World, matrix); NzRenderer::SetMatrix(nzMatrixType_World, matrix);

View File

@ -1,160 +0,0 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/LightManager.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzLightManager::NzLightManager() :
m_lightCount(0)
{
}
NzLightManager::NzLightManager(const NzLight** lights, unsigned int lightCount)
{
SetLights(lights, lightCount);
}
void NzLightManager::AddLights(const NzLight** lights, unsigned int lightCount)
{
m_lights.push_back(std::make_pair(lights, lightCount));
m_lightCount += lightCount;
}
void NzLightManager::Clear()
{
m_lights.clear();
m_lightCount = 0;
}
unsigned int NzLightManager::ComputeClosestLights(const NzVector3f& position, float squaredRadius, unsigned int maxResults)
{
m_results.resize(maxResults);
for (Light& light : m_results)
{
light.light = nullptr;
light.score = std::numeric_limits<unsigned int>::max(); // Nous jouons au Golf
}
for (auto it = m_lights.begin(); it != m_lights.end(); ++it)
{
const NzLight** lights = it->first;
unsigned int lightCount = it->second;
for (unsigned int j = 0; j < lightCount; ++j)
{
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:
{
float lightRadius = light->GetRadius();
float squaredDistance = position.SquaredDistance(light->GetPosition());
if (squaredDistance - squaredRadius <= lightRadius*lightRadius)
score = static_cast<unsigned int>(squaredDistance*1000.f);
break;
}
case nzLightType_Spot:
{
float lightRadius = light->GetRadius();
///TODO: Attribuer bonus/malus selon l'angle du spot ?
float squaredDistance = position.SquaredDistance(light->GetPosition());
if (squaredDistance - squaredRadius <= lightRadius*lightRadius)
score = static_cast<unsigned int>(squaredDistance*1000.f);
break;
}
}
if (score < m_results[0].score)
{
unsigned int k;
for (k = 1; k < maxResults; ++k)
{
if (score > m_results[k].score)
break;
}
k--; // Position de la nouvelle lumière
// Décalage
std::memcpy(&m_results[0], &m_results[1], k*sizeof(Light));
m_results[k].light = light;
m_results[k].score = score;
}
}
}
unsigned int i;
for (i = 0; i < maxResults; ++i)
{
if (m_results[i].light)
break;
}
return maxResults-i;
}
const NzLight* NzLightManager::GetLight(unsigned int index) const
{
#if NAZARA_GRAPHICS_SAFE
if (index >= m_lightCount)
{
NazaraError("Light index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_lightCount) + ')');
return nullptr;
}
#endif
for (unsigned int i = 0; i < m_lights.size(); ++i)
{
unsigned int lightCount = m_lights[i].second;
if (index > lightCount)
index -= lightCount;
else
{
const NzLight** lights = m_lights[i].first;
return lights[i];
}
}
#if NAZARA_GRAPHICS_SAFE
NazaraInternalError("Light not found");
#else
NazaraError("Light not found");
#endif
return nullptr;
}
unsigned int NzLightManager::GetLightCount() const
{
return m_lightCount;
}
const NzLight* NzLightManager::GetResult(unsigned int i) const
{
return m_results[m_results.size()-i-1].light;
}
bool NzLightManager::IsEmpty() const
{
return m_lightCount == 0;
}
void NzLightManager::SetLights(const NzLight** lights, unsigned int lightCount)
{
Clear();
AddLights(lights, lightCount);
}