feat: Implement dynamic shadows
This commit is contained in:
parent
4bb45a54c2
commit
47daa821a8
13 changed files with 260 additions and 32 deletions
|
@ -37,22 +37,33 @@ Framebuffer::~Framebuffer() {
|
|||
void Framebuffer::init() {
|
||||
if (_inited) return;
|
||||
|
||||
_colorBuffers.resize(_colorBufferCount);
|
||||
glGenTextures(_colorBufferCount, &_colorBuffers[0]);
|
||||
if (_colorBufferCount > 0) {
|
||||
_colorBuffers.resize(_colorBufferCount);
|
||||
glGenTextures(_colorBufferCount, &_colorBuffers[0]);
|
||||
|
||||
for (int i = 0; i < _colorBufferCount; ++i) {
|
||||
glBindTexture(GL_TEXTURE_2D, _colorBuffers[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
for (int i = 0; i < _colorBufferCount; ++i) {
|
||||
glBindTexture(GL_TEXTURE_2D, _colorBuffers[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
glGenRenderbuffers(1, &_depthBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
glGenTextures(1, &_depthBuffer);
|
||||
glBindTexture(GL_TEXTURE_2D, _depthBuffer);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _width, _height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
|
||||
float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glGenFramebuffers(1, &_framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
|
||||
|
@ -60,7 +71,7 @@ void Framebuffer::init() {
|
|||
for (int i = 0; i < _colorBufferCount; ++i) {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, _colorBuffers[i], 0);
|
||||
}
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthBuffer, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
throw runtime_error("Control: framebuffer is not complete");
|
||||
|
@ -74,8 +85,11 @@ void Framebuffer::deinit() {
|
|||
if (!_inited) return;
|
||||
|
||||
glDeleteFramebuffers(1, &_framebuffer);
|
||||
glDeleteTextures(_colorBufferCount, &_colorBuffers[0]);
|
||||
glDeleteRenderbuffers(1, &_depthBuffer);
|
||||
|
||||
if (_colorBufferCount > 0) {
|
||||
glDeleteTextures(_colorBufferCount, &_colorBuffers[0]);
|
||||
}
|
||||
glDeleteTextures(1, &_depthBuffer);
|
||||
|
||||
_inited = false;
|
||||
}
|
||||
|
@ -92,10 +106,18 @@ void Framebuffer::bindColorBuffer(int n) const {
|
|||
glBindTexture(GL_TEXTURE_2D, _colorBuffers[n]);
|
||||
}
|
||||
|
||||
void Framebuffer::bindDepthBuffer() const {
|
||||
glBindTexture(GL_TEXTURE_2D, _depthBuffer);
|
||||
}
|
||||
|
||||
void Framebuffer::unbindColorBuffer() const {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Framebuffer::unbindDepthBuffer() const {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
int Framebuffer::width() const {
|
||||
return _width;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,9 @@ public:
|
|||
void bind() const;
|
||||
void unbind() const;
|
||||
void bindColorBuffer(int n) const;
|
||||
void bindDepthBuffer() const;
|
||||
void unbindColorBuffer() const;
|
||||
void unbindDepthBuffer() const;
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
|
|
@ -31,21 +31,32 @@ namespace reone {
|
|||
|
||||
namespace render {
|
||||
|
||||
static const int kShadowResolution = 2048;
|
||||
|
||||
WorldRenderPipeline::WorldRenderPipeline(IRenderable *scene, const GraphicsOptions &opts) :
|
||||
_scene(scene),
|
||||
_opts(opts),
|
||||
_geometry(opts.width, opts.height, 2),
|
||||
_verticalBlur(opts.width, opts.height),
|
||||
_horizontalBlur(opts.width, opts.height) {
|
||||
|
||||
for (int i = 0; i < kMaxShadowLightCount; ++i) {
|
||||
_shadows.push_back(make_unique<Framebuffer>(kShadowResolution, kShadowResolution, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderPipeline::init() {
|
||||
_geometry.init();
|
||||
_verticalBlur.init();
|
||||
_horizontalBlur.init();
|
||||
|
||||
for (auto &shadows : _shadows) {
|
||||
shadows->init();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderPipeline::render() const {
|
||||
drawShadows();
|
||||
drawGeometry();
|
||||
|
||||
float w = static_cast<float>(_opts.width);
|
||||
|
@ -61,6 +72,39 @@ void WorldRenderPipeline::render() const {
|
|||
drawResult();
|
||||
}
|
||||
|
||||
void WorldRenderPipeline::drawShadows() const {
|
||||
const vector<ShadowLight> &lights = _scene->shadowLights();
|
||||
|
||||
int lightCount = static_cast<int>(lights.size());
|
||||
if (lightCount == 0) return;
|
||||
|
||||
int viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
glViewport(0, 0, kShadowResolution, kShadowResolution);
|
||||
|
||||
GlobalUniforms globals;
|
||||
|
||||
for (int i = 0; i < lightCount; ++i) {
|
||||
_shadows[i]->bind();
|
||||
|
||||
glDrawBuffer(GL_NONE);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
globals.cameraPosition = lights[i].position;
|
||||
globals.projection = lights[i].projection;
|
||||
globals.view = lights[i].view;
|
||||
|
||||
Shaders::instance().setGlobalUniforms(globals);
|
||||
|
||||
withDepthTest([this]() { _scene->renderNoGlobalUniforms(); });
|
||||
|
||||
_shadows[i]->unbind();
|
||||
}
|
||||
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
}
|
||||
|
||||
void WorldRenderPipeline::drawGeometry() const {
|
||||
_geometry.bind();
|
||||
|
||||
|
@ -68,6 +112,13 @@ void WorldRenderPipeline::drawGeometry() const {
|
|||
glDrawBuffers(2, buffers);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
int lightCount = static_cast<int>(_scene->shadowLights().size());
|
||||
|
||||
for (int i = 0; i < lightCount; ++i) {
|
||||
glActiveTexture(GL_TEXTURE0 + TextureUniforms::shadowmap0 + i);
|
||||
_shadows[i]->bindDepthBuffer();
|
||||
}
|
||||
withDepthTest([this]() { _scene->render(); });
|
||||
|
||||
_geometry.unbind();
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "../framebuffer.h"
|
||||
#include "../types.h"
|
||||
|
||||
|
@ -37,10 +40,12 @@ private:
|
|||
Framebuffer _geometry;
|
||||
Framebuffer _verticalBlur;
|
||||
Framebuffer _horizontalBlur;
|
||||
std::vector<std::unique_ptr<Framebuffer>> _shadows;
|
||||
|
||||
WorldRenderPipeline(const WorldRenderPipeline &) = delete;
|
||||
WorldRenderPipeline &operator=(const WorldRenderPipeline &) = delete;
|
||||
|
||||
void drawShadows() const;
|
||||
void drawGeometry() const;
|
||||
void applyHorizontalBlur() const;
|
||||
void applyVerticalBlur() const;
|
||||
|
|
|
@ -36,12 +36,14 @@ namespace render {
|
|||
static const int kFeaturesBindingPointIndex = 1;
|
||||
static const int kGeneralBindingPointIndex = 2;
|
||||
static const int kLightingBindingPointIndex = 3;
|
||||
static const int kSkeletalBindingPointIndex = 4;
|
||||
static const int kShadowsBindingPointIndex = 4;
|
||||
static const int kSkeletalBindingPointIndex = 5;
|
||||
|
||||
static const GLchar kCommonShaderHeader[] = R"END(
|
||||
#version 330
|
||||
|
||||
const int MAX_LIGHTS = 8;
|
||||
const int MAX_SHADOW_LIGHTS = 4;
|
||||
const int MAX_BONES = 128;
|
||||
|
||||
struct Light {
|
||||
|
@ -50,6 +52,12 @@ struct Light {
|
|||
float radius;
|
||||
};
|
||||
|
||||
struct ShadowLight {
|
||||
vec4 position;
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
};
|
||||
|
||||
layout(std140) uniform General {
|
||||
bool uLightmapEnabled;
|
||||
bool uEnvmapEnabled;
|
||||
|
@ -61,6 +69,7 @@ layout(std140) uniform General {
|
|||
bool uBlurEnabled;
|
||||
bool uBloomEnabled;
|
||||
bool uDiscardEnabled;
|
||||
bool uShadowsEnabled;
|
||||
|
||||
uniform mat4 uModel;
|
||||
uniform vec4 uColor;
|
||||
|
@ -77,6 +86,11 @@ layout(std140) uniform Lighting {
|
|||
Light uLights[MAX_LIGHTS];
|
||||
};
|
||||
|
||||
layout(std140) uniform Shadows {
|
||||
int uShadowLightCount;
|
||||
ShadowLight uShadowLights[MAX_SHADOW_LIGHTS];
|
||||
};
|
||||
|
||||
layout(std140) uniform Skeletal {
|
||||
uniform mat4 uAbsTransform;
|
||||
uniform mat4 uAbsTransformInv;
|
||||
|
@ -102,6 +116,7 @@ void main() {
|
|||
static const GLchar kModelVertexShader[] = R"END(
|
||||
uniform mat4 uProjection;
|
||||
uniform mat4 uView;
|
||||
uniform mat4 uLightSpaceMatrix;
|
||||
|
||||
layout(location = 0) in vec3 aPosition;
|
||||
layout(location = 1) in vec3 aNormal;
|
||||
|
@ -187,6 +202,7 @@ uniform sampler2D uLightmap;
|
|||
uniform sampler2D uBumpmap;
|
||||
uniform samplerCube uEnvmap;
|
||||
uniform samplerCube uBumpyShiny;
|
||||
uniform sampler2D uShadowmaps[MAX_SHADOW_LIGHTS];
|
||||
|
||||
uniform vec3 uCameraPosition;
|
||||
|
||||
|
@ -221,18 +237,41 @@ void applyLighting(vec3 normal, inout vec3 color) {
|
|||
vec3 lightDir = normalize(surfaceToLight);
|
||||
|
||||
vec3 surfaceToCamera = normalize(uCameraPosition - fragPosition);
|
||||
float diffuseCoeff = max(dot(normal, lightDir), 0.0);
|
||||
float specularCoeff = 0.0;
|
||||
float diffuse = max(dot(normal, lightDir), 0.0);
|
||||
float specular = 0.0;
|
||||
|
||||
if (diffuseCoeff > 0.0) {
|
||||
specularCoeff = 0.25 * pow(max(0.0, dot(surfaceToCamera, reflect(-lightDir, normal))), 32);
|
||||
if (diffuse > 0.0) {
|
||||
specular = 0.25 * pow(max(0.0, dot(surfaceToCamera, reflect(-lightDir, normal))), 32);
|
||||
}
|
||||
float distToLight = length(surfaceToLight);
|
||||
|
||||
float attenuation = clamp(1.0 - distToLight / uLights[i].radius, 0.0, 1.0);
|
||||
attenuation *= attenuation;
|
||||
|
||||
color += attenuation * (diffuseCoeff + specularCoeff) * uLights[i].color.rgb;
|
||||
color += attenuation * (diffuse + specular) * uLights[i].color.rgb;
|
||||
}
|
||||
}
|
||||
|
||||
void applyShadows(vec3 normal, inout vec3 color) {
|
||||
for (int i = 0; i < uShadowLightCount; ++i) {
|
||||
vec4 lightSpacePos = uShadowLights[i].projection * uShadowLights[i].view * vec4(fragPosition, 1.0);
|
||||
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.z > 1.0) continue;
|
||||
|
||||
float distToCenter = distance(vec2(0.5), projCoords.xy); // between 0.0 and 0.5
|
||||
float shadow = 0.5 * (1.0 - smoothstep(0.25, 0.5, distToCenter));
|
||||
float closestDepth = texture(uShadowmaps[i], projCoords.xy).r;
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
vec3 surfaceToLight = uShadowLights[i].position.xyz - fragPosition;
|
||||
vec3 lightDir = normalize(surfaceToLight);
|
||||
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
|
||||
|
||||
if (currentDepth - bias > closestDepth) {
|
||||
color *= 1.0 - shadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,6 +295,9 @@ void main() {
|
|||
} else {
|
||||
lightColor = vec3(1.0);
|
||||
}
|
||||
if (uShadowsEnabled) {
|
||||
applyShadows(normal, lightColor);
|
||||
}
|
||||
float finalAlpha = uAlpha;
|
||||
|
||||
if (!uEnvmapEnabled && !uBumpyShinyEnabled && !uBumpmapEnabled) {
|
||||
|
@ -336,6 +378,7 @@ void Shaders::initGL() {
|
|||
|
||||
glGenBuffers(1, &_generalUbo);
|
||||
glGenBuffers(1, &_lightingUbo);
|
||||
glGenBuffers(1, &_shadowsUbo);
|
||||
glGenBuffers(1, &_skeletalUbo);
|
||||
|
||||
for (auto &program : _programs) {
|
||||
|
@ -352,6 +395,10 @@ void Shaders::initGL() {
|
|||
if (lightingBlockIdx != GL_INVALID_INDEX) {
|
||||
glUniformBlockBinding(_activeOrdinal, lightingBlockIdx, kLightingBindingPointIndex);
|
||||
}
|
||||
uint32_t shadowsBlockIdx = glGetUniformBlockIndex(_activeOrdinal, "Shadows");
|
||||
if (shadowsBlockIdx != GL_INVALID_INDEX) {
|
||||
glUniformBlockBinding(_activeOrdinal, shadowsBlockIdx, kShadowsBindingPointIndex);
|
||||
}
|
||||
uint32_t skeletalBlockIdx = glGetUniformBlockIndex(_activeOrdinal, "Skeletal");
|
||||
if (skeletalBlockIdx != GL_INVALID_INDEX) {
|
||||
glUniformBlockBinding(_activeOrdinal, skeletalBlockIdx, kSkeletalBindingPointIndex);
|
||||
|
@ -363,6 +410,11 @@ void Shaders::initGL() {
|
|||
setUniform("uBumpmap", TextureUniforms::bumpmap);
|
||||
setUniform("uBloom", TextureUniforms::bloom);
|
||||
|
||||
for (int i = 0; i < kMaxShadowLightCount; ++i) {
|
||||
string name(str(boost::format("uShadowmaps[%d]") % i));
|
||||
setUniform(name, TextureUniforms::shadowmap0 + i);
|
||||
}
|
||||
|
||||
_activeOrdinal = 0;
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
@ -418,6 +470,10 @@ void Shaders::deinitGL() {
|
|||
glDeleteBuffers(1, &_skeletalUbo);
|
||||
_skeletalUbo = 0;
|
||||
}
|
||||
if (_shadowsUbo) {
|
||||
glDeleteBuffers(1, &_shadowsUbo);
|
||||
_shadowsUbo = 0;
|
||||
}
|
||||
if (_lightingUbo) {
|
||||
glDeleteBuffers(1, &_lightingUbo);
|
||||
_lightingUbo = 0;
|
||||
|
@ -550,6 +606,9 @@ void Shaders::setGlobalUniforms(const GlobalUniforms &globals) {
|
|||
setUniform("uProjection", globals.projection);
|
||||
setUniform("uView", globals.view);
|
||||
setUniform("uCameraPosition", globals.cameraPosition);
|
||||
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, kShadowsBindingPointIndex, _shadowsUbo);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(ShadowsUniforms), &globals.shadows, GL_STATIC_DRAW);
|
||||
}
|
||||
glUseProgram(ordinal);
|
||||
_activeOrdinal = ordinal;
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include "glm/glm.hpp"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace render {
|
||||
|
@ -49,12 +51,20 @@ struct TextureUniforms {
|
|||
static constexpr int bumpyShiny { 3 };
|
||||
static constexpr int bumpmap { 4 };
|
||||
static constexpr int bloom { 5 };
|
||||
static constexpr int shadowmap0 { 6 };
|
||||
};
|
||||
|
||||
struct ShadowsUniforms {
|
||||
int shadowLightCount { 0 };
|
||||
char padding[12];
|
||||
ShadowLight shadowLights[kMaxShadowLightCount];
|
||||
};
|
||||
|
||||
struct GlobalUniforms {
|
||||
glm::mat4 projection { 1.0f };
|
||||
glm::mat4 view { 1.0f };
|
||||
glm::vec3 cameraPosition { 0.0f };
|
||||
ShadowsUniforms shadows;
|
||||
};
|
||||
|
||||
struct GeneralUniforms {
|
||||
|
@ -68,7 +78,8 @@ struct GeneralUniforms {
|
|||
int blurEnabled { false };
|
||||
int bloomEnabled { false };
|
||||
int discardEnabled { false };
|
||||
char padding1[8];
|
||||
int shadowsEnabled { false };
|
||||
char padding1[4];
|
||||
|
||||
glm::mat4 model { 1.0f };
|
||||
glm::vec4 color { 1.0f };
|
||||
|
@ -142,6 +153,7 @@ private:
|
|||
|
||||
uint32_t _generalUbo { 0 };
|
||||
uint32_t _lightingUbo { 0 };
|
||||
uint32_t _shadowsUbo { 0 };
|
||||
uint32_t _skeletalUbo { 0 };
|
||||
|
||||
// END Uniform buffer objects
|
||||
|
|
|
@ -22,12 +22,16 @@
|
|||
|
||||
#include "SDL2/SDL_events.h"
|
||||
|
||||
#include "glm/mat4x4.hpp"
|
||||
#include "glm/vec3.hpp"
|
||||
#include "glm/vec4.hpp"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace render {
|
||||
|
||||
const int kMaxShadowLightCount = 4;
|
||||
|
||||
enum class TextureType {
|
||||
Diffuse,
|
||||
Lightmap,
|
||||
|
@ -59,14 +63,24 @@ struct TextureFeatures {
|
|||
std::vector<glm::vec3> lowerRightCoords;
|
||||
};
|
||||
|
||||
struct ShadowLight {
|
||||
glm::vec4 position { 0.0f };
|
||||
glm::mat4 view { 1.0f };
|
||||
glm::mat4 projection { 1.0f };
|
||||
};
|
||||
|
||||
class IEventHandler {
|
||||
public:
|
||||
virtual bool handle(const SDL_Event &event) = 0;
|
||||
};
|
||||
|
||||
// TODO: move render pipelines into libscene
|
||||
class IRenderable {
|
||||
public:
|
||||
virtual void render() const = 0;
|
||||
virtual void renderNoGlobalUniforms() const = 0;
|
||||
|
||||
virtual const std::vector<ShadowLight> &shadowLights() const = 0;
|
||||
};
|
||||
|
||||
} // namespace render
|
||||
|
|
|
@ -23,11 +23,12 @@ namespace reone {
|
|||
|
||||
namespace scene {
|
||||
|
||||
LightSceneNode::LightSceneNode(SceneGraph *sceneGraph, int priority, const glm::vec3 &color, float radius, bool shadow) :
|
||||
LightSceneNode::LightSceneNode(SceneGraph *sceneGraph, int priority, const glm::vec3 &color, float radius, float multiplier, bool shadow) :
|
||||
SceneNode(sceneGraph),
|
||||
_priority(priority),
|
||||
_color(color),
|
||||
_radius(radius),
|
||||
_multiplier(multiplier),
|
||||
_shadow(shadow) {
|
||||
}
|
||||
|
||||
|
@ -43,6 +44,10 @@ float LightSceneNode::radius() const {
|
|||
return _radius;
|
||||
}
|
||||
|
||||
float LightSceneNode::multiplier() const {
|
||||
return _multiplier;
|
||||
}
|
||||
|
||||
bool LightSceneNode::shadow() const {
|
||||
return _shadow;
|
||||
}
|
||||
|
|
|
@ -25,17 +25,19 @@ namespace scene {
|
|||
|
||||
class LightSceneNode : public SceneNode {
|
||||
public:
|
||||
LightSceneNode(SceneGraph *sceneGraph, int priority, const glm::vec3 &color, float radius, bool shadow);
|
||||
LightSceneNode(SceneGraph *sceneGraph, int priority, const glm::vec3 &color, float radius, float multiplier, bool shadow);
|
||||
|
||||
int priority() const;
|
||||
const glm::vec3 &color() const;
|
||||
float radius() const;
|
||||
float multiplier() const;
|
||||
bool shadow() const;
|
||||
|
||||
private:
|
||||
int _priority { 0 };
|
||||
glm::vec3 _color { 1.0f };
|
||||
float _radius { 1.0f };
|
||||
float _multiplier { 1.0f };
|
||||
bool _shadow { false };
|
||||
};
|
||||
|
||||
|
|
|
@ -83,6 +83,9 @@ void ModelNodeSceneNode::renderSingle() const {
|
|||
if (mesh->hasBumpmapTexture()) {
|
||||
locals.general.bumpmapEnabled = true;
|
||||
}
|
||||
if (_modelSceneNode->model()->classification() == Model::Classification::Other) {
|
||||
locals.general.shadowsEnabled = true;
|
||||
}
|
||||
if (skeletal) {
|
||||
locals.general.skeletalEnabled = true;
|
||||
locals.skeletal = Shaders::instance().skeletalUniforms();
|
||||
|
|
|
@ -67,7 +67,7 @@ void ModelSceneNode::initModelNodes() {
|
|||
|
||||
shared_ptr<ModelNode::Light> light(child->light());
|
||||
if (light) {
|
||||
shared_ptr<LightSceneNode> lightNode(new LightSceneNode(_sceneGraph, light->priority, child->color(), child->radius(), light->shadow));
|
||||
shared_ptr<LightSceneNode> lightNode(new LightSceneNode(_sceneGraph, light->priority, child->color(), child->radius(), child->multiplier(), light->shadow));
|
||||
childNode->addChild(lightNode);
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ void ModelSceneNode::updateLighting() {
|
|||
_lightsAffectedBy.clear();
|
||||
glm::vec3 center(_absoluteTransform * glm::vec4(_model->aabb().center(), 1.0f));
|
||||
|
||||
_sceneGraph->getLightsAt(center, _lightsAffectedBy);
|
||||
_sceneGraph->getLightsAt(center, kMaxLightCount, [](auto &) { return true; }, _lightsAffectedBy);
|
||||
_lightingDirty = false;
|
||||
|
||||
for (auto &attached : _attachedModels) {
|
||||
|
|
|
@ -60,6 +60,7 @@ void SceneGraph::prepareFrame() {
|
|||
if (!_activeCamera) return;
|
||||
|
||||
refreshMeshesAndLights();
|
||||
refreshShadowLights();
|
||||
|
||||
for (auto &root : _roots) {
|
||||
ModelSceneNode *modelNode = dynamic_cast<ModelSceneNode *>(root.get());
|
||||
|
@ -128,6 +129,28 @@ void SceneGraph::refreshMeshesAndLights() {
|
|||
}
|
||||
}
|
||||
|
||||
void SceneGraph::refreshShadowLights() {
|
||||
_shadowLights.clear();
|
||||
|
||||
if (!_activeCamera) return;
|
||||
|
||||
glm::vec3 cameraPosition(_activeCamera->absoluteTransform()[3]);
|
||||
|
||||
vector<LightSceneNode *> nodes;
|
||||
getLightsAt(cameraPosition, kMaxShadowLightCount, [](auto &light) { return light.shadow(); }, nodes);
|
||||
|
||||
for (auto &node : nodes) {
|
||||
glm::mat4 projection, view;
|
||||
getLightProjectionView(*node, projection, view);
|
||||
|
||||
ShadowLight light;
|
||||
light.position = node->absoluteTransform()[3];
|
||||
light.view = view;
|
||||
light.projection = projection;
|
||||
_shadowLights.push_back(move(light));
|
||||
}
|
||||
}
|
||||
|
||||
void SceneGraph::render() const {
|
||||
if (!_activeCamera) return;
|
||||
|
||||
|
@ -136,8 +159,19 @@ void SceneGraph::render() const {
|
|||
globals.view = _activeCamera->view();
|
||||
globals.cameraPosition = _activeCamera->absoluteTransform()[3];
|
||||
|
||||
int lightCount = static_cast<int>(_shadowLights.size());
|
||||
globals.shadows.shadowLightCount = lightCount;
|
||||
for (int i = 0; i < lightCount; ++i) {
|
||||
ShadowLight &light = globals.shadows.shadowLights[i];
|
||||
light = _shadowLights[i];
|
||||
}
|
||||
|
||||
Shaders::instance().setGlobalUniforms(globals);
|
||||
|
||||
renderNoGlobalUniforms();
|
||||
}
|
||||
|
||||
void SceneGraph::renderNoGlobalUniforms() const {
|
||||
for (auto &root : _roots) {
|
||||
root->render();
|
||||
}
|
||||
|
@ -149,11 +183,22 @@ void SceneGraph::render() const {
|
|||
}
|
||||
}
|
||||
|
||||
void SceneGraph::getLightsAt(const glm::vec3 &position, vector<LightSceneNode *> &lights) const {
|
||||
const vector<ShadowLight> &SceneGraph::shadowLights() const {
|
||||
return _shadowLights;
|
||||
}
|
||||
|
||||
void SceneGraph::getLightProjectionView(const LightSceneNode &light, glm::mat4 &projection, glm::mat4 &view) const {
|
||||
projection = glm::perspective(glm::radians(100.0f), 1.0f, 1.0f, light.radius());
|
||||
view = light.absoluteTransformInverse();
|
||||
}
|
||||
|
||||
void SceneGraph::getLightsAt(const glm::vec3 &position, int count, const function<bool(const LightSceneNode &)> &pred, vector<LightSceneNode *> &lights) const {
|
||||
unordered_map<LightSceneNode *, float> distances;
|
||||
lights.clear();
|
||||
|
||||
for (auto &light : _lights) {
|
||||
if (!pred(*light)) continue;
|
||||
|
||||
float distance = light->distanceTo(position);
|
||||
float radius = light->radius();
|
||||
if (distance > radius) continue;
|
||||
|
@ -175,8 +220,8 @@ void SceneGraph::getLightsAt(const glm::vec3 &position, vector<LightSceneNode *>
|
|||
return leftDistance < rightDistance;
|
||||
});
|
||||
|
||||
if (lights.size() > kMaxLightCount) {
|
||||
lights.erase(lights.begin() + kMaxLightCount, lights.end());
|
||||
if (lights.size() > count) {
|
||||
lights.erase(lights.begin() + count, lights.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
@ -41,6 +42,7 @@ public:
|
|||
SceneGraph(const render::GraphicsOptions &opts);
|
||||
|
||||
void render() const override;
|
||||
void renderNoGlobalUniforms() const override;
|
||||
|
||||
void clear();
|
||||
|
||||
|
@ -54,7 +56,9 @@ public:
|
|||
|
||||
// Lights
|
||||
|
||||
void getLightsAt(const glm::vec3 &position, std::vector<LightSceneNode *> &lights) const;
|
||||
const std::vector<render::ShadowLight> &shadowLights() const override;
|
||||
|
||||
void getLightsAt(const glm::vec3 &position, int count, const std::function<bool(const LightSceneNode &)> &pred, std::vector<LightSceneNode *> &lights) const;
|
||||
|
||||
const glm::vec3 &ambientLightColor() const;
|
||||
void setAmbientLightColor(const glm::vec3 &color);
|
||||
|
@ -71,11 +75,15 @@ private:
|
|||
glm::vec3 _ambientLightColor { 0.5f };
|
||||
uint32_t _textureId { 0 };
|
||||
Octree _octree;
|
||||
std::vector<render::ShadowLight> _shadowLights;
|
||||
|
||||
SceneGraph(const SceneGraph &) = delete;
|
||||
SceneGraph &operator=(const SceneGraph &) = delete;
|
||||
|
||||
void refreshMeshesAndLights();
|
||||
void refreshShadowLights();
|
||||
|
||||
void getLightProjectionView(const LightSceneNode &light, glm::mat4 &projection, glm::mat4 &view) const;
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
|
Loading…
Reference in a new issue