Improve shadows
- Treat lights with radius of 1000 and higher as directional - Use orthographic projection for directional shadow lights
This commit is contained in:
parent
874a4006b6
commit
e3b5f3eba1
18 changed files with 239 additions and 140 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -88,6 +88,9 @@ void Textures::bindDefaults() {
|
|||
_default->bind();
|
||||
|
||||
setActiveTextureUnit(TextureUnits::shadowMap);
|
||||
_default->bind();
|
||||
|
||||
setActiveTextureUnit(TextureUnits::cubeShadowMap);
|
||||
_defaultCubemap->bind();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue