Merge pull request #1027 from OpenSpace/feature/RingShadows

Feature/ring shadows
This commit is contained in:
Alexander Bock
2020-01-24 21:23:59 +01:00
committed by GitHub
18 changed files with 2076 additions and 40 deletions

View File

@@ -34,7 +34,16 @@ local Saturn = {
Enabled = true
}
}
}
},
Rings = {
Texture = textures .. "/saturn_rings.png",
Size = 140445000,
Offset = { 74500 / 140445.100671159, 1.0 }, -- min / max extend
},
Shadows = {
Enabled = true,
DistanceFraction = 40.0
}
},
Tag = { "planet_solarSystem", "planet_giants" },
GUI = {
@@ -42,21 +51,6 @@ local Saturn = {
}
}
local SaturnRings = {
Identifier = "SaturnRings",
Parent = Saturn.Identifier,
Renderable = {
Type = "RenderableRings",
Texture = textures .. "/saturn_rings.png",
Size = 140445000,
Offset = { 74500 / 140445.100671159, 1.0 } -- min / max extend
},
GUI = {
Name = "Saturn Rings",
Path = "/Solar System/Planets/Saturn"
}
}
local SaturnLabel = {
Identifier = "SaturnLabel",
Parent = Saturn.Identifier,
@@ -78,5 +72,4 @@ local SaturnLabel = {
}
}
assetHelper.registerSceneGraphNodesAndExport(asset, { Saturn, SaturnRings, SaturnLabel })
assetHelper.registerSceneGraphNodesAndExport(asset, { Saturn, SaturnLabel })

View File

@@ -51,6 +51,8 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/src/rawtile.h
${CMAKE_CURRENT_SOURCE_DIR}/src/rawtiledatareader.h
${CMAKE_CURRENT_SOURCE_DIR}/src/renderableglobe.h
${CMAKE_CURRENT_SOURCE_DIR}/src/ringscomponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/shadowcomponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/skirtedgrid.h
${CMAKE_CURRENT_SOURCE_DIR}/src/tileindex.h
${CMAKE_CURRENT_SOURCE_DIR}/src/tileloadjob.h
@@ -80,6 +82,8 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/src/rawtile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rawtiledatareader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/renderableglobe.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ringscomponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/shadowcomponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/skirtedgrid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tileindex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/tileloadjob.cpp
@@ -94,6 +98,10 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/shaders/globalrenderer_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/localrenderer_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/renderer_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_geom_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_geom_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/texturetilemapping.hglsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/tile.hglsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/tileheight.hglsl

View File

@@ -48,6 +48,14 @@ out vec3 positionCameraSpace;
uniform dmat4 modelTransform;
#endif
#if SHADOW_MAPPING_ENABLED
// ShadowMatrix is the matrix defined by:
// textureCoordsMatrix * projectionMatrix * combinedViewMatrix * modelMatrix
// where textureCoordsMatrix is just a scale and bias computation: [-1,1] to [0,1]
uniform dmat4 shadowMatrix;
out vec4 shadowCoords;
#endif
uniform mat4 modelViewProjectionTransform;
uniform mat4 modelViewTransform;
uniform vec3 radiiSquared;
@@ -130,4 +138,8 @@ void main() {
#if USE_ECLIPSE_SHADOWS
positionWorldSpace = vec3(modelTransform * dvec4(pair.position, 1.0));
#endif
#if SHADOW_MAPPING_ENABLED
shadowCoords = vec4(shadowMatrix * dvec4(pair.position, 1.0));
#endif
}

View File

@@ -48,6 +48,14 @@ out vec3 positionWorldSpace;
uniform dmat4 inverseViewTransform;
#endif
#if SHADOW_MAPPING_ENABLED
// ShadowMatrix is the matrix defined by:
// textureCoordsMatrix * projectionMatrix * combinedViewMatrix * modelMatrix
// where textureCoordsMatrix is just a scale and bias computation: [-1,1] to [0,1]
uniform dmat4 shadowMatrix;
out vec4 shadowCoords;
#endif
uniform mat4 projectionTransform;
// Input points in camera space
uniform vec3 p00;
@@ -113,4 +121,8 @@ void main() {
#if USE_ECLIPSE_SHADOWS
positionWorldSpace = vec3(inverseViewTransform * dvec4(p, 1.0));
#endif
#if SHADOW_MAPPING_ENABLED
shadowCoords = vec4(shadowMatrix * dvec4(p, 1.0));
#endif
}

View File

@@ -59,6 +59,13 @@ uniform vec3 lightDirectionCameraSpace;
uniform float orenNayarRoughness;
#endif
#if SHADOW_MAPPING_ENABLED
in vec4 shadowCoords;
uniform sampler2DShadow shadowMapTexture;
uniform int nShadowSamples;
uniform float zFightingPercentage;
#endif
#if USE_ECLIPSE_SHADOWS
/*******************************************************************************
@@ -258,5 +265,30 @@ Fragment getFragment() {
}
#endif // SHOW_CHUNK_EDGES
#if SHADOW_MAPPING_ENABLED
float shadow = 1.0;
if (shadowCoords.w > 1) {
vec4 normalizedShadowCoords = shadowCoords;
normalizedShadowCoords.z = normalizeFloat(zFightingPercentage * normalizedShadowCoords.w);
normalizedShadowCoords.xy = normalizedShadowCoords.xy / normalizedShadowCoords.w;
normalizedShadowCoords.w = 1.0;
float sum = 0;
for (int i = 0; i < nShadowSamples; ++i) {
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(-nShadowSamples + i, -nShadowSamples + i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(-nShadowSamples + i, 0));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(-nShadowSamples + i, nShadowSamples - i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( 0 , -nShadowSamples + i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( 0 , nShadowSamples - i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( nShadowSamples - i, -nShadowSamples + i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( nShadowSamples - i, 0));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( nShadowSamples - i, nShadowSamples - i));
}
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(0, 0));
shadow = sum / (8.0 * nShadowSamples + 1.f);
}
frag.color.xyz *= shadow < 0.99 ? clamp(shadow + 0.3, 0.0, 1.0) : shadow;
#endif
return frag;
}

View File

@@ -0,0 +1,126 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "PowerScaling/powerScaling_fs.hglsl"
#include "fragment.glsl"
in vec2 vs_st;
in float vs_screenSpaceDepth;
in vec4 shadowCoords;
uniform sampler2DShadow shadowMapTexture;
uniform sampler1D ringTexture;
uniform vec2 textureOffset;
uniform float transparency;
uniform vec3 sunPosition;
uniform float _nightFactor;
uniform int nShadowSamples;
uniform float zFightingPercentage;
// temp
in vec4 fragPosInLightSpace;
Fragment getFragment() {
// Moving the origin to the center
vec2 st = (vs_st - vec2(0.5)) * 2.0;
// The length of the texture coordinates vector is our distance from the center
float radius = length(st);
// We only want to consider ring-like objects so we need to discard everything else
if (radius > 1.0) {
discard;
}
// Remapping the texture coordinates
// Radius \in [0,1], texCoord \in [textureOffset.x, textureOffset.y]
// textureOffset.x -> 0
// textureOffset.y -> 1
float texCoord = (radius - textureOffset.x) / (textureOffset.y - textureOffset.x);
if (texCoord < 0.f || texCoord > 1.f) {
discard;
}
vec4 diffuse = texture(ringTexture, texCoord);
float colorValue = length(diffuse.rgb);
// times 3 as length of vec3(1.0, 1.0, 1.0) will return 3 and we want
// to normalize the transparency value to [0,1]
if (colorValue < 3.0 * transparency) {
diffuse.a = pow(colorValue / (3.0 * transparency), 1);
//diffuse.a = (colorValue / 3.0) * transparency;
if (diffuse.a < 0.65)
discard;
}
// shadow == 1.0 means it is not in shadow
float shadow = 1.0;
if ( shadowCoords.z >= 0 ) {
vec4 normalizedShadowCoords = shadowCoords;
normalizedShadowCoords.z = normalizeFloat(zFightingPercentage * normalizedShadowCoords.w);
normalizedShadowCoords.xy = normalizedShadowCoords.xy / normalizedShadowCoords.w;
normalizedShadowCoords.w = 1.0;
float sum = 0;
for (int i = 0; i < nShadowSamples; ++i) {
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(-nShadowSamples + i, -nShadowSamples + i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(-nShadowSamples + i, 0));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(-nShadowSamples + i, nShadowSamples - i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( 0 , -nShadowSamples + i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( 0 , nShadowSamples - i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( nShadowSamples - i, -nShadowSamples + i));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( nShadowSamples - i, 0));
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2( nShadowSamples - i, nShadowSamples - i));
}
sum += textureProjOffset(shadowMapTexture, normalizedShadowCoords, ivec2(0, 0));
shadow = sum / (8.0 * nShadowSamples + 1.f);
}
// The normal for the one plane depends on whether we are dealing
// with a front facing or back facing fragment
vec3 normal;
// The plane is oriented on the xz plane
// WARNING: This might not be the case for Uranus
if (gl_FrontFacing) {
normal = vec3(-1.0, 0.0, 0.0);
}
else {
normal = vec3(1.0, 0.0, 0.0);
}
// Reduce the color of the fragment by the user factor
// if we are facing away from the Sun
if (dot(sunPosition, normal) < 0) {
diffuse.xyz *= _nightFactor;
}
Fragment frag;
frag.color = (0.65 * diffuse * shadow) + diffuse * 0.35;
frag.depth = vs_screenSpaceDepth;
frag.gPosition = vec4(1e30, 1e30, 1e30, 1.0);
frag.gNormal = vec4(normal, 1.0);
return frag;
}

View File

@@ -0,0 +1,74 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "PowerScaling/powerScaling_fs.hglsl"
#include "fragment.glsl"
in vec2 vs_st;
in float vs_screenSpaceDepth;
uniform sampler1D ringTexture;
uniform vec2 textureOffset;
Fragment getFragment() {
// Moving the origin to the center
vec2 st = (vs_st - vec2(0.5)) * 2.0;
// The length of the texture coordinates vector is our distance from the center
float radius = length(st);
// We only want to consider ring-like objects so we need to discard everything else
if (radius > 1.0) {
discard;
}
// Remapping the texture coordinates
// Radius \in [0,1], texCoord \in [textureOffset.x, textureOffset.y]
// textureOffset.x -> 0
// textureOffset.y -> 1
float texCoord = (radius - textureOffset.x) / (textureOffset.y - textureOffset.x);
if (texCoord < 0.f || texCoord > 1.f) {
discard;
}
float diffuse = length(texture(ringTexture, texCoord).rgb);
// The normal for the one plane depends on whether we are dealing
// with a front facing or back facing fragment
//vec3 normal;
// The plane is oriented on the xz plane
// WARNING: This might not be the case for Uranus
// if (gl_FrontFacing) {
// normal = vec3(-1.0, 0.0, 0.0);
// }
// else {
// normal = vec3(1.0, 0.0, 0.0);
// }
Fragment frag;
frag.color = vec4(vec3(vs_screenSpaceDepth), 1.0);
frag.depth = (diffuse < 0.5) ? 1E30 : vs_screenSpaceDepth;
return frag;
}

View File

@@ -0,0 +1,47 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#version __CONTEXT__
#include "PowerScaling/powerScalingMath.hglsl"
layout(location = 0) in vec2 in_position;
layout(location = 1) in vec2 in_st;
out vec2 vs_st;
out float vs_screenSpaceDepth;
uniform dmat4 modelViewProjectionMatrix;
void main() {
vs_st = in_st;
dvec4 positionClipSpace = modelViewProjectionMatrix *
dvec4(in_position, 0.0, 1.0);
vec4 positionClipSpaceZNorm = z_normalization(vec4(positionClipSpace));
vs_screenSpaceDepth = positionClipSpaceZNorm.w;
gl_Position = positionClipSpaceZNorm;
}

View File

@@ -0,0 +1,53 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#version __CONTEXT__
#include "PowerScaling/powerScaling_vs.hglsl"
layout(location = 0) in vec2 in_position;
layout(location = 1) in vec2 in_st;
out vec2 vs_st;
out float vs_screenSpaceDepth;
out vec4 vs_positionViewSpace;
out vec4 shadowCoords;
uniform dmat4 modelViewProjectionMatrix;
// ShadowMatrix is the matrix defined by:
// textureCoordsMatrix * projectionMatrix * combinedViewMatrix * modelMatrix
// where textureCoordsMatrix is just a scale and bias computation: [-1,1] to [0,1]
uniform dmat4 shadowMatrix;
void main() {
vs_st = in_st;
dvec4 positionClipSpace = modelViewProjectionMatrix * dvec4(in_position, 0.0, 1.0);
vec4 positionClipSpaceZNorm = z_normalization(vec4(positionClipSpace));
shadowCoords = vec4(shadowMatrix * dvec4(in_position, 0.0, 1.0));
vs_screenSpaceDepth = positionClipSpaceZNorm.w;
gl_Position = positionClipSpaceZNorm;
}

View File

@@ -0,0 +1,40 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "PowerScaling/powerScaling_fs.hglsl"
#include "fragment.glsl"
precision highp float;
in vec2 texCoord;
uniform highp sampler2D shadowMapTexture;
Fragment getFragment() {
Fragment frag;
frag.color = vec4(vec3(1.f) - texture(shadowMapTexture, texCoord).rrr, 1.f);
frag.depth = 0.f;
return frag;
}

View File

@@ -0,0 +1,51 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#version __CONTEXT__
out vec2 texCoord;
const vec3 posData[6] = vec3[] (
vec3(1.0, -0.5, 0.0),
vec3(0.5, -0.5, 0.0),
vec3(0.5, -1.0, 0.0),
vec3(1.0, -1.0, 0.0),
vec3(1.0, -0.5, 0.0),
vec3(0.5, -1.0, 0.0)
);
const vec2 texData[6] = vec2[] (
vec2(1.0, 1.0),
vec2(0.0, 1.0),
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(1.0, 1.0),
vec2(0.0, 0.0)
);
void main() {
texCoord = texData[gl_VertexID];
gl_Position = vec4(posData[gl_VertexID], 1.0);
}

View File

@@ -64,6 +64,7 @@
#define SHOW_CHUNK_EDGES #{showChunkEdges}
#define SHOW_HEIGHT_RESOLUTION #{showHeightResolution}
#define SHOW_HEIGHT_INTENSITIES #{showHeightIntensities}
#define SHADOW_MAPPING_ENABLED #{enableShadowMapping}
const vec3 DefaultLevelWeights = vec3(1.0, 0.0, 0.0);

View File

@@ -184,6 +184,26 @@ namespace {
"Enables the rendering of eclipse shadows using hard shadows"
};
constexpr openspace::properties::Property::PropertyInfo ShadowMappingInfo = {
"ShadowMapping",
"Shadow Mapping",
"Enables shadow mapping algorithm. Used by renderable rings too."
};
constexpr openspace::properties::Property::PropertyInfo ZFightingPercentageInfo = {
"ZFightingPercentage",
"Z-Fighting Percentage",
"The percentage of the correct distance to the surface being shadowed. "
"Possible values: [0.0, 1.0]"
};
constexpr openspace::properties::Property::PropertyInfo NumberShadowSamplesInfo = {
"NumberShadowSamples",
"Number of Shadow Samples",
"The number of samples used during shadow mapping calculation "
"(Percentage Closer Filtering)."
};
constexpr openspace::properties::Property::PropertyInfo TargetLodScaleFactorInfo = {
"TargetLodScaleFactor",
"Target Level of Detail Scale Factor",
@@ -495,16 +515,22 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
BoolProperty(AccurateNormalsInfo, false),
BoolProperty(EclipseInfo, false),
BoolProperty(EclipseHardShadowsInfo, false),
BoolProperty(ShadowMappingInfo, false),
FloatProperty(ZFightingPercentageInfo, 0.995f, 0.000001f, 1.f),
IntProperty(NumberShadowSamplesInfo, 5, 1, 20),
FloatProperty(TargetLodScaleFactorInfo, 15.f, 1.f, 50.f),
FloatProperty(CurrentLodScaleFactorInfo, 15.f, 1.f, 50.f),
FloatProperty(CameraMinHeightInfo, 100.f, 0.f, 1000.f),
FloatProperty(OrenNayarRoughnessInfo, 0.f, 0.f, 1.f),
IntProperty(NActiveLayersInfo, 0, 0, OpenGLCap.maxTextureUnits() / 3)
})
, _shadowMappingPropertyOwner({ "Shadow Mapping" })
, _debugPropertyOwner({ "Debug" })
, _grid(DefaultSkirtedGridSegments, DefaultSkirtedGridSegments)
, _leftRoot(Chunk(LeftHemisphereIndex))
, _rightRoot(Chunk(RightHemisphereIndex))
, _ringsComponent(dictionary)
, _shadowComponent(dictionary)
{
_generalProperties.currentLodScaleFactor.setReadOnly(true);
@@ -535,6 +561,12 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
addProperty(_generalProperties.useAccurateNormals);
addProperty(_generalProperties.eclipseShadowsEnabled);
addProperty(_generalProperties.eclipseHardShadows);
_shadowMappingPropertyOwner.addProperty(_generalProperties.shadowMapping);
_shadowMappingPropertyOwner.addProperty(_generalProperties.zFightingPercentage);
_shadowMappingPropertyOwner.addProperty(_generalProperties.nShadowSamples);
addPropertySubOwner(_shadowMappingPropertyOwner);
_generalProperties.targetLodScaleFactor.onChange([this]() {
float sf = _generalProperties.targetLodScaleFactor;
_generalProperties.currentLodScaleFactor = sf;
@@ -656,6 +688,24 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
_labelsDictionary = dictionary.value<ghoul::Dictionary>(KeyLabels);
}
// Components
if (dictionary.hasKey("Rings")) {
_ringsComponent.initialize();
addPropertySubOwner(_ringsComponent);
_hasRings = true;
ghoul::Dictionary ringsDic;
dictionary.getValue("Rings", ringsDic);
}
if (dictionary.hasKey("Shadows")) {
_shadowComponent.initialize();
addPropertySubOwner(_shadowComponent);
_hasShadows = true;
_generalProperties.shadowMapping = true;
}
_generalProperties.shadowMapping.onChange(notifyShaderRecompilation);
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION
_module = global::moduleEngine.module<GlobeBrowsingModule>();
#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION
@@ -670,6 +720,15 @@ void RenderableGlobe::initializeGL() {
_layerManager.update();
_grid.initializeGL();
if (_hasRings) {
_ringsComponent.initializeGL();
}
if (_hasShadows) {
_shadowComponent.initializeGL();
}
// Recompile the shaders directly so that it is not done the first time the render
// function is called.
recompileShaders();
@@ -691,6 +750,14 @@ void RenderableGlobe::deinitializeGL() {
}
_grid.deinitializeGL();
if (_hasRings) {
_ringsComponent.deinitializeGL();
}
if (_hasShadows) {
_shadowComponent.deinitializeGL();
}
}
bool RenderableGlobe::isReady() const {
@@ -713,8 +780,44 @@ void RenderableGlobe::render(const RenderData& data, RendererTasks& rendererTask
if (distanceToCamera < distance) {
try {
renderChunks(data, rendererTask);
_globeLabelsComponent.draw(data);
// Before Shadows
//renderChunks(data, rendererTask);
//_globeLabelsComponent.draw(data);
if (_hasShadows && _shadowComponent.isEnabled()) {
// Set matrices and other GL states
RenderData lightRenderData(_shadowComponent.begin(data));
glDisable(GL_BLEND);
// Render from light source point of view
renderChunks(lightRenderData, rendererTask, {}, true);
if (_hasRings && _ringsComponent.isEnabled()) {
_ringsComponent.draw(lightRenderData, RingsComponent::GeometryOnly);
}
glEnable(GL_BLEND);
_shadowComponent.setViewDepthMap(false);
_shadowComponent.end();
// Render again from original point of view
renderChunks(data, rendererTask, _shadowComponent.shadowMapData());
if (_hasRings && _ringsComponent.isEnabled()) {
_ringsComponent.draw(
data,
RingsComponent::GeometryAndShading,
_shadowComponent.shadowMapData()
);
}
}
else {
renderChunks(data, rendererTask);
if (_hasRings && _ringsComponent.isEnabled()) {
_ringsComponent.draw(data, RingsComponent::GeometryAndShading);
}
}
}
catch (const ghoul::opengl::TextureUnit::TextureUnitError&) {
std::string layer = _lastChangedLayer ? _lastChangedLayer->guiName() : "";
@@ -807,6 +910,15 @@ void RenderableGlobe::update(const UpdateData& data) {
_layerManager.reset();
_debugProperties.resetTileProviders = false;
}
if (_hasRings) {
_ringsComponent.update(data);
}
if (_hasShadows) {
_shadowComponent.update(data);
}
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION
_nUploadedTiles = _layerManager.update();
#else
@@ -852,7 +964,10 @@ const glm::dmat4& RenderableGlobe::modelTransform() const {
// Rendering code
//////////////////////////////////////////////////////////////////////////////////////////
void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) {
void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&,
const ShadowComponent::ShadowMapData& shadowData,
bool renderGeomOnly)
{
if (_shadersNeedRecompilation) {
recompileShaders();
}
@@ -1086,7 +1201,7 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) {
// Render all chunks that want to be rendered globally
_globalRenderer.program->activate();
for (int i = 0; i < std::min(globalCount, ChunkBufferSize); ++i) {
renderChunkGlobally(*global[i], data);
renderChunkGlobally(*global[i], data, shadowData, renderGeomOnly);
}
_globalRenderer.program->deactivate();
@@ -1094,7 +1209,7 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) {
// Render all chunks that need to be rendered locally
_localRenderer.program->activate();
for (int i = 0; i < std::min(localCount, ChunkBufferSize); ++i) {
renderChunkLocally(*local[i], data);
renderChunkLocally(*local[i], data, shadowData, renderGeomOnly);
}
_localRenderer.program->deactivate();
@@ -1151,7 +1266,10 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) {
}
}
void RenderableGlobe::renderChunkGlobally(const Chunk& chunk, const RenderData& data) {
void RenderableGlobe::renderChunkGlobally(const Chunk& chunk, const RenderData& data,
const ShadowComponent::ShadowMapData& shadowData,
bool renderGeomOnly)
{
//PerfMeasure("globally");
const TileIndex& tileIndex = chunk.tileIndex;
ghoul::opengl::ProgramObject& program = *_globalRenderer.program;
@@ -1198,17 +1316,43 @@ void RenderableGlobe::renderChunkGlobally(const Chunk& chunk, const RenderData&
calculateEclipseShadows(program, data, ShadowCompType::GLOBAL_SHADOW);
}
// Shadow Mapping
ghoul::opengl::TextureUnit shadowMapUnit;
if (_generalProperties.shadowMapping && shadowData.shadowDepthTexture != 0) {
// Adding the model transformation to the final shadow matrix so we have a
// complete transformation from the model coordinates to the clip space of
// the light position.
program.setUniform(
"shadowMatrix",
shadowData.shadowMatrix * modelTransform()
);
shadowMapUnit.activate();
glBindTexture(GL_TEXTURE_2D, shadowData.shadowDepthTexture);
program.setUniform("shadowMapTexture", shadowMapUnit);
program.setUniform("nShadowSamples", _generalProperties.nShadowSamples);
program.setUniform("zFightingPercentage", _generalProperties.zFightingPercentage);
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
if (!renderGeomOnly) {
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
_grid.drawUsingActiveProgram();
for (GPULayerGroup& l : _globalRenderer.gpuLayerGroups) {
l.deactivate();
}
}
void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& data) {
void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& data,
const ShadowComponent::ShadowMapData& shadowData,
bool renderGeomOnly)
{
//PerfMeasure("locally");
const TileIndex& tileIndex = chunk.tileIndex;
ghoul::opengl::ProgramObject& program = *_localRenderer.program;
@@ -1300,10 +1444,31 @@ void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& d
calculateEclipseShadows(program, data, ShadowCompType::LOCAL_SHADOW);
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// Shadow Mapping
ghoul::opengl::TextureUnit shadowMapUnit;
if (_generalProperties.shadowMapping && shadowData.shadowDepthTexture != 0) {
// Adding the model transformation to the final shadow matrix so we have a
// complete transformation from the model coordinates to the clip space of
// the light position.
program.setUniform(
"shadowMatrix",
shadowData.shadowMatrix * modelTransform()
);
shadowMapUnit.activate();
glBindTexture(GL_TEXTURE_2D, shadowData.shadowDepthTexture);
program.setUniform("shadowMapTexture", shadowMapUnit);
program.setUniform("nShadowSamples", _generalProperties.nShadowSamples);
program.setUniform("zFightingPercentage", _generalProperties.zFightingPercentage);
}
glEnable(GL_DEPTH_TEST);
if (!renderGeomOnly) {
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
_grid.drawUsingActiveProgram();
for (GPULayerGroup& l : _localRenderer.gpuLayerGroups) {
@@ -1465,6 +1630,10 @@ void RenderableGlobe::recompileShaders() {
"useEclipseHardShadows",
std::to_string(_generalProperties.eclipseHardShadows)
);
pairs.emplace_back(
"enableShadowMapping",
std::to_string(_generalProperties.shadowMapping)
);
pairs.emplace_back("showChunkEdges", std::to_string(_debugProperties.showChunkEdges));
pairs.emplace_back("showHeightResolution",
std::to_string(_debugProperties.showHeightResolution)

View File

@@ -32,6 +32,8 @@
#include <modules/globebrowsing/src/globelabelscomponent.h>
#include <modules/globebrowsing/src/gpulayergroup.h>
#include <modules/globebrowsing/src/layermanager.h>
#include <modules/globebrowsing/src/ringscomponent.h>
#include <modules/globebrowsing/src/shadowcomponent.h>
#include <modules/globebrowsing/src/skirtedgrid.h>
#include <modules/globebrowsing/src/tileindex.h>
#include <openspace/properties/scalar/floatproperty.h>
@@ -127,24 +129,29 @@ private:
properties::BoolProperty showHeightIntensities;
properties::BoolProperty levelByProjectedAreaElseDistance;
properties::BoolProperty resetTileProviders;
properties::IntProperty modelSpaceRenderingCutoffLevel;
properties::IntProperty dynamicLodIterationCount;
properties::IntProperty modelSpaceRenderingCutoffLevel;
properties::IntProperty dynamicLodIterationCount;
} _debugProperties;
struct {
properties::BoolProperty performShading;
properties::BoolProperty useAccurateNormals;
properties::BoolProperty eclipseShadowsEnabled;
properties::BoolProperty eclipseHardShadows;
properties::BoolProperty performShading;
properties::BoolProperty useAccurateNormals;
properties::BoolProperty eclipseShadowsEnabled;
properties::BoolProperty eclipseHardShadows;
properties::BoolProperty shadowMapping;
properties::FloatProperty zFightingPercentage;
properties::IntProperty nShadowSamples;
properties::FloatProperty targetLodScaleFactor;
properties::FloatProperty currentLodScaleFactor;
properties::FloatProperty cameraMinHeight;
properties::FloatProperty orenNayarRoughness;
properties::IntProperty nActiveLayers;
properties::IntProperty nActiveLayers;
} _generalProperties;
properties::PropertyOwner _debugPropertyOwner;
properties::PropertyOwner _shadowMappingPropertyOwner;
/**
* Test if a specific chunk can safely be culled without affecting the rendered
* image.
@@ -181,7 +188,9 @@ private:
*/
float getHeight(const glm::dvec3& position) const;
void renderChunks(const RenderData& data, RendererTasks& rendererTask);
void renderChunks(const RenderData& data, RendererTasks& rendererTask,
const ShadowComponent::ShadowMapData& shadowData = {}, bool renderGeomOnly = false
);
/**
* Chunks can be rendered either globally or locally. Global rendering is performed
@@ -191,7 +200,9 @@ private:
* point precision by doing this which means that the camera too close to a global
* tile will lead to jagging. We only render global chunks for lower chunk levels.
*/
void renderChunkGlobally(const Chunk& chunk, const RenderData& data);
void renderChunkGlobally(const Chunk& chunk, const RenderData& data,
const ShadowComponent::ShadowMapData& shadowData = {}, bool renderGeomOnly = false
);
/**
* Local rendering of chunks are done using linear interpolation in camera space.
@@ -204,7 +215,9 @@ private:
* levels) the better the approximation becomes. This is why we only render local
* chunks for higher chunk levels.
*/
void renderChunkLocally(const Chunk& chunk, const RenderData& data);
void renderChunkLocally(const Chunk& chunk, const RenderData& data,
const ShadowComponent::ShadowMapData& shadowData = {}, bool renderGeomOnly = false
);
void debugRenderChunk(const Chunk& chunk, const glm::dmat4& mvp,
bool renderBounds, bool renderAABB) const;
@@ -275,6 +288,12 @@ private:
size_t _iterationsOfUnavailableData = 0;
Layer* _lastChangedLayer = nullptr;
// Components
RingsComponent _ringsComponent;
ShadowComponent _shadowComponent;
bool _hasRings = false;
bool _hasShadows = false;
// Labels
GlobeLabelsComponent _globeLabelsComponent;
ghoul::Dictionary _labelsDictionary;

View File

@@ -0,0 +1,481 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/src/ringscomponent.h>
#include <modules/globebrowsing/globebrowsingmodule.h>
#include <modules/globebrowsing/src/renderableglobe.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scene.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/font/fontmanager.h>
#include <ghoul/font/fontrenderer.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/io/texture/texturereader.h>
#include <fstream>
#include <cstdlib>
#include <locale>
namespace {
constexpr const std::array<const char*, 10> UniformNames = {
"modelViewProjectionMatrix", "textureOffset", "transparency", "_nightFactor",
"sunPosition", "ringTexture", "shadowMatrix", "shadowMapTexture",
"nShadowSamples", "zFightingPercentage"
};
constexpr const std::array<const char*, 3> GeomUniformNames = {
"modelViewProjectionMatrix", "textureOffset", "ringTexture"
};
constexpr openspace::properties::Property::PropertyInfo TextureInfo = {
"Texture",
"Texture",
"This value is the path to a texture on disk that contains a one-dimensional "
"texture which is used for these rings."
};
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
"Size",
"Size",
"This value specifies the radius of the rings in meter."
};
constexpr openspace::properties::Property::PropertyInfo OffsetInfo = {
"Offset",
"Offset",
"This value is used to limit the width of the rings.Each of the two values is a "
"value between 0 and 1, where 0 is the center of the ring and 1 is the maximum "
"extent at the radius. If this value is, for example {0.5, 1.0}, the ring is "
"only shown between radius/2 and radius. It defaults to {0.0, 1.0}."
};
constexpr openspace::properties::Property::PropertyInfo NightFactorInfo = {
"NightFactor",
"Night Factor",
"This value is a multiplicative factor that is applied to the side of the rings "
"that is facing away from the Sun. If this value is equal to '1', no darkening "
"of the night side occurs."
};
constexpr openspace::properties::Property::PropertyInfo TransparencyInfo = {
"Transparency",
"Transparency",
"This value determines the transparency of part of the rings depending on the "
"color values. For this value v, the transparency is equal to length(color) / v."
};
constexpr openspace::properties::Property::PropertyInfo ZFightingPercentageInfo = {
"ZFightingPercentage",
"Z-Fighting Percentage",
"The percentage of the correct distance to the surface being shadowed. "
"Possible values: [0.0, 1.0]"
};
constexpr openspace::properties::Property::PropertyInfo NumberShadowSamplesInfo = {
"NumberShadowSamples",
"Number of Shadow Samples",
"The number of samples used during shadow mapping calculation "
"(Percentage Closer Filtering)."
};
} // namespace
namespace openspace {
documentation::Documentation RingsComponent::Documentation() {
using namespace documentation;
return {
"Rings Component",
"globebrowsing_rings_component",
{
{
TextureInfo.identifier,
new StringVerifier,
Optional::Yes,
TextureInfo.description
},
{
SizeInfo.identifier,
new DoubleVerifier,
Optional::Yes,
SizeInfo.description
},
{
OffsetInfo.identifier,
new DoubleVector2Verifier,
Optional::Yes,
OffsetInfo.description
},
{
NightFactorInfo.identifier,
new DoubleVerifier,
Optional::Yes,
NightFactorInfo.description
},
{
TransparencyInfo.identifier,
new DoubleVerifier,
Optional::Yes,
TransparencyInfo.description
},
{
ZFightingPercentageInfo.identifier,
new DoubleVerifier,
Optional::Yes,
ZFightingPercentageInfo.description
},
{
NumberShadowSamplesInfo.identifier,
new IntVerifier,
Optional::Yes,
NumberShadowSamplesInfo.description
}
}
};
}
RingsComponent::RingsComponent(const ghoul::Dictionary& dictionary)
: properties::PropertyOwner({ "Rings" })
, _texturePath(TextureInfo)
, _size(SizeInfo, 1.f, 0.f, 1e25f)
, _offset(OffsetInfo, glm::vec2(0.f, 1.f), glm::vec2(0.f), glm::vec2(1.f))
, _nightFactor(NightFactorInfo, 0.33f, 0.f, 1.f)
, _transparency(TransparencyInfo, 0.15f, 0.f, 1.f)
, _enabled({ "Enabled", "Enabled", "Enable/Disable Rings" }, true)
, _zFightingPercentage(ZFightingPercentageInfo, 0.995f, 0.000001f, 1.f)
, _nShadowSamples(NumberShadowSamplesInfo, 2, 1, 20)
, _ringsDictionary(dictionary)
{
using ghoul::filesystem::File;
if (dictionary.hasKey("Rings")) {
// @TODO (abock, 2019-12-16) It would be better to not store the dictionary long
// term and rather extract the values directly here. This would require a bit of
// a rewrite in the RenderableGlobe class to not create the RingsComponent in the
// class-initializer list though
dictionary.getValue("Rings", _ringsDictionary);
}
documentation::testSpecificationAndThrow(
Documentation(),
_ringsDictionary,
"RingsComponent"
);
}
void RingsComponent::initialize() {
using ghoul::filesystem::File;
addProperty(_enabled);
_size = static_cast<float>(_ringsDictionary.value<double>(SizeInfo.identifier));
//setBoundingSphere(_size);
_size.onChange([&]() { _planeIsDirty = true; });
addProperty(_size);
_texturePath = absPath(
_ringsDictionary.value<std::string>(TextureInfo.identifier)
);
_textureFile = std::make_unique<File>(_texturePath);
if (_ringsDictionary.hasKeyAndValue<glm::vec2>(OffsetInfo.identifier)) {
_offset = _ringsDictionary.value<glm::vec2>(OffsetInfo.identifier);
}
addProperty(_offset);
_texturePath.onChange([&]() { loadTexture(); });
addProperty(_texturePath);
_textureFile->setCallback([&](const File&) { _textureIsDirty = true; });
if (_ringsDictionary.hasKeyAndValue<double>(NightFactorInfo.identifier)) {
_nightFactor = static_cast<float>(
_ringsDictionary.value<double>(NightFactorInfo.identifier)
);
}
addProperty(_nightFactor);
if (_ringsDictionary.hasKeyAndValue<double>(TransparencyInfo.identifier)) {
_transparency = static_cast<float>(
_ringsDictionary.value<double>(TransparencyInfo.identifier)
);
}
// Shadow Mapping Quality Controls
if (_ringsDictionary.hasKey(ZFightingPercentageInfo.identifier)) {
_zFightingPercentage = _ringsDictionary.value<float>(
ZFightingPercentageInfo.identifier
);
}
addProperty(_zFightingPercentage);
if (_ringsDictionary.hasKey(NumberShadowSamplesInfo.identifier)) {
_nShadowSamples = _ringsDictionary.value<int>(NumberShadowSamplesInfo.identifier);
}
addProperty(_nShadowSamples);
addProperty(_transparency);
}
bool RingsComponent::isReady() const {
return (_shader || _geometryOnlyShader) && _texture;
}
void RingsComponent::initializeGL() {
_shader = global::renderEngine.buildRenderProgram(
"RingsProgram",
absPath("${MODULE_GLOBEBROWSING}/shaders/rings_vs.glsl"),
absPath("${MODULE_GLOBEBROWSING}/shaders/rings_fs.glsl")
);
_geometryOnlyShader = global::renderEngine.buildRenderProgram(
"RingsGeomOnlyProgram",
absPath("${MODULE_GLOBEBROWSING}/shaders/rings_geom_vs.glsl"),
absPath("${MODULE_GLOBEBROWSING}/shaders/rings_geom_fs.glsl")
);
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
ghoul::opengl::updateUniformLocations(
*_geometryOnlyShader,
_geomUniformCache,
GeomUniformNames
);
glGenVertexArrays(1, &_quad);
glGenBuffers(1, &_vertexPositionBuffer);
createPlane();
loadTexture();
}
void RingsComponent::deinitializeGL() {
glDeleteVertexArrays(1, &_quad);
_quad = 0;
glDeleteBuffers(1, &_vertexPositionBuffer);
_vertexPositionBuffer = 0;
_textureFile = nullptr;
_texture = nullptr;
global::renderEngine.removeRenderProgram(_shader.get());
_shader = nullptr;
global::renderEngine.removeRenderProgram(_geometryOnlyShader.get());
_geometryOnlyShader = nullptr;
}
void RingsComponent::draw(const RenderData& data,
const RingsComponent::RenderPass renderPass,
const ShadowComponent::ShadowMapData& shadowData)
{
if (renderPass == GeometryAndShading) {
_shader->activate();
}
else if (renderPass == GeometryOnly) {
_geometryOnlyShader->activate();
}
const glm::dmat4 modelTransform =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
glm::dmat4(data.modelTransform.rotation) *
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
const glm::dmat4 modelViewProjectionTransform =
glm::dmat4(data.camera.projectionMatrix()) * data.camera.combinedViewMatrix()
* modelTransform;
ghoul::opengl::TextureUnit ringTextureUnit;
if (renderPass == GeometryAndShading) {
_shader->setUniform(
_uniformCache.modelViewProjectionMatrix,
modelViewProjectionTransform
);
_shader->setUniform(_uniformCache.textureOffset, _offset);
_shader->setUniform(_uniformCache.transparency, _transparency);
_shader->setUniform(_uniformCache.nightFactor, _nightFactor);
_shader->setUniform(_uniformCache.sunPosition, _sunPosition);
_shader->setUniform(_uniformCache.nShadowSamples, _nShadowSamples);
_shader->setUniform(_uniformCache.zFightingPercentage, _zFightingPercentage);
ringTextureUnit.activate();
_texture->bind();
_shader->setUniform(_uniformCache.ringTexture, ringTextureUnit);
// Adding the model transformation to the final shadow matrix so we have a
// complete transformation from the model coordinates to the clip space of
// the light position.
_shader->setUniform(
_uniformCache.shadowMatrix,
shadowData.shadowMatrix * modelTransform
);
ghoul::opengl::TextureUnit shadowMapUnit;
shadowMapUnit.activate();
glBindTexture(GL_TEXTURE_2D, shadowData.shadowDepthTexture);
_shader->setUniform(_uniformCache.shadowMapTexture, shadowMapUnit);
}
else if (renderPass == GeometryOnly) {
_geometryOnlyShader->setUniform(
_geomUniformCache.modelViewProjectionMatrix,
modelViewProjectionTransform
);
_geometryOnlyShader->setUniform(_geomUniformCache.textureOffset, _offset);
ringTextureUnit.activate();
_texture->bind();
_shader->setUniform(_geomUniformCache.ringTexture, ringTextureUnit);
}
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glBindVertexArray(_quad);
glDrawArrays(GL_TRIANGLES, 0, 6);
glEnable(GL_CULL_FACE);
if (renderPass == GeometryAndShading) {
_shader->deactivate();
}
else if (renderPass == GeometryOnly) {
_geometryOnlyShader->deactivate();
}
}
void RingsComponent::update(const UpdateData& data) {
if (_shader->isDirty()) {
_shader->rebuildFromFile();
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
}
if (_geometryOnlyShader->isDirty()) {
_geometryOnlyShader->rebuildFromFile();
ghoul::opengl::updateUniformLocations(
*_geometryOnlyShader,
_geomUniformCache,
GeomUniformNames
);
}
if (_planeIsDirty) {
createPlane();
_planeIsDirty = false;
}
if (_textureIsDirty) {
loadTexture();
_textureIsDirty = false;
}
_sunPosition = glm::normalize(
global::renderEngine.scene()->sceneGraphNode("Sun")->worldPosition() -
data.modelTransform.translation
);
}
void RingsComponent::loadTexture() {
if (!_texturePath.value().empty()) {
using namespace ghoul::io;
using namespace ghoul::opengl;
std::unique_ptr<Texture> texture = TextureReader::ref().loadTexture(
absPath(_texturePath)
);
if (texture) {
LDEBUGC(
"RingsComponent",
fmt::format("Loaded texture from '{}'", absPath(_texturePath))
);
_texture = std::move(texture);
_texture->uploadTexture();
_texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath);
_textureFile->setCallback(
[&](const ghoul::filesystem::File&) { _textureIsDirty = true; }
);
}
}
}
void RingsComponent::createPlane() {
const GLfloat size = _size;
struct VertexData {
GLfloat x;
GLfloat y;
GLfloat s;
GLfloat t;
};
VertexData data[] = {
{ -size, -size, 0.f, 0.f },
{ size, size, 1.f, 1.f },
{ -size, size, 0.f, 1.f },
{ -size, -size, 0.f, 0.f },
{ size, -size, 1.f, 0.f },
{ size, size, 1.f, 1.f },
};
glBindVertexArray(_quad);
glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0,
2,
GL_FLOAT,
GL_FALSE,
sizeof(VertexData),
nullptr
);
glEnableVertexAttribArray(1);
glVertexAttribPointer(
1,
2,
GL_FLOAT,
GL_FALSE,
sizeof(VertexData),
reinterpret_cast<void*>(offsetof(VertexData, s)) // NOLINT
);
}
bool RingsComponent::isEnabled() const {
return _enabled;
}
} // namespace openspace

View File

@@ -0,0 +1,112 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___RINGSCOMPONENT___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___RINGSCOMPONENT___H__
#include <openspace/properties/propertyowner.h>
#include <modules/globebrowsing/src/shadowcomponent.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/vector/vec2property.h>
#include <openspace/properties/vector/vec4property.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/uniformcache.h>
namespace ghoul { class Dictionary; }
namespace ghoul::filesystem { class File; }
namespace ghoul::opengl { class ProgramObject; }
namespace openspace {
struct RenderData;
struct UpdateData;
namespace documentation { struct Documentation; }
class RingsComponent : public properties::PropertyOwner {
public:
enum RenderPass {
GeometryOnly,
GeometryAndShading
};
RingsComponent(const ghoul::Dictionary& dictionary);
void initialize();
void initializeGL();
void deinitializeGL();
bool isReady() const;
void draw(
const RenderData& data,
const RingsComponent::RenderPass renderPass,
const ShadowComponent::ShadowMapData& shadowData = {}
);
void update(const UpdateData& data);
static documentation::Documentation Documentation();
bool isEnabled() const;
private:
void loadTexture();
void createPlane();
properties::StringProperty _texturePath;
properties::FloatProperty _size;
properties::Vec2Property _offset;
properties::FloatProperty _nightFactor;
properties::FloatProperty _transparency;
properties::BoolProperty _enabled;
properties::FloatProperty _zFightingPercentage;
properties::IntProperty _nShadowSamples;
std::unique_ptr<ghoul::opengl::ProgramObject> _shader;
std::unique_ptr<ghoul::opengl::ProgramObject> _geometryOnlyShader;
UniformCache(modelViewProjectionMatrix, textureOffset, transparency, nightFactor,
sunPosition, ringTexture, shadowMatrix, shadowMapTexture, nShadowSamples,
zFightingPercentage
) _uniformCache;
UniformCache(modelViewProjectionMatrix, textureOffset, ringTexture)
_geomUniformCache;
std::unique_ptr<ghoul::opengl::Texture> _texture;
std::unique_ptr<ghoul::filesystem::File> _textureFile;
ghoul::Dictionary _ringsDictionary;
bool _textureIsDirty = false;
GLuint _quad = 0;
GLuint _vertexPositionBuffer = 0;
bool _planeIsDirty = false;
glm::vec3 _sunPosition;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___RINGSCOMPONENT___H__

View File

@@ -0,0 +1,652 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/src/shadowcomponent.h>
#include <modules/globebrowsing/globebrowsingmodule.h>
#include <modules/globebrowsing/src/renderableglobe.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/interaction/keyframenavigator.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scene.h>
#include <openspace/util/camera.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/font/fontmanager.h>
#include <ghoul/font/fontrenderer.h>
#include <glm/gtc/matrix_transform.hpp>
#include <fstream>
#include <cstdlib>
#include <locale>
namespace {
constexpr const char* _loggerCat = "ShadowComponent";
constexpr openspace::properties::Property::PropertyInfo SaveDepthTextureInfo = {
"SaveDepthTextureInfo",
"Save Depth Texture",
"Debug"
};
constexpr openspace::properties::Property::PropertyInfo DistanceFractionInfo = {
"DistanceFraction",
"Distance Fraction",
"Distance fraction of original distance from light source to the globe to be "
"considered as the new light source distance."
};
constexpr openspace::properties::Property::PropertyInfo DepthMapSizeInfo = {
"DepthMapSize",
"Depth Map Size",
"The depth map size in pixels. You must entry the width and height values."
};
constexpr const GLfloat ShadowBorder[] = { 1.f, 1.f, 1.f, 1.f };
void checkFrameBufferState(const std::string& codePosition) {
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LERROR("Framework not built. " + codePosition);
GLenum fbErr = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch (fbErr) {
case GL_FRAMEBUFFER_UNDEFINED:
LERROR("Indefined framebuffer.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
LERROR("Incomplete, missing attachement.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
LERROR("Framebuffer doesn't have at least one image attached to it.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
LERROR(
"Returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is "
"GL_NONE for any color attachment point(s) named by GL_DRAW_BUFFERi."
);
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
LERROR(
"Returned if GL_READ_BUFFER is not GL_NONE and the value of "
"GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color "
"attachment point named by GL_READ_BUFFER.");
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
LERROR(
"Returned if the combination of internal formats of the attached "
"images violates an implementation - dependent set of restrictions."
);
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
LERROR(
"Returned if the value of GL_RENDERBUFFE_r_samples is not the same "
"for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES "
"is the not same for all attached textures; or , if the attached "
"images are a mix of renderbuffers and textures, the value of "
"GL_RENDERBUFFE_r_samples does not match the value of "
"GL_TEXTURE_SAMPLES."
);
LERROR(
"Returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not "
"the same for all attached textures; or , if the attached images are "
"a mix of renderbuffers and textures, the value of "
"GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached "
"textures."
);
break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
LERROR(
"Returned if any framebuffer attachment is layered, and any "
"populated attachment is not layered, or if all populated color "
"attachments are not from textures of the same target."
);
break;
default:
LDEBUG("No error found checking framebuffer: " + codePosition);
break;
}
}
}
} // namespace
namespace openspace {
documentation::Documentation ShadowComponent::Documentation() {
using namespace documentation;
return {
"ShadowsRing Component",
"globebrowsing_shadows_component",
{
{
DistanceFractionInfo.identifier,
new DoubleVerifier,
Optional::Yes,
DistanceFractionInfo.description
},
{
DepthMapSizeInfo.identifier,
new Vector2ListVerifier<float>,
Optional::Yes,
DepthMapSizeInfo.description
}
}
};
}
ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary)
: properties::PropertyOwner({ "Shadows Component" })
, _saveDepthTexture(SaveDepthTextureInfo)
, _distanceFraction(DistanceFractionInfo, 20, 1, 10000)
, _enabled({ "Enabled", "Enabled", "Enable/Disable Shadows" }, true)
, _shadowMapDictionary(dictionary)
{
using ghoul::filesystem::File;
if (dictionary.hasKey("Shadows")) {
// @TODO (abock, 2019-12-16) It would be better to not store the dictionary long
// term and rather extract the values directly here. This would require a bit of
// a rewrite in the RenderableGlobe class to not create the ShadowComponent in the
// class-initializer list though
dictionary.getValue("Shadows", _shadowMapDictionary);
}
documentation::testSpecificationAndThrow(
Documentation(),
_shadowMapDictionary,
"ShadowComponent"
);
if (_shadowMapDictionary.hasKey(DistanceFractionInfo.identifier)) {
_distanceFraction = static_cast<int>(
_shadowMapDictionary.value<float>(DistanceFractionInfo.identifier)
);
}
_saveDepthTexture.onChange([&]() { _executeDepthTextureSave = true; });
if (_shadowMapDictionary.hasKey(DepthMapSizeInfo.identifier)) {
glm::vec2 depthMapSize =
_shadowMapDictionary.value<glm::vec2>(DepthMapSizeInfo.identifier);
_shadowDepthTextureWidth = depthMapSize.x;
_shadowDepthTextureHeight = depthMapSize.y;
_dynamicDepthTextureRes = false;
}
else {
glm::ivec2 renderingResolution = global::renderEngine.renderingResolution();
_shadowDepthTextureWidth = renderingResolution.x * 2;
_shadowDepthTextureHeight = renderingResolution.y * 2;
_dynamicDepthTextureRes = true;
}
_saveDepthTexture.onChange([&]() { _executeDepthTextureSave = true; });
_viewDepthMap = false;
addProperty(_enabled);
addProperty(_saveDepthTexture);
addProperty(_distanceFraction);
}
void ShadowComponent::initialize() {}
bool ShadowComponent::isReady() const {
return true;
}
void ShadowComponent::initializeGL() {
createDepthTexture();
createShadowFBO();
}
void ShadowComponent::deinitializeGL() {
glDeleteTextures(1, &_shadowDepthTexture);
glDeleteTextures(1, &_positionInLightSpaceTexture);
glDeleteFramebuffers(1, &_shadowFBO);
}
RenderData ShadowComponent::begin(const RenderData& data) {
// ===========================================
// Builds light's ModelViewProjectionMatrix:
// ===========================================
glm::dvec3 diffVector = glm::dvec3(_sunPosition) - data.modelTransform.translation;
double originalLightDistance = glm::length(diffVector);
glm::dvec3 lightDirection = glm::normalize(diffVector);
// Percentage of the original light source distance (to avoid artifacts)
//double multiplier = originalLightDistance *
// (static_cast<double>(_distanceFraction)/1.0E5);
double multiplier = originalLightDistance *
(static_cast<double>(_distanceFraction) / 1E17);
// New light source position
//glm::dvec3 lightPosition = data.modelTransform.translation +
// (lightDirection * multiplier);
glm::dvec3 lightPosition = data.modelTransform.translation +
(diffVector * multiplier);
//// Light Position
//glm::dvec3 lightPosition = glm::dvec3(_sunPosition);
//=============== Manually Created Camera Matrix ===================
//==================================================================
// camera Z
glm::dvec3 cameraZ = lightDirection;
// camera X
glm::dvec3 upVector = glm::dvec3(0.0, 1.0, 0.0);
glm::dvec3 cameraX = glm::normalize(glm::cross(upVector, cameraZ));
// camera Y
glm::dvec3 cameraY = glm::cross(cameraZ, cameraX);
// init 4x4 matrix
glm::dmat4 cameraRotationMatrix(1.0);
double* matrix = glm::value_ptr(cameraRotationMatrix);
matrix[0] = cameraX.x;
matrix[4] = cameraX.y;
matrix[8] = cameraX.z;
matrix[1] = cameraY.x;
matrix[5] = cameraY.y;
matrix[9] = cameraY.z;
matrix[2] = cameraZ.x;
matrix[6] = cameraZ.y;
matrix[10] = cameraZ.z;
// set translation part
// We aren't setting the position here because it is set in
// the camera->setPosition()
//matrix[12] = -glm::dot(cameraX, lightPosition);
//matrix[13] = -glm::dot(cameraY, lightPosition);
//matrix[14] = -glm::dot(cameraZ, lightPosition);
_lightCamera = std::move(std::unique_ptr<Camera>(new Camera(data.camera)));
_lightCamera->setPositionVec3(lightPosition);
_lightCamera->setRotation(glm::dquat(glm::inverse(cameraRotationMatrix)));
//=======================================================================
//=======================================================================
//============= Light Matrix by Camera Matrices Composition =============
//=======================================================================
glm::dmat4 lightProjectionMatrix = glm::dmat4(_lightCamera->projectionMatrix());
// The model transformation missing in the final shadow matrix is add when rendering each
// object (using its transformations provided by the RenderData structure)
_shadowData.shadowMatrix =
_toTextureCoordsMatrix * lightProjectionMatrix *
_lightCamera->combinedViewMatrix();
// Saves current state
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO);
glGetIntegerv(GL_VIEWPORT, _mViewport);
_faceCulling = glIsEnabled(GL_CULL_FACE);
glGetIntegerv(GL_CULL_FACE_MODE, &_faceToCull);
_polygonOffSet = glIsEnabled(GL_POLYGON_OFFSET_FILL);
glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &_polygonOffSetFactor);
glGetFloatv(GL_POLYGON_OFFSET_UNITS, &_polygonOffSetUnits);
glGetFloatv(GL_COLOR_CLEAR_VALUE, _colorClearValue);
glGetFloatv(GL_DEPTH_CLEAR_VALUE, &_depthClearValue);
_depthIsEnabled = glIsEnabled(GL_DEPTH_TEST);
glGetIntegerv(GL_DEPTH_FUNC, &_depthFunction);
_blendIsEnabled = glIsEnabled(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, _shadowFBO);
GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE };
glDrawBuffers(3, drawBuffers);
glViewport(0, 0, _shadowDepthTextureWidth, _shadowDepthTextureHeight);
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glEnable(GL_CULL_FACE);
//checkGLError("begin() -- enabled cull face");
//glCullFace(GL_FRONT);
//checkGLError("begin() -- set cullface to front");
//glEnable(GL_POLYGON_OFFSET_FILL);
//checkGLError("begin() -- enabled polygon offset fill");
//glPolygonOffset(2.5f, 10.0f);
//checkGLError("begin() -- set values for polygon offset");
RenderData lightRenderData{
*_lightCamera,
data.time,
data.doPerformanceMeasurement,
data.renderBinMask,
data.modelTransform
};
return lightRenderData;
}
void ShadowComponent::end() {
if (_executeDepthTextureSave) {
saveDepthBuffer();
_executeDepthTextureSave = false;
}
// Restores system state
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO);
GLenum drawBuffers[] = {
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2
};
glDrawBuffers(3, drawBuffers);
glViewport(_mViewport[0], _mViewport[1], _mViewport[2], _mViewport[3]);
if (_faceCulling) {
glEnable(GL_CULL_FACE);
glCullFace(_faceToCull);
}
else {
glDisable(GL_CULL_FACE);
}
if (_depthIsEnabled) {
glEnable(GL_DEPTH_TEST);
}
else {
glDisable(GL_DEPTH_TEST);
}
glDepthFunc(_depthFunction);
if (_polygonOffSet) {
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(_polygonOffSetFactor, _polygonOffSetUnits);
}
else {
glDisable(GL_POLYGON_OFFSET_FILL);
}
glClearColor(
_colorClearValue[0],
_colorClearValue[1],
_colorClearValue[2],
_colorClearValue[3]
);
glClearDepth(_depthClearValue);
if (_blendIsEnabled) {
glEnable(GL_BLEND);
}
if (_viewDepthMap) {
if (!_renderDMProgram) {
_renderDMProgram = global::renderEngine.buildRenderProgram(
"ShadowMappingDebuggingProgram",
absPath("${MODULE_GLOBEBROWSING}/shaders/smviewer_vs.glsl"),
absPath("${MODULE_GLOBEBROWSING}/shaders/smviewer_fs.glsl")
);
}
if (!_quadVAO) {
glGenVertexArrays(1, &_quadVAO);
}
_renderDMProgram->activate();
ghoul::opengl::TextureUnit shadowMapUnit;
shadowMapUnit.activate();
glBindTexture(GL_TEXTURE_2D, _shadowDepthTexture);
_renderDMProgram->setUniform("shadowMapTexture", shadowMapUnit);
glBindVertexArray(_quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
_renderDMProgram->deactivate();
}
}
void ShadowComponent::update(const UpdateData&) {
_sunPosition = global::renderEngine.scene()->sceneGraphNode("Sun")->worldPosition();
glm::ivec2 renderingResolution = global::renderEngine.renderingResolution();
if (_dynamicDepthTextureRes && ((_shadowDepthTextureWidth != renderingResolution.x) ||
(_shadowDepthTextureHeight != renderingResolution.y)))
{
_shadowDepthTextureWidth = renderingResolution.x * 2;
_shadowDepthTextureHeight = renderingResolution.y * 2;
updateDepthTexture();
}
}
void ShadowComponent::createDepthTexture() {
glGenTextures(1, &_shadowDepthTexture);
updateDepthTexture();
_shadowData.shadowDepthTexture = _shadowDepthTexture;
//_shadowData.positionInLightSpaceTexture = _positionInLightSpaceTexture;
}
void ShadowComponent::createShadowFBO() {
// Saves current FBO first
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO);
glGenFramebuffers(1, &_shadowFBO);
glBindFramebuffer(GL_FRAMEBUFFER, _shadowFBO);
glFramebufferTexture(
GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
_shadowDepthTexture,
0
);
//glFramebufferTexture(
// GL_FRAMEBUFFER,
// GL_COLOR_ATTACHMENT0,
// _positionInLightSpaceTexture,
// 0
//);
//GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE };
GLenum drawBuffers[] = { GL_NONE, GL_NONE, GL_NONE };
glDrawBuffers(3, drawBuffers);
checkFrameBufferState("createShadowFBO()");
// Restores system state
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO);
}
void ShadowComponent::updateDepthTexture() {
glBindTexture(GL_TEXTURE_2D, _shadowDepthTexture);
//glTexStorage2D(
// GL_TEXTURE_2D,
// 1,
// GL_DEPTH_COMPONENT32F,
// _shadowDepthTextureWidth,
// _shadowDepthTextureHeight
//);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_DEPTH_COMPONENT32F,
_shadowDepthTextureWidth,
_shadowDepthTextureHeight,
0,
GL_DEPTH_COMPONENT,
GL_FLOAT,
0
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, ShadowBorder);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
//glGenTextures(1, &_positionInLightSpaceTexture);
//glBindTexture(GL_TEXTURE_2D, _positionInLightSpaceTexture);
//glTexImage2D(
// GL_TEXTURE_2D,
// 0,
// GL_RGB32F,
// _shadowDepthTextureWidth,
// _shadowDepthTextureHeight,
// 0,
// GL_RGBA,
// GL_FLOAT,
// 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);
}
void ShadowComponent::saveDepthBuffer() {
int size = _shadowDepthTextureWidth * _shadowDepthTextureHeight;
std::vector<GLubyte> buffer(size);
glReadPixels(
0,
0,
_shadowDepthTextureWidth,
_shadowDepthTextureHeight,
GL_DEPTH_COMPONENT,
GL_UNSIGNED_BYTE,
buffer.data()
);
std::fstream ppmFile;
ppmFile.open("depthBufferShadowMapping.ppm", std::fstream::out);
if (ppmFile.is_open()) {
ppmFile << "P3" << std::endl;
ppmFile << _shadowDepthTextureWidth << " " << _shadowDepthTextureHeight
<< std::endl;
ppmFile << "255" << std::endl;
std::cout << "\n\nSaving depth texture to file depthBufferShadowMapping.ppm\n\n";
int k = 0;
for (int i = 0; i < _shadowDepthTextureWidth; i++) {
for (int j = 0; j < _shadowDepthTextureHeight; j++, k++) {
unsigned int val = static_cast<unsigned int>(buffer[k]);
ppmFile << val << " " << val << " " << val << " ";
}
ppmFile << std::endl;
}
ppmFile.close();
std::cout << "Texture saved to file depthBufferShadowMapping.ppm\n\n";
}
buffer.clear();
std::vector<GLfloat> bBuffer(size * 4);
glReadBuffer(GL_COLOR_ATTACHMENT3);
glReadPixels(
0,
0,
_shadowDepthTextureWidth,
_shadowDepthTextureHeight,
GL_RGBA,
GL_FLOAT,
bBuffer.data()
);
ppmFile.clear();
ppmFile.open("positionBufferShadowMapping.ppm", std::fstream::out);
if (ppmFile.is_open()) {
ppmFile << "P3" << std::endl;
ppmFile << _shadowDepthTextureWidth << " " << _shadowDepthTextureHeight
<< std::endl;
ppmFile << "255" << std::endl;
std::cout << "\n\nSaving texture position to positionBufferShadowMapping.ppm\n\n";
float biggestValue = 0.f;
int k = 0;
for (int i = 0; i < _shadowDepthTextureWidth; i++) {
for (int j = 0; j < _shadowDepthTextureHeight; j++) {
biggestValue = bBuffer[k] > biggestValue ?
bBuffer[k] : biggestValue;
k += 4;
}
}
biggestValue /= 255.f;
k = 0;
for (int i = 0; i < _shadowDepthTextureWidth; i++) {
for (int j = 0; j < _shadowDepthTextureHeight; j++) {
ppmFile << static_cast<unsigned int>(bBuffer[k] / biggestValue) << " "
<< static_cast<unsigned int>(bBuffer[k + 1] / biggestValue) << " "
<< static_cast<unsigned int>(bBuffer[k + 2] / biggestValue) << " ";
k += 4;
}
ppmFile << std::endl;
}
ppmFile.close();
LINFO("Texture saved to file positionBufferShadowMapping.ppm");
}
}
bool ShadowComponent::isEnabled() const {
return _enabled;
}
ShadowComponent::ShadowMapData ShadowComponent::shadowMapData() const {
return _shadowData;
}
void ShadowComponent::setViewDepthMap(bool enable) {
_viewDepthMap = enable;
}
} // namespace openspace

View File

@@ -0,0 +1,154 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___SHADOWCOMPONENT___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___SHADOWCOMPONENT___H__
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/triggerproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/vector/vec2property.h>
#include <openspace/properties/vector/vec4property.h>
#include <openspace/util/camera.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/uniformcache.h>
#include <string>
#include <sstream>
namespace ghoul { class Dictionary; }
namespace ghoul::filesystem { class File; }
namespace ghoul::opengl { class ProgramObject; }
namespace openspace {
struct RenderData;
struct UpdateData;
namespace documentation { struct Documentation; }
class ShadowComponent : public properties::PropertyOwner {
public:
struct ShadowMapData {
glm::dmat4 shadowMatrix;
GLuint shadowDepthTexture;
};
ShadowComponent(const ghoul::Dictionary& dictionary);
void initialize();
void initializeGL();
void deinitializeGL();
//bool deinitialize();
bool isReady() const;
RenderData begin(const RenderData& data);
void end();
void update(const UpdateData& data);
static documentation::Documentation Documentation();
bool isEnabled() const;
ShadowComponent::ShadowMapData shadowMapData() const;
void setViewDepthMap(bool enable);
private:
void createDepthTexture();
void createShadowFBO();
void updateDepthTexture();
// Debug
void saveDepthBuffer();
ShadowMapData _shadowData;
// Texture coords in [0, 1], while clip coords in [-1, 1]
const glm::dmat4 _toTextureCoordsMatrix = glm::dmat4(
glm::dvec4(0.5, 0.0, 0.0, 0.0),
glm::dvec4(0.0, 0.5, 0.0, 0.0),
glm::dvec4(0.0, 0.0, 1.0, 0.0),
glm::dvec4(0.5, 0.5, 0.0, 1.0)
);
// DEBUG
properties::TriggerProperty _saveDepthTexture;
properties::IntProperty _distanceFraction;
properties::BoolProperty _enabled;
ghoul::Dictionary _shadowMapDictionary;
int _shadowDepthTextureHeight = 4096;
int _shadowDepthTextureWidth = 4096;
bool _dynamicDepthTextureRes = true;
// All of these initializations should probably be 0 since they are GLuints?
GLuint _shadowDepthTexture = -1;
GLuint _positionInLightSpaceTexture = -1;
GLuint _shadowFBO = -1;
GLuint _firstPassSubroutine = -1;
GLuint _secondPassSubroutine = 1;
GLint _defaultFBO = -1;
GLint _mViewport[4];
GLboolean _faceCulling;
GLboolean _polygonOffSet;
GLboolean _depthIsEnabled;
GLboolean _blendIsEnabled = false;
GLenum _faceToCull;
GLenum _depthFunction;
GLfloat _polygonOffSetFactor;
GLfloat _polygonOffSetUnits;
GLfloat _colorClearValue[4];
GLfloat _depthClearValue;
glm::vec3 _sunPosition;
glm::dmat4 _shadowMatrix = glm::dmat4(1.0);
glm::dvec3 _cameraPos = glm::dvec3(0.0);
glm::dvec3 _cameraFocus;
glm::dquat _cameraRotation;
std::stringstream _serializedCamera;
std::unique_ptr<Camera> _lightCamera;
// DEBUG
bool _executeDepthTextureSave = false;
bool _viewDepthMap = false;
std::unique_ptr<ghoul::opengl::ProgramObject> _renderDMProgram;
GLuint _quadVAO = 0u;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___SHADOWCOMPONENT___H__