Graphics/ForwardShading: Remake light selection
Former-commit-id: f7e761411e88513d1772b55f09ac4bd3a1d065a7
This commit is contained in:
parent
eba7cfc538
commit
4a87481311
|
|
@ -11,7 +11,6 @@
|
|||
#include <Nazara/Graphics/AbstractRenderTechnique.hpp>
|
||||
#include <Nazara/Graphics/ForwardRenderQueue.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/LightManager.hpp>
|
||||
#include <Nazara/Utility/IndexBuffer.hpp>
|
||||
#include <Nazara/Utility/VertexBuffer.hpp>
|
||||
|
||||
|
|
@ -21,8 +20,8 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
|
|||
NzForwardRenderTechnique();
|
||||
~NzForwardRenderTechnique() = default;
|
||||
|
||||
void Clear(const NzScene* scene) const;
|
||||
bool Draw(const NzScene* scene) const;
|
||||
void Clear(const NzScene* scene) const override;
|
||||
bool Draw(const NzScene* scene) const override;
|
||||
|
||||
unsigned int GetMaxLightPassPerObject() const;
|
||||
NzAbstractRenderQueue* GetRenderQueue() override;
|
||||
|
|
@ -36,11 +35,27 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
|
|||
private:
|
||||
struct ShaderUniforms;
|
||||
|
||||
bool ChooseLights(const NzSpheref& object, bool includeDirectionalLights = true) const;
|
||||
void DrawBasicSprites(const NzScene* scene) const;
|
||||
void DrawBillboards(const NzScene* scene) const;
|
||||
void DrawOpaqueModels(const NzScene* scene) const;
|
||||
void DrawTransparentModels(const NzScene* scene) 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
|
||||
{
|
||||
|
|
@ -58,10 +73,9 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
|
|||
};
|
||||
|
||||
mutable std::unordered_map<const NzShader*, ShaderUniforms> m_shaderUniforms;
|
||||
mutable std::vector<LightIndex> m_lights;
|
||||
NzBuffer m_vertexBuffer;
|
||||
mutable NzForwardRenderQueue m_renderQueue;
|
||||
mutable NzLightManager m_directionalLights;
|
||||
mutable NzLightManager m_lights;
|
||||
NzVertexBuffer m_billboardPointBuffer;
|
||||
NzVertexBuffer m_spriteBuffer;
|
||||
unsigned int m_maxLightPassPerObject;
|
||||
|
|
@ -72,4 +86,6 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
|
|||
static NzVertexDeclaration s_billboardVertexDeclaration;
|
||||
};
|
||||
|
||||
#include <Nazara/Graphics/ForwardRenderTechnique.inl>
|
||||
|
||||
#endif // NAZARA_FORWARDRENDERTECHNIQUE_HPP
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -63,8 +63,6 @@ void NzForwardRenderTechnique::Clear(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());
|
||||
|
||||
if (!m_renderQueue.opaqueModels.empty())
|
||||
|
|
@ -106,6 +104,108 @@ void NzForwardRenderTechnique::SetMaxLightPassPerObject(unsigned int 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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
// 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 && (!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é)
|
||||
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
|
||||
// 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;
|
||||
nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc();
|
||||
|
||||
|
|
@ -522,11 +559,9 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
NzRenderer::SetDepthFunc(nzRendererComparison_Equal);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < renderedLightCount; ++i)
|
||||
m_directionalLights.GetLight(lightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
|
||||
for (unsigned int i = renderedLightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
|
||||
NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
// Sends the uniforms
|
||||
for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
|
||||
SendLightUniforms(shader, shaderUniforms->lightUniforms, i*shaderUniforms->lightOffset, lightIndex++);
|
||||
}
|
||||
|
||||
const NzMatrix4f* instanceMatrices = &instances[0];
|
||||
|
|
@ -558,20 +593,19 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
{
|
||||
for (const NzMatrix4f& matrix : instances)
|
||||
{
|
||||
unsigned int directionalLightCount = m_directionalLights.GetLightCount();
|
||||
unsigned int otherLightCount = m_lights.ComputeClosestLights(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius, m_maxLightPassPerObject*NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - directionalLightCount);
|
||||
unsigned int lightCount = directionalLightCount + otherLightCount;
|
||||
// Choose the lights depending on an object position and apparent radius
|
||||
ChooseLights(NzSpheref(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius));
|
||||
|
||||
unsigned int lightCount = m_lights.size();
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
|
||||
unsigned int directionalLightIndex = 0;
|
||||
unsigned int otherLightIndex = 0;
|
||||
unsigned int lightIndex = 0;
|
||||
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;
|
||||
for (unsigned int pass = 0; pass < passCount; ++pass)
|
||||
{
|
||||
unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
|
||||
lightCount -= renderedLightCount;
|
||||
lightCount -= std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
|
||||
|
||||
if (pass == 1)
|
||||
{
|
||||
|
|
@ -584,18 +618,9 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
NzRenderer::SetDepthFunc(nzRendererComparison_Equal);
|
||||
}
|
||||
|
||||
// On active les lumières de cette passe-ci
|
||||
for (unsigned int i = 0; i < renderedLightCount; ++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);
|
||||
// Sends the light uniforms to the shader
|
||||
for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
|
||||
SendLightUniforms(shader, shaderUniforms->lightUniforms, lightIndex++, shaderUniforms->lightOffset*i);
|
||||
|
||||
// Et on passe à l'affichage
|
||||
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)
|
||||
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)
|
||||
m_directionalLights.GetLight(i)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
SendLightUniforms(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset * i, i);
|
||||
}
|
||||
|
||||
lastShader = shader;
|
||||
|
|
@ -693,22 +719,15 @@ void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const
|
|||
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||
NzRenderer::SetVertexBuffer(vertexBuffer);
|
||||
|
||||
if (shaderUniforms->hasLightUniforms)
|
||||
if (shaderUniforms->hasLightUniforms && lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS)
|
||||
{
|
||||
// Calcul des lumières les plus proches
|
||||
if (lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS && !m_lights.IsEmpty())
|
||||
{
|
||||
NzVector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition();
|
||||
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++));
|
||||
}
|
||||
// Compute the closest lights
|
||||
NzVector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition();
|
||||
float radius = modelData.squaredBoundingSphere.radius;
|
||||
ChooseLights(NzSpheref(position, radius), false);
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue