Improve shadows

- Treat lights with radius of 1000 and higher as directional
- Use orthographic projection for directional shadow lights
This commit is contained in:
Vsevolod Kremianskii 2021-04-21 09:51:26 +07:00
parent 874a4006b6
commit e3b5f3eba1
18 changed files with 239 additions and 140 deletions

View file

@ -35,7 +35,7 @@ namespace game {
AnimatedCamera::AnimatedCamera(SceneGraph *sceneGraph, float aspect) : _sceneGraph(sceneGraph), _aspect(aspect) {
_sceneGraph = sceneGraph;
_sceneNode = make_shared<CameraSceneNode>(_sceneGraph, glm::mat4(1.0f), _zFar);
_sceneNode = make_shared<CameraSceneNode>(_sceneGraph, glm::mat4(1.0f), _aspect, _zNear, _zFar);
updateProjection();
}

View file

@ -30,7 +30,7 @@ namespace game {
DialogCamera::DialogCamera(SceneGraph *sceneGraph, const CameraStyle &style, float aspect, float zNear, float zFar) {
glm::mat4 projection(glm::perspective(glm::radians(style.viewAngle), aspect, zNear, zFar));
_sceneNode = make_shared<CameraSceneNode>(sceneGraph, projection, zFar);
_sceneNode = make_shared<CameraSceneNode>(sceneGraph, projection, aspect, zNear, zFar);
}
void DialogCamera::setSpeakerPosition(const glm::vec3 &position) {

View file

@ -32,7 +32,7 @@ static constexpr float kMouseMultiplier = glm::pi<float>() / 4000.0f;
FirstPersonCamera::FirstPersonCamera(SceneGraph *sceneGraph, float aspect, float fovy, float zNear, float zFar) {
glm::mat4 projection(glm::perspective(fovy, aspect, zNear, zFar));
_sceneNode = make_unique<CameraSceneNode>(sceneGraph, projection, zFar);
_sceneNode = make_unique<CameraSceneNode>(sceneGraph, projection, aspect, zNear, zFar);
}
bool FirstPersonCamera::handle(const SDL_Event &event) {

View file

@ -29,14 +29,15 @@ namespace reone {
namespace game {
static constexpr float kNearPlane = 0.1f;
static constexpr float kFarPlane = 10000.0f;
StaticCamera::StaticCamera(SceneGraph *sceneGraph, float aspect) : _aspect(aspect) {
_sceneNode = make_unique<CameraSceneNode>(sceneGraph, glm::mat4(1.0f), kFarPlane);
_sceneNode = make_unique<CameraSceneNode>(sceneGraph, glm::mat4(1.0f), aspect, kNearPlane, kFarPlane);
}
void StaticCamera::setObject(const PlaceableCamera &object) {
glm::mat4 projection(glm::perspective(glm::radians(object.fieldOfView()), _aspect, 0.1f, kFarPlane));
glm::mat4 projection(glm::perspective(glm::radians(object.fieldOfView()), _aspect, kNearPlane, kFarPlane));
_sceneNode->setLocalTransform(object.transform());
_sceneNode->setProjection(projection);

View file

@ -36,7 +36,7 @@ static constexpr float kMouseRotationSpeed = 0.001f;
ThirdPersonCamera::ThirdPersonCamera(Game *game, SceneGraph *sceneGraph, float aspect, const CameraStyle &style, float zNear, float zFar) : _game(game) {
glm::mat4 projection(glm::perspective(glm::radians(style.viewAngle), aspect, zNear, zFar));
_sceneNode = make_unique<CameraSceneNode>(sceneGraph, projection, zFar);
_sceneNode = make_unique<CameraSceneNode>(sceneGraph, projection, aspect, zNear, zFar);
_style = style;
}

View file

@ -51,7 +51,7 @@ unique_ptr<SceneGraph> SceneBuilder::build() {
_modelScale + _modelOffset.y,
_zNear, _zFar));
auto camera = make_shared<CameraSceneNode>(scene.get(), projection, _zFar);
auto camera = make_shared<CameraSceneNode>(scene.get(), projection, _aspect, _zNear, _zFar);
if (_cameraNodeName.empty()) {
camera->setLocalTransform(_cameraTransform);
} else {

View file

@ -106,7 +106,7 @@ struct Material {
};
struct Shadows {
mat4 matrices[NUM_CUBE_FACES];
mat4 lightSpaceMatrices[NUM_CUBE_FACES];
vec4 lightPosition;
bool lightPresent;
float strength;
@ -193,13 +193,15 @@ const float SELFILLUM_THRESHOLD = 0.85;
uniform sampler2D uDiffuse;
uniform sampler2D uLightmap;
uniform sampler2D uBumpmap;
uniform sampler2D uShadowMap;
uniform samplerCube uEnvmap;
uniform samplerCube uShadowMap;
uniform samplerCube uCubeShadowMap;
in vec3 fragPosition;
in vec3 fragNormal;
in vec2 fragTexCoords;
in vec2 fragLightmapCoords;
in vec4 fragPosLightSpace;
in mat3 fragTanSpace;
layout(location = 0) out vec4 fragColor;
@ -263,30 +265,57 @@ vec3 getNormalFromBumpmap(vec2 uv) {
}
float getShadow() {
if (!uShadows.lightPresent) return 0.0;
vec3 fragToLight = fragPosition - uShadows.lightPosition.xyz;
float currentDepth = length(fragToLight);
float shadow = 0.0;
float bias = 0.1;
float samples = 4.0;
float offset = 0.1;
for (float x = -offset; x < offset; x += offset / (samples * 0.5)) {
for (float y = -offset; y < offset; y += offset / (samples * 0.5)) {
for (float z = -offset; z < offset; z += offset / (samples * 0.5)) {
float closestDepth = texture(uShadowMap, fragToLight + vec3(x, y, z)).r;
closestDepth *= SHADOW_FAR_PLANE;
if (uShadows.lightPresent) {
if (uShadows.lightPosition.w == 0.0) {
// Directional light
if (currentDepth - bias > closestDepth) {
shadow += 1.0;
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
float closestDepth = texture(uShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
vec2 texelSize = 1.0 / textureSize(uShadowMap, 0);
for (int x = -1; x <= 1; ++x) {
for (int y = -1; y <= 1; ++y) {
float pcfDepth = texture(uShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
if (projCoords.z > 1.0) {
shadow = 0.0;
}
} else {
// Point light
vec3 fragToLight = fragPosition - uShadows.lightPosition.xyz;
float currentDepth = length(fragToLight);
float bias = 0.1;
float samples = 4.0;
float offset = 0.1;
for (float x = -offset; x < offset; x += offset / (samples * 0.5)) {
for (float y = -offset; y < offset; y += offset / (samples * 0.5)) {
for (float z = -offset; z < offset; z += offset / (samples * 0.5)) {
float closestDepth = texture(uCubeShadowMap, fragToLight + vec3(x, y, z)).r;
closestDepth *= SHADOW_FAR_PLANE;
if (currentDepth - bias > closestDepth) {
shadow += 1.0;
}
}
}
}
shadow /= samples * samples * samples;
}
}
return uShadows.strength * shadow / (samples * samples * samples);
return uShadows.strength * shadow;
}
float getLightAttenuation(int light) {
@ -451,6 +480,7 @@ out vec3 fragPosition;
out vec3 fragNormal;
out vec2 fragTexCoords;
out vec2 fragLightmapCoords;
out vec4 fragPosLightSpace;
out mat3 fragTanSpace;
void main() {
@ -485,6 +515,13 @@ void main() {
fragTexCoords = aTexCoords;
fragLightmapCoords = aLightmapCoords;
// Compute light space fragment position for directional lights
if (uShadows.lightPresent && uShadows.lightPosition.w == 0.0) {
fragPosLightSpace = uShadows.lightSpaceMatrices[0] * vec4(fragPosition, 1.0);
} else {
fragPosLightSpace = vec4(0.0);
}
if (isFeatureEnabled(FEATURE_BUMPMAPS)) {
vec3 T = normalize(vec3(uGeneral.model * vec4(aTangent, 0.0)));
vec3 B = normalize(vec3(uGeneral.model * vec4(aBitangent, 0.0)));
@ -592,14 +629,23 @@ layout(triangle_strip, max_vertices=18) out;
out vec4 fragPosition;
void main() {
for (int face = 0; face < NUM_CUBE_FACES; ++face) {
gl_Layer = face;
if (uShadows.lightPosition.w == 0.0) {
for (int i = 0; i < 3; ++i) {
fragPosition = gl_in[i].gl_Position;
gl_Position = uShadows.matrices[face] * fragPosition;
gl_Position = uShadows.lightSpaceMatrices[0] * fragPosition;
EmitVertex();
}
EndPrimitive();
} else {
for (int face = 0; face < NUM_CUBE_FACES; ++face) {
gl_Layer = face;
for (int i = 0; i < 3; ++i) {
fragPosition = gl_in[i].gl_Position;
gl_Position = uShadows.lightSpaceMatrices[face] * fragPosition;
EmitVertex();
}
EndPrimitive();
}
}
}
)END";
@ -1243,6 +1289,7 @@ void Shaders::initTextureUniforms() {
setUniform("uPrefilterMap", TextureUnits::prefilterMap);
setUniform("uBRDFLookup", TextureUnits::brdfLookup);
setUniform("uShadowMap", TextureUnits::shadowMap);
setUniform("uCubeShadowMap", TextureUnits::cubeShadowMap);
}
Shaders::~Shaders() {

View file

@ -103,8 +103,8 @@ struct ShaderMaterial {
};
struct ShaderShadows {
glm::mat4 matrices[kNumCubeFaces];
glm::vec4 lightPosition { 0.0f };
glm::mat4 lightSpaceMatrices[kNumCubeFaces];
glm::vec4 lightPosition { 0.0f }; /**< W = 0 if light is directional */
int lightPresent { false };
float strength { 1.0f };
char padding[8];
@ -136,7 +136,7 @@ struct CombinedUniforms {
};
struct ShaderLight {
glm::vec4 position { 0.0f };
glm::vec4 position { 0.0f }; /**< W = 0 if light is directional */
glm::vec4 color { 1.0f };
float multiplier { 1.0f };
float radius { 1.0f };

View file

@ -88,6 +88,9 @@ void Textures::bindDefaults() {
_default->bind();
setActiveTextureUnit(TextureUnits::shadowMap);
_default->bind();
setActiveTextureUnit(TextureUnits::cubeShadowMap);
_defaultCubemap->bind();
}

View file

@ -86,11 +86,12 @@ struct TextureUnits {
static constexpr int lightmap { 1 };
static constexpr int envmap { 2 };
static constexpr int bumpmap { 3 };
static constexpr int bloom { 5 };
static constexpr int irradianceMap { 6 };
static constexpr int prefilterMap { 7 };
static constexpr int brdfLookup { 8 };
static constexpr int shadowMap { 9 };
static constexpr int bloom { 4 };
static constexpr int irradianceMap { 5 };
static constexpr int prefilterMap { 6 };
static constexpr int brdfLookup { 7 };
static constexpr int shadowMap { 8 };
static constexpr int cubeShadowMap { 9 };
};
class IEventHandler {

View file

@ -25,9 +25,11 @@ namespace reone {
namespace scene {
CameraSceneNode::CameraSceneNode(SceneGraph *sceneGraph, const glm::mat4 &projection, float farPlane) :
CameraSceneNode::CameraSceneNode(SceneGraph *sceneGraph, glm::mat4 projection, float aspect, float nearPlane, float farPlane) :
SceneNode(SceneNodeType::Camera, sceneGraph),
_projection(projection),
_aspect(aspect),
_nearPlane(nearPlane),
_farPlane(farPlane) {
updateFrustum();
@ -46,22 +48,22 @@ void CameraSceneNode::updateView() {
void CameraSceneNode::updateFrustum() {
glm::mat4 vp(_projection * _view);
for (int i = 3; i >= 0; --i) {
_frustum[0][i] = vp[i][3] + vp[i][0];
_frustum[1][i] = vp[i][3] - vp[i][0];
_frustum[2][i] = vp[i][3] + vp[i][1];
_frustum[3][i] = vp[i][3] - vp[i][1];
_frustum[4][i] = vp[i][3] + vp[i][2];
_frustum[5][i] = vp[i][3] - vp[i][2];
_frustumPlanes[0][i] = vp[i][3] + vp[i][0];
_frustumPlanes[1][i] = vp[i][3] - vp[i][0];
_frustumPlanes[2][i] = vp[i][3] + vp[i][1];
_frustumPlanes[3][i] = vp[i][3] - vp[i][1];
_frustumPlanes[4][i] = vp[i][3] + vp[i][2];
_frustumPlanes[5][i] = vp[i][3] - vp[i][2];
}
for (int i = 0; i < kFrustumPlaneCount; ++i) {
_frustum[i] = glm::normalize(_frustum[i]);
for (int i = 0; i < kNumFrustumPlanes; ++i) {
_frustumPlanes[i] = glm::normalize(_frustumPlanes[i]);
}
}
bool CameraSceneNode::isInFrustum(const glm::vec3 &point) const {
glm::vec4 point4(point, 1.0f);
for (int i = 0; i < kFrustumPlaneCount; ++i) {
if (glm::dot(_frustum[i], point4) < 0.0f) return false;
for (int i = 0; i < kNumFrustumPlanes; ++i) {
if (glm::dot(_frustumPlanes[i], point4) < 0.0f) return false;
}
return true;
}
@ -70,15 +72,15 @@ bool CameraSceneNode::isInFrustum(const AABB &aabb) const {
glm::vec3 center(aabb.center());
glm::vec3 halfSize(aabb.getSize() * 0.5f);
for (int i = 0; i < kFrustumPlaneCount; ++i) {
if (glm::dot(_frustum[i], glm::vec4(center.x - halfSize.x, center.y - halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustum[i], glm::vec4(center.x + halfSize.x, center.y - halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustum[i], glm::vec4(center.x - halfSize.x, center.y + halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustum[i], glm::vec4(center.x - halfSize.x, center.y - halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustum[i], glm::vec4(center.x + halfSize.x, center.y + halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustum[i], glm::vec4(center.x + halfSize.x, center.y - halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustum[i], glm::vec4(center.x - halfSize.x, center.y + halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustum[i], glm::vec4(center.x + halfSize.x, center.y + halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
for (int i = 0; i < kNumFrustumPlanes; ++i) {
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x - halfSize.x, center.y - halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x + halfSize.x, center.y - halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x - halfSize.x, center.y + halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x - halfSize.x, center.y - halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x + halfSize.x, center.y + halfSize.y, center.z - halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x + halfSize.x, center.y - halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x - halfSize.x, center.y + halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
if (glm::dot(_frustumPlanes[i], glm::vec4(center.x + halfSize.x, center.y + halfSize.y, center.z + halfSize.z, 1.0f)) >= 0.0f) continue;
return false;
}
@ -91,10 +93,6 @@ void CameraSceneNode::setProjection(const glm::mat4 &projection) {
updateFrustum();
}
void CameraSceneNode::setFarPlane(float far) {
_farPlane = far;
}
} // namespace scene
} // namespace reone

View file

@ -25,27 +25,30 @@ namespace reone {
namespace scene {
const int kFrustumPlaneCount = 6;
const int kNumFrustumPlanes = 6;
class CameraSceneNode : public SceneNode {
public:
CameraSceneNode(SceneGraph *sceneGraph, const glm::mat4 &projection, float farPlane);
CameraSceneNode(SceneGraph *sceneGraph, glm::mat4 projection, float aspect, float nearPlane, float farPlane);
bool isInFrustum(const glm::vec3 &point) const;
bool isInFrustum(const render::AABB &aabb) const;
const glm::mat4 &projection() const { return _projection; }
const glm::mat4 &view() const { return _view; }
float aspect() const { return _aspect; }
float nearPlane() const { return _nearPlane; }
float farPlane() const { return _farPlane; }
void setProjection(const glm::mat4 &projection);
void setFarPlane(float far);
private:
glm::mat4 _projection { 1.0f };
glm::mat4 _view { 1.0f };
glm::vec4 _frustum[kFrustumPlaneCount];
float _farPlane { 1.0f };
glm::vec4 _frustumPlanes[kNumFrustumPlanes];
float _aspect { 1.0f };
float _nearPlane { 0.0f };
float _farPlane { 0.0f };
void updateAbsoluteTransform() override;

View file

@ -19,8 +19,6 @@
#include "../scenegraph.h"
using namespace std;
namespace reone {
namespace scene {
@ -30,26 +28,6 @@ LightSceneNode::LightSceneNode(int priority, SceneGraph *sceneGraph) :
_priority(priority) {
}
void LightSceneNode::setColor(glm::vec3 color) {
_color = move(color);
}
void LightSceneNode::setMultiplier(float multiplier) {
_multiplier = multiplier;
}
void LightSceneNode::setRadius(float radius) {
_radius = radius;
}
void LightSceneNode::setShadow(bool shadow) {
_shadow = shadow;
}
void LightSceneNode::setAmbientOnly(bool ambientOnly) {
_ambientOnly = ambientOnly;
}
} // namespace scene
} // namespace reone

View file

@ -31,17 +31,19 @@ public:
bool isShadow() const { return _shadow; }
bool isAmbientOnly() const { return _ambientOnly; }
bool isDirectional() const { return _directional; }
const glm::vec3 &color() const { return _color; }
int priority() const { return _priority; }
float multiplier() const { return _multiplier; }
float radius() const { return _radius; }
void setColor(glm::vec3 color);
void setMultiplier(float multiplier);
void setRadius(float radius);
void setShadow(bool shadow);
void setAmbientOnly(bool ambientOnly);
void setColor(glm::vec3 color) { _color = std::move(color); }
void setMultiplier(float multiplier) { _multiplier = multiplier; }
void setRadius(float radius) { _radius = radius; }
void setShadow(bool shadow) { _shadow = shadow; }
void setAmbientOnly(bool ambientOnly) { _ambientOnly = ambientOnly; }
void setDirectional(bool directional) { _directional = directional; }
private:
int _priority;
@ -51,6 +53,7 @@ private:
float _radius { 1.0f };
bool _shadow { false };
bool _ambientOnly { false };
bool _directional { false };
};
} // namespace scene

View file

@ -288,8 +288,11 @@ void ModelNodeSceneNode::drawSingle(bool shadowPass) {
uniforms.lighting->lightCount = static_cast<int>(lights.size());
for (int i = 0; i < uniforms.lighting->lightCount; ++i) {
glm::vec4 position(lights[i]->absoluteTransform()[3]);
position.w = lights[i]->isDirectional() ? 0.0f : 1.0f;
ShaderLight &shaderLight = uniforms.lighting->lights[i];
shaderLight.position = lights[i]->absoluteTransform()[3];
shaderLight.position = move(position);
shaderLight.color = glm::vec4(lights[i]->color(), 1.0f);
shaderLight.multiplier = lights[i]->multiplier();
shaderLight.radius = lights[i]->radius();

View file

@ -43,6 +43,8 @@ namespace reone {
namespace scene {
const float kMinDirectionalLightRadius = 1000.0f;
static bool g_debugAABB = false;
ModelSceneNode::ModelSceneNode(ModelUsage usage, const shared_ptr<Model> &model, SceneGraph *sceneGraph, set<string> ignoreNodes) :
@ -113,12 +115,17 @@ void ModelSceneNode::initModelNodes() {
shared_ptr<ModelNode::Light> light(child->light());
if (light) {
// Light is considered directional if its radius exceeds a certain threshold
float radius = child->lightRadii().getByKeyframeOrElse(0, 1.0f);
bool directional = radius >= kMinDirectionalLightRadius;
auto lightNode = make_shared<LightSceneNode>(light->priority, _sceneGraph);
lightNode->setColor(child->lightColors().getByKeyframeOrElse(0, glm::vec3(1.0f)));
lightNode->setMultiplier(child->lightMultipliers().getByKeyframeOrElse(0, 1.0f));
lightNode->setRadius(child->lightRadii().getByKeyframeOrElse(0, 1.0f));
lightNode->setRadius(radius);
lightNode->setShadow(light->shadow);
lightNode->setAmbientOnly(light->ambientOnly);
lightNode->setDirectional(directional);
childNode->addChild(lightNode);
_lightNodeByNumber.insert(make_pair(modelNode->nodeNumber(), lightNode.get()));
}

View file

@ -43,13 +43,19 @@ namespace reone {
namespace scene {
static constexpr float kShadowFarPlane = 10000.0f;
const float kShadowNearPlane = 0.0f;
const float kShadowFarPlane = 10000.0f;
const float kOrthographicScale = 10.0f;
static bool g_wireframesEnabled = false;
static bool g_debugCubeMap = false;
WorldRenderPipeline::WorldRenderPipeline(SceneGraph *scene, const GraphicsOptions &opts) :
_scene(scene), _opts(opts) {
for (int i = 0; i < kNumCubeFaces; ++i) {
_lightSpaceMatrices[i] = glm::mat4(1.0f);
}
}
void WorldRenderPipeline::init() {
@ -113,19 +119,21 @@ void WorldRenderPipeline::init() {
// Shadows framebuffer
_shadowsDepth = make_unique<Texture>("shadows_depth", getTextureProperties(TextureUsage::CubeMapDepthBuffer));
_shadowsDepth = make_unique<Texture>("shadows_depth", getTextureProperties(TextureUsage::DepthBuffer));
_shadowsDepth->init();
_shadowsDepth->bind();
_shadowsDepth->clearPixels(1024 * _opts.shadowResolution, 1024 * _opts.shadowResolution, PixelFormat::Depth);
_cubeShadowsDepth = make_unique<Texture>("cubeshadows_depth", getTextureProperties(TextureUsage::CubeMapDepthBuffer));
_cubeShadowsDepth->init();
_cubeShadowsDepth->bind();
_cubeShadowsDepth->clearPixels(1024 * _opts.shadowResolution, 1024 * _opts.shadowResolution, PixelFormat::Depth);
_shadows.init();
_shadows.bind();
_shadows.attachDepth(*_shadowsDepth);
_shadows.checkCompleteness();
_shadows.unbind();
}
void WorldRenderPipeline::render() {
computeLightSpaceMatrices();
drawShadows();
drawGeometry();
applyHorizontalBlur();
@ -133,39 +141,8 @@ void WorldRenderPipeline::render() {
drawResult();
}
void WorldRenderPipeline::drawShadows() {
if (_opts.shadowResolution < 1) return;
const LightSceneNode *shadowLight = _scene->shadowLight();
if (!shadowLight) return;
withViewport(glm::ivec4(0, 0, 1024 * _opts.shadowResolution, 1024 * _opts.shadowResolution), [&]() {
_shadows.bind();
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glm::mat4 projection(glm::perspective(glm::radians(90.0f), 1.0f, 1.0f, kShadowFarPlane));
glm::vec3 lightPosition(shadowLight->absoluteTransform()[3]);
ShaderUniforms uniforms(Shaders::instance().defaultUniforms());
uniforms.combined.featureMask |= UniformFeatureFlags::shadows;
uniforms.combined.shadows.lightPosition = glm::vec4(lightPosition, 1.0f);
for (int i = 0; i < kNumCubeFaces; ++i) {
auto side = static_cast<CubeMapFace>(i);
uniforms.combined.shadows.matrices[i] = projection * getShadowView(lightPosition, side);
}
_scene->setUniformsPrototype(move(uniforms));
glClear(GL_DEPTH_BUFFER_BIT);
withDepthTest([this]() { _scene->draw(true); });
});
}
glm::mat4 WorldRenderPipeline::getShadowView(const glm::vec3 &lightPos, CubeMapFace side) const {
switch (side) {
static glm::mat4 getPointLightView(const glm::vec3 &lightPos, CubeMapFace face) {
switch (face) {
case CubeMapFace::PositiveX:
return glm::lookAt(lightPos, lightPos + glm::vec3(1.0, 0.0, 0.0), glm::vec3(0.0, -1.0, 0.0));
case CubeMapFace::NegativeX:
@ -183,6 +160,65 @@ glm::mat4 WorldRenderPipeline::getShadowView(const glm::vec3 &lightPos, CubeMapF
}
}
void WorldRenderPipeline::computeLightSpaceMatrices() {
static glm::vec3 up(0.0f, 0.0f, 1.0f);
const LightSceneNode *shadowLight = _scene->shadowLight();
if (!shadowLight) return;
shared_ptr<CameraSceneNode> camera(_scene->activeCamera());
if (!camera) return;
glm::vec3 lightPosition(shadowLight->absoluteTransform()[3]);
glm::vec3 cameraPosition(camera->absoluteTransform()[3]);
if (shadowLight->isDirectional()) {
glm::mat4 projection(glm::ortho(-kOrthographicScale, kOrthographicScale, -kOrthographicScale, kOrthographicScale, kShadowNearPlane, kShadowFarPlane));
glm::mat4 lightView(glm::lookAt(lightPosition, cameraPosition, up));
_lightSpaceMatrices[0] = projection * lightView;
} else {
glm::mat4 projection(glm::perspective(glm::radians(90.0f), 1.0f, kShadowNearPlane, kShadowFarPlane));
for (int i = 0; i < kNumCubeFaces; ++i) {
glm::mat4 lightView(getPointLightView(lightPosition, static_cast<CubeMapFace>(i)));
_lightSpaceMatrices[i] = projection * lightView;
}
}
}
void WorldRenderPipeline::drawShadows() {
if (_opts.shadowResolution < 1) return;
const LightSceneNode *shadowLight = _scene->shadowLight();
if (!shadowLight) return;
withViewport(glm::ivec4(0, 0, 1024 * _opts.shadowResolution, 1024 * _opts.shadowResolution), [&]() {
_shadows.bind();
if (shadowLight->isDirectional()) {
_shadows.attachDepth(*_shadowsDepth);
} else {
_shadows.attachDepth(*_cubeShadowsDepth);
}
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glm::vec4 lightPosition(
glm::vec3(shadowLight->absoluteTransform()[3]),
shadowLight->isDirectional() ? 0.0f : 1.0f);
ShaderUniforms uniforms(Shaders::instance().defaultUniforms());
uniforms.combined.featureMask |= UniformFeatureFlags::shadows;
uniforms.combined.shadows.lightPosition = move(lightPosition);
for (int i = 0; i < kNumCubeFaces; ++i) {
uniforms.combined.shadows.lightSpaceMatrices[i] = _lightSpaceMatrices[i];
}
_scene->setUniformsPrototype(move(uniforms));
glClear(GL_DEPTH_BUFFER_BIT);
withDepthTest([this]() { _scene->draw(true); });
});
}
void WorldRenderPipeline::drawGeometry() {
_geometry.bind();
@ -196,13 +232,30 @@ void WorldRenderPipeline::drawGeometry() {
uniforms.combined.general.view = _scene->activeCamera()->view();
uniforms.combined.general.cameraPosition = _scene->activeCamera()->absoluteTransform()[3];
uniforms.combined.shadows.lightPresent = static_cast<bool>(shadowLight);
uniforms.combined.shadows.lightPosition = shadowLight ? shadowLight->absoluteTransform()[3] : glm::vec4(0.0f);
uniforms.combined.shadows.strength = _scene->shadowStrength();
if (shadowLight) {
glm::vec4 lightPosition(
glm::vec3(shadowLight->absoluteTransform()[3]),
shadowLight->isDirectional() ? 0.0f : 1.0f);
uniforms.combined.shadows.lightPosition = move(lightPosition);
uniforms.combined.shadows.strength = _scene->shadowStrength();
for (int i = 0; i < kNumCubeFaces; ++i) {
uniforms.combined.shadows.lightSpaceMatrices[i] = _lightSpaceMatrices[i];
}
}
_scene->setUniformsPrototype(move(uniforms));
if (shadowLight) {
setActiveTextureUnit(TextureUnits::shadowMap);
_shadowsDepth->bind();
if (shadowLight->isDirectional()) {
setActiveTextureUnit(TextureUnits::shadowMap);
_shadowsDepth->bind();
} else {
setActiveTextureUnit(TextureUnits::cubeShadowMap);
_cubeShadowsDepth->bind();
}
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

View file

@ -25,7 +25,8 @@
#include "../../render/renderbuffer.h"
#include "../../render/texture.h"
#include "../../render/types.h"
#include "../../scene/scenegraph.h"
#include "../scenegraph.h"
namespace reone {
@ -41,6 +42,7 @@ public:
private:
SceneGraph *_scene { nullptr };
render::GraphicsOptions _opts;
glm::mat4 _lightSpaceMatrices[render::kNumCubeFaces];
// Framebuffers
@ -59,16 +61,16 @@ private:
std::unique_ptr<render::Texture> _verticalBlurColor;
std::unique_ptr<render::Texture> _horizontalBlurColor;
std::unique_ptr<render::Texture> _shadowsDepth;
std::unique_ptr<render::Texture> _cubeShadowsDepth;
// END Framebuffers targets
void computeLightSpaceMatrices();
void drawShadows();
void drawGeometry();
void applyHorizontalBlur();
void applyVerticalBlur();
void drawResult();
glm::mat4 getShadowView(const glm::vec3 &lightPos, render::CubeMapFace side) const;
};
} // namespace scene