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/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
|
||||||
|
|
|
||||||
|
|
@ -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
|
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);
|
||||||
|
|
|
||||||
|
|
@ -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