From 052d1584056bc8a69335fb089c8be263c43a185f Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 12 Dec 2024 12:57:26 +0100 Subject: [PATCH 01/57] :tada: Add new star draw mode --- plugins/csp-stars/gui/stars_settings.html | 8 +++- plugins/csp-stars/src/Plugin.cpp | 10 ++++- plugins/csp-stars/src/Shaders.cpp | 51 ++++++++++++++++------- plugins/csp-stars/src/Stars.cpp | 2 + plugins/csp-stars/src/Stars.hpp | 10 ++++- 5 files changed, 61 insertions(+), 20 deletions(-) diff --git a/plugins/csp-stars/gui/stars_settings.html b/plugins/csp-stars/gui/stars_settings.html index ef7752ec6..6298e68f4 100644 --- a/plugins/csp-stars/gui/stars_settings.html +++ b/plugins/csp-stars/gui/stars_settings.html @@ -59,13 +59,19 @@
+
+
+
diff --git a/plugins/csp-stars/src/Plugin.cpp b/plugins/csp-stars/src/Plugin.cpp index 741d98771..33a42c08c 100644 --- a/plugins/csp-stars/src/Plugin.cpp +++ b/plugins/csp-stars/src/Plugin.cpp @@ -166,9 +166,12 @@ void Plugin::init() { "Enables scaled disc draw mode for the stars.", std::function([this]() { mPluginSettings.mDrawMode = Stars::DrawMode::eScaledDisc; })); mGuiManager->getGui()->registerCallback("stars.setDrawMode5", + "Enables glare disc draw mode for the stars.", + std::function([this]() { mPluginSettings.mDrawMode = Stars::DrawMode::eGlareDisc; })); + mGuiManager->getGui()->registerCallback("stars.setDrawMode6", "Enables sprite draw mode for the stars.", std::function([this]() { mPluginSettings.mDrawMode = Stars::DrawMode::eSprite; })); - mPluginSettings.mDrawMode.connect([this](Stars::DrawMode drawMode) { + mPluginSettings.mDrawMode.connectAndTouch([this](Stars::DrawMode drawMode) { if (drawMode == Stars::DrawMode::ePoint) { mGuiManager->setRadioChecked("stars.setDrawMode0"); } else if (drawMode == Stars::DrawMode::eSmoothPoint) { @@ -179,8 +182,10 @@ void Plugin::init() { mGuiManager->setRadioChecked("stars.setDrawMode3"); } else if (drawMode == Stars::DrawMode::eScaledDisc) { mGuiManager->setRadioChecked("stars.setDrawMode4"); - } else if (drawMode == Stars::DrawMode::eSprite) { + } else if (drawMode == Stars::DrawMode::eGlareDisc) { mGuiManager->setRadioChecked("stars.setDrawMode5"); + } else if (drawMode == Stars::DrawMode::eSprite) { + mGuiManager->setRadioChecked("stars.setDrawMode6"); } }); @@ -218,6 +223,7 @@ void Plugin::deInit() { mGuiManager->getGui()->unregisterCallback("stars.setDrawMode3"); mGuiManager->getGui()->unregisterCallback("stars.setDrawMode4"); mGuiManager->getGui()->unregisterCallback("stars.setDrawMode5"); + mGuiManager->getGui()->unregisterCallback("stars.setDrawMode6"); mGuiManager->getGui()->unregisterCallback("stars.setEnabled"); mGuiManager->getGui()->unregisterCallback("stars.setEnableGrid"); mGuiManager->getGui()->unregisterCallback("stars.setEnableFigures"); diff --git a/plugins/csp-stars/src/Shaders.cpp b/plugins/csp-stars/src/Shaders.cpp index 47e006749..53d0c2a4a 100644 --- a/plugins/csp-stars/src/Shaders.cpp +++ b/plugins/csp-stars/src/Shaders.cpp @@ -32,6 +32,10 @@ vec3 SRGBtoLINEAR(vec3 srgbIn) { return mix( srgbIn/vec3(12.92), pow((srgbIn+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); } +float mapRange(float value, float oldMin, float oldMax, float newMin, float newMax) { + return newMin + (clamp(value, oldMin, oldMax) - oldMin) * (newMax - newMin) / (oldMax - oldMin); +} + // http://filmicworlds.com/blog/filmic-tonemapping-operators/ float A = 0.15; float B = 0.50; @@ -116,19 +120,20 @@ void main() { float scale = dist * diameter; #ifdef DRAWMODE_SCALED_DISC - scale *= 1.0 - (iMagnitude - uMinMagnitude) / (uMaxMagnitude - uMinMagnitude); - // We scale the stars up a little in this mode so that they look more similar to // the other modes without the user having to move the star-size slider. - scale *= 3.0; + scale *= mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 3.0, 0.3); #endif - #ifdef DRAWMODE_SPRITE - scale *= 1.0 - (iMagnitude - uMinMagnitude) / (uMaxMagnitude - uMinMagnitude); + #ifdef DRAWMODE_GLARE_DISC + float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); + scale *= mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); + #endif + #ifdef DRAWMODE_SPRITE // We scale the stars up a little in this mode so that they look more similar to // the other modes without the user having to move the star-size slider. - scale *= 10.0; + scale *= mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 10.0, 1.0); #endif for(int j=0; j!=2; ++j) { @@ -175,24 +180,38 @@ void main() { #ifdef DRAWMODE_SMOOTH_DISC // the brightness is basically a cone from above - to achieve the // same total brightness, we have to multiply it with three - float fac = luminance * clamp(1-dist, 0, 1) * 3; + float fac = luminance * clamp(1 - dist, 0, 1) * 3; #endif #ifdef DRAWMODE_SCALED_DISC - float scaleFac = 1.0 - (iMagnitude - uMinMagnitude) / (uMaxMagnitude - uMinMagnitude); + // The stars were scaled in this mode so we have to incorporate this here to + // achieve the same total luminance. + float scaleFac = mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 3.0, 0.3); + float fac = luminance * clamp(1 - dist, 0, 1) * 3 / (scaleFac * scaleFac); + #endif + + #ifdef DRAWMODE_GLARE_DISC + float scaleFac = mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); + + // In this mode, 90% of the brightness is drawn using a small smooth disc in the center + // (just like DRAWMODE_SMOOTH_DISC) and the othe 10% are drawn as glare using an inverse + // quadratic falloff. + + // The billboard is scaled depending on the magnitude, but we want the disc to be the same + // size for all stars. So we have to scale the distance accordingly. + float disc = luminance * clamp(1 - dist * scaleFac, 0, 1) * 3; + + float falloff = max(0, 0.5 / pow(dist+0.1, 2) - 0.5); + float glare = luminance * falloff / (scaleFac * scaleFac) * 0.5; - // The stars were scaled up a little in this mode so we have to incorporate this - // here to achieve the same total luminance. - scaleFac *= 3.0; - float fac = luminance * clamp(1-dist, 0, 1) * 3 / (scaleFac * scaleFac); + float fac = 0.5 * glare + 0.5 * disc; #endif #ifdef DRAWMODE_SPRITE - float scaleFac = 1.0 - (iMagnitude - uMinMagnitude) / (uMaxMagnitude - uMinMagnitude); - // The stars were scaled up a little in this mode so we have to incorporate this - // here to achieve the same total luminance. - scaleFac *= 10.0; + // The stars were scaled in this mode so we have to incorporate this here to achieve the + // same total luminance. + float scaleFac = mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 10.0, 1.0); float fac = texture(iTexture, iTexcoords * 0.5 + 0.5).r * luminance / (scaleFac * scaleFac); // A magic number here. This is the average brightness of the currently used diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index 395c7e27a..9007d62bc 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -348,6 +348,8 @@ bool Stars::Do() { defines += "#define DRAWMODE_SMOOTH_DISC\n"; } else if (mDrawMode == DrawMode::eScaledDisc) { defines += "#define DRAWMODE_SCALED_DISC\n"; + } else if (mDrawMode == DrawMode::eGlareDisc) { + defines += "#define DRAWMODE_GLARE_DISC\n"; } else if (mDrawMode == DrawMode::eSprite) { defines += "#define DRAWMODE_SPRITE\n"; } diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index 838bfecaa..6d812bceb 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -48,7 +48,15 @@ class Stars : public IVistaOpenGLDraw { eCount }; - enum class DrawMode { ePoint, eSmoothPoint, eDisc, eSmoothDisc, eScaledDisc, eSprite }; + enum class DrawMode { + ePoint, + eSmoothPoint, + eDisc, + eSmoothDisc, + eScaledDisc, + eGlareDisc, + eSprite + }; /// It is possible to load multiple catalogs, currently Hipparcos and any of Tycho or Tycho2 can /// be loaded together. Stars which are in both catalogs will be loaded from Hipparcos. Once From 65ae7311640c95f90b78da4be03af2c86401705f Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 12 Dec 2024 13:49:43 +0100 Subject: [PATCH 02/57] :wrench: Load tonemapping shaders from files --- resources/shaders/tonemap.frag | 170 +++++++++++++++++++++++++ resources/shaders/tonemap.vert | 13 ++ src/cs-graphics/ToneMappingNode.cpp | 188 +--------------------------- 3 files changed, 189 insertions(+), 182 deletions(-) create mode 100644 resources/shaders/tonemap.frag create mode 100644 resources/shaders/tonemap.vert diff --git a/resources/shaders/tonemap.frag b/resources/shaders/tonemap.frag new file mode 100644 index 000000000..cd67dc3f3 --- /dev/null +++ b/resources/shaders/tonemap.frag @@ -0,0 +1,170 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +in vec2 vTexcoords; + +layout(pixel_center_integer) in vec4 gl_FragCoord; + +#if NUM_MULTISAMPLES > 0 +layout(binding = 0) uniform sampler2DMS uComposite; +layout(binding = 1) uniform sampler2DMS uDepth; +#else +layout(binding = 0) uniform sampler2D uComposite; +layout(binding = 1) uniform sampler2D uDepth; +#endif + +layout(binding = 2) uniform sampler2D uGlareMipMap; + +uniform float uExposure; +uniform float uGlareIntensity; + +layout(location = 0) out vec3 oColor; + +// http://filmicworlds.com/blog/filmic-tonemapping-operators/ +float A = 0.15; +float B = 0.50; +float C = 0.10; +float D = 0.20; +float E = 0.02; +float F = 0.30; +float W = 11.2; + +vec3 Uncharted2Tonemap(vec3 x) { + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} + +float linear_to_srgb(float c) { + if (c <= 0.0031308) + return 12.92 * c; + else + return 1.055 * pow(c, 1.0 / 2.4) - 0.055; +} + +vec3 linear_to_srgb(vec3 c) { + return vec3(linear_to_srgb(c.r), linear_to_srgb(c.g), linear_to_srgb(c.b)); +} + +// 4x4 bicubic filter using 4 bilinear texture lookups +// See GPU Gems 2: "Fast Third-Order Texture Filtering", Sigg & Hadwiger: +// http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html + +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { + float lod = float(p_lod); + vec2 tex_size = textureSize(uGlareMipMap, p_lod); + vec2 pixel_size = 1.0 / tex_size; + uv = uv * tex_size + 0.5; + vec2 iuv = floor(uv); + vec2 fuv = fract(uv); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; + + return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); +} + +void main() { +#if NUM_MULTISAMPLES > 0 + vec3 color = vec3(0.0); + for (int i = 0; i < NUM_MULTISAMPLES; ++i) { + color += texelFetch(uComposite, ivec2(vTexcoords * textureSize(uComposite)), i).rgb; + } + color /= NUM_MULTISAMPLES; + + float depth = 1.0; + for (int i = 0; i < NUM_MULTISAMPLES; ++i) { + depth = min(depth, texelFetch(uDepth, ivec2(vTexcoords * textureSize(uDepth)), i).r); + } + gl_FragDepth = depth; +#else + vec3 color = texelFetch(uComposite, ivec2(vTexcoords * textureSize(uComposite, 0)), 0).rgb; + gl_FragDepth = texelFetch(uDepth, ivec2(vTexcoords * textureSize(uDepth, 0)), 0).r; +#endif + + if (uGlareIntensity > 0) { + vec3 glare = vec3(0); + float maxLevels = textureQueryLevels(uGlareMipMap); + + float totalWeight = 0; + + // Each level contains a successively more blurred version of the scene. We have to + // accumulate them with an exponentially decreasing weight to get a proper glare distribution. + for (int i = 0; i < maxLevels; ++i) { + float weight = 1.0 / pow(2, i); + +#ifdef BICUBIC_GLARE_FILTER + glare += texture2D_bicubic(uGlareMipMap, vTexcoords, i).rgb * weight; +#else + glare += texture2D(uGlareMipMap, vTexcoords, i).rgb * weight; +#endif + + totalWeight += weight; + } + + // To make sure that we do not add energy, we divide by the total weight. + color = mix(color, glare / totalWeight, pow(uGlareIntensity, 2.0)); + } + +// Filmic +#if TONE_MAPPING_MODE == 2 + color = Uncharted2Tonemap(uExposure * color); + vec3 whiteScale = vec3(1.0) / Uncharted2Tonemap(vec3(W)); + oColor = linear_to_srgb(color * whiteScale); + +// Gamma only +#elif TONE_MAPPING_MODE == 1 + oColor = linear_to_srgb(uExposure * color); + +// None +#else + oColor = uExposure * color; +#endif +} \ No newline at end of file diff --git a/resources/shaders/tonemap.vert b/resources/shaders/tonemap.vert new file mode 100644 index 000000000..f35a53e8f --- /dev/null +++ b/resources/shaders/tonemap.vert @@ -0,0 +1,13 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +out vec2 vTexcoords; + +void main() { + vTexcoords = vec2(gl_VertexID & 2, (gl_VertexID << 1) & 2); + gl_Position = vec4(vTexcoords * 2.0 - 1.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/src/cs-graphics/ToneMappingNode.cpp b/src/cs-graphics/ToneMappingNode.cpp index 8077de7e1..6e2273062 100644 --- a/src/cs-graphics/ToneMappingNode.cpp +++ b/src/cs-graphics/ToneMappingNode.cpp @@ -8,6 +8,7 @@ #include "ToneMappingNode.hpp" #include "../cs-utils/FrameStats.hpp" +#include "../cs-utils/filesystem.hpp" #include "HDRBuffer.hpp" #include @@ -122,186 +123,6 @@ void ApplyProgramAuto( //////////////////////////////////////////////////////////////////////////////////////////////////// -static const char* sVertexShader = R"( - out vec2 vTexcoords; - - void main() - { - vTexcoords = vec2(gl_VertexID & 2, (gl_VertexID << 1) & 2); - gl_Position = vec4(vTexcoords * 2.0 - 1.0, 0.0, 1.0); - } -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -static const char* sFragmentShader = R"( - in vec2 vTexcoords; - - layout(pixel_center_integer) in vec4 gl_FragCoord; - - #if NUM_MULTISAMPLES > 0 - layout (binding = 0) uniform sampler2DMS uComposite; - layout (binding = 1) uniform sampler2DMS uDepth; - #else - layout (binding = 0) uniform sampler2D uComposite; - layout (binding = 1) uniform sampler2D uDepth; - #endif - - layout (binding = 2) uniform sampler2D uGlareMipMap; - - uniform float uExposure; - uniform float uGlareIntensity; - - layout(location = 0) out vec3 oColor; - - // http://filmicworlds.com/blog/filmic-tonemapping-operators/ - float A = 0.15; - float B = 0.50; - float C = 0.10; - float D = 0.20; - float E = 0.02; - float F = 0.30; - float W = 11.2; - - vec3 Uncharted2Tonemap(vec3 x) { - return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; - } - - float linear_to_srgb(float c) { - if(c <= 0.0031308) - return 12.92*c; - else - return 1.055 * pow(c, 1.0/2.4) - 0.055; - } - - vec3 linear_to_srgb(vec3 c) { - return vec3(linear_to_srgb(c.r), linear_to_srgb(c.g), linear_to_srgb(c.b)); - } - - // 4x4 bicubic filter using 4 bilinear texture lookups - // See GPU Gems 2: "Fast Third-Order Texture Filtering", Sigg & Hadwiger: - // http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html - - // w0, w1, w2, and w3 are the four cubic B-spline basis functions - float w0(float a) { - return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); - } - - float w1(float a) { - return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); - } - - float w2(float a) { - return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); - } - - float w3(float a) { - return (1.0 / 6.0) * (a * a * a); - } - - // g0 and g1 are the two amplitude functions - float g0(float a) { - return w0(a) + w1(a); - } - - float g1(float a) { - return w2(a) + w3(a); - } - - // h0 and h1 are the two offset functions - float h0(float a) { - return -1.0 + w1(a) / (w0(a) + w1(a)); - } - - float h1(float a) { - return 1.0 + w3(a) / (w2(a) + w3(a)); - } - - vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { - float lod = float(p_lod); - vec2 tex_size = textureSize(uGlareMipMap, p_lod); - vec2 pixel_size = 1.0 / tex_size; - uv = uv * tex_size + 0.5; - vec2 iuv = floor(uv); - vec2 fuv = fract(uv); - - float g0x = g0(fuv.x); - float g1x = g1(fuv.x); - float h0x = h0(fuv.x); - float h1x = h1(fuv.x); - float h0y = h0(fuv.y); - float h1y = h1(fuv.y); - - vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size; - vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; - - return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + - (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); - } - - void main() { - #if NUM_MULTISAMPLES > 0 - vec3 color = vec3(0.0); - for (int i = 0; i < NUM_MULTISAMPLES; ++i) { - color += texelFetch(uComposite, ivec2(vTexcoords * textureSize(uComposite)), i).rgb; - } - color /= NUM_MULTISAMPLES; - - float depth = 1.0; - for (int i = 0; i < NUM_MULTISAMPLES; ++i) { - depth = min(depth, texelFetch(uDepth, ivec2(vTexcoords * textureSize(uDepth)), i).r); - } - gl_FragDepth = depth; - #else - vec3 color = texelFetch(uComposite, ivec2(vTexcoords * textureSize(uComposite, 0)), 0).rgb; - gl_FragDepth = texelFetch(uDepth, ivec2(vTexcoords * textureSize(uDepth, 0)), 0).r; - #endif - - if (uGlareIntensity > 0) { - vec3 glare = vec3(0); - float maxLevels = textureQueryLevels(uGlareMipMap); - - float totalWeight = 0; - - // Each level contains a successively more blurred version of the scene. We have to - // accumulate them with an exponentially decreasing weight to get a proper glare distribution. - for (int i=0; i hdrBuffer) : mHDRBuffer(std::move(hdrBuffer)) { @@ -479,9 +300,12 @@ bool ToneMappingNode::ToneMappingNode::Do() { defines += "#define TONE_MAPPING_MODE " + std::to_string(static_cast(mToneMappingMode)) + "\n"; + std::string vert(utils::filesystem::loadToString("../share/resources/shaders/tonemap.vert")); + std::string frag(utils::filesystem::loadToString("../share/resources/shaders/tonemap.frag")); + mShader = std::make_unique(); - mShader->InitVertexShaderFromString(defines + sVertexShader); - mShader->InitFragmentShaderFromString(defines + sFragmentShader); + mShader->InitVertexShaderFromString(defines + vert); + mShader->InitFragmentShaderFromString(defines + frag); mShader->Link(); mUniforms.exposure = mShader->GetUniformLocation("uExposure"); From d58c7d877bf36f1676e1f9151e1e0a03ae5f592f Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 12 Dec 2024 13:57:59 +0100 Subject: [PATCH 03/57] :wrench: Load glare shader from file --- resources/shaders/glare.comp | 243 +++++++++++++++++++++++++++++++ src/cs-graphics/GlareMipMap.cpp | 247 +------------------------------- 2 files changed, 245 insertions(+), 245 deletions(-) create mode 100644 resources/shaders/glare.comp diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp new file mode 100644 index 000000000..086d50ccc --- /dev/null +++ b/resources/shaders/glare.comp @@ -0,0 +1,243 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +layout(local_size_x = 16, local_size_y = 16) in; + +#if NUM_MULTISAMPLES > 0 +layout(rgba32f, binding = 0) readonly uniform image2DMS uInHDRBuffer; +#else +layout(rgba32f, binding = 0) readonly uniform image2D uInHDRBuffer; +#endif + +layout(rgba32f, binding = 1) readonly uniform image2D uInGlare; +layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; + +uniform int uPass; +uniform int uLevel; +uniform mat4 uMatP; +uniform mat4 uMatInvP; + +const float PI = 3.14159265359; + +// Makes four texture look-ups in the input HDRBuffer at the four pixels corresponding to +// the pixel position in the base layer of the output mipmap pyramid. +// For performance reasons, we only use one sample for multisample inputs. +vec3 sampleHDRBuffer(ivec2 pos) { +#if NUM_MULTISAMPLES > 0 + vec3 col = imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 0)), 0).rgb * 0.25 + + imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 0)), 0).rgb * 0.25 + + imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 1)), 0).rgb * 0.25 + + imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 1)), 0).rgb * 0.25; +#else + vec3 col = imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 0))).rgb * 0.25 + + imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 0))).rgb * 0.25 + + imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 1))).rgb * 0.25 + + imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 1))).rgb * 0.25; +#endif + return col; +} + +// Calls the method above four times in order to allow for bilinear interpolation. This is +// required for floating point positions and results in sixteen texture look-ups. +vec3 sampleHDRBuffer(vec2 pos) { + ivec2 ipos = ivec2(pos); + vec3 tl = sampleHDRBuffer(ipos); + vec3 tr = sampleHDRBuffer(ipos + ivec2(1, 0)); + vec3 bl = sampleHDRBuffer(ipos + ivec2(0, 1)); + vec3 br = sampleHDRBuffer(ipos + ivec2(1, 1)); + vec2 f = fract(pos); + vec3 tA = mix(tl, tr, f.x); + vec3 tB = mix(bl, br, f.x); + return mix(tA, tB, f.y); +} + +// Makes four texture look-ups in the input layer of the glare mipmap at the four pixels +// corresponding to the pixel position in the current layer of the mipmap pyramid. +vec3 sampleHigherLevel(ivec2 pos) { + vec3 col = imageLoad(uInGlare, ivec2(pos * 2 + ivec2(0, 0))).rgb * 0.25 + + imageLoad(uInGlare, ivec2(pos * 2 + ivec2(1, 0))).rgb * 0.25 + + imageLoad(uInGlare, ivec2(pos * 2 + ivec2(0, 1))).rgb * 0.25 + + imageLoad(uInGlare, ivec2(pos * 2 + ivec2(1, 1))).rgb * 0.25; + return col; +} + +// Calls the method above four times in order to allow for bilinear interpolation. This is +// required for floating point positions and results in sixteen texture look-ups. +vec3 sampleHigherLevel(vec2 pos) { + ivec2 ipos = ivec2(pos); + vec3 tl = sampleHigherLevel(ipos); + vec3 tr = sampleHigherLevel(ipos + ivec2(1, 0)); + vec3 bl = sampleHigherLevel(ipos + ivec2(0, 1)); + vec3 br = sampleHigherLevel(ipos + ivec2(1, 1)); + vec2 f = fract(pos); + vec3 tA = mix(tl, tr, f.x); + vec3 tB = mix(bl, br, f.x); + return mix(tA, tB, f.y); +} + +// Makes just one texture look-ups in the input layer of the glare mipmap at the given +// pixel position. +vec3 sampleSameLevel(ivec2 pos) { + return imageLoad(uInGlare, pos).rgb; +} + +// Calls the method above four times in order to allow for bilinear interpolation. This is +// required for floating point positions and results in four texture look-ups. +vec3 sampleSameLevel(vec2 pos) { + ivec2 ipos = ivec2(pos); + vec3 tl = sampleSameLevel(ipos); + vec3 tr = sampleSameLevel(ipos + ivec2(1, 0)); + vec3 bl = sampleSameLevel(ipos + ivec2(0, 1)); + vec3 br = sampleSameLevel(ipos + ivec2(1, 1)); + vec2 f = fract(pos); + vec3 tA = mix(tl, tr, f.x); + vec3 tB = mix(bl, br, f.x); + return mix(tA, tB, f.y); +} + +// Rotates the given vector around a given axis. +// Based on comment from http://www.neilmendoza.com/glsl-rotation-about-an-arbitrary-axis/ +vec3 rotate(vec3 v, vec3 axis, float angle) { + return mix(dot(axis, v) * axis, v, cos(angle)) + cross(axis, v) * sin(angle); +} + +// Evalutes the normal distribution function for the given value. +float getGauss(float sigma, float value) { + return 1.0 / (sigma * sqrt(2 * PI)) * exp(-0.5 * pow(value / sigma, 2)); +} + +void main() { + + ivec2 pixelPos = ivec2(gl_GlobalInvocationID.xy); + ivec2 outputSize = imageSize(uOutGlare); + + // Discard any threads outside the output layer. + if (pixelPos.x >= outputSize.x || pixelPos.y >= outputSize.y) { + return; + } + + // Take an odd number of samples to make sure that we sample the center value. + const float samples = 2 * (GLARE_QUALITY + 1) + 1; + + // These values will contain the accumulated glare values. + vec3 glare = vec3(0); + float totalWeight = 0; + + // The first glare variant computes a symmetrical gaussian blur in screen space. It is separated + // into a vertical and horizontal component which are computed in different passes. + // If uPass == 0, horizontal blurring happens, if uPass == 1, vertical blurring happens. + // This is not perspectively correct but very fast. + +#ifdef GLAREMODE_SYMMETRIC_GAUSS + for (float i = 0; i < samples; ++i) { + float offset = i - floor(samples / 2); + float weight = getGauss(1, offset); + totalWeight += weight; + + // If we are writing to level zero, we have to sample the input HDR buffer. For all + // successive levels we sample the previous level in the first passes and the same level + // in the second passes. + if (uPass == 0 && uLevel == 0) { + glare += sampleHDRBuffer(pixelPos + ivec2(offset, 0)) * weight; + } else if (uPass == 0) { + glare += sampleHigherLevel(pixelPos + ivec2(offset, 0)) * weight; + } else { + glare += sampleSameLevel(pixelPos + ivec2(0, offset)) * weight; + } + } +#endif + + // The second variant computes an asymmetric perspectively correct gaussian decomposed into two + // components which can be roughly described as radial and circular. A naive implementation with + // a strictly circular and a strictly radial component suffers from undefined behavior close to + // the focal point (usually in the middle of the screen) resulting in weird glow patterns in + // this area: + // + // Primary Component Secondary (orthogonal) Component + // + // \ | / / --- \ + // -- X -- | O | + // / | \ \ --- / + // radial circular + // + // + // Therefore the implementation below uses two components like the following: + // + // \ | / / -- \ + // | | | -- -- -- + // / | \ \ -- / + // vertical / radial horizontal / circular + +#ifdef GLAREMODE_ASYMMETRIC_GAUSS + + // Reproject the current pixel position to view space. + vec2 posClipSpace = 2.0 * vec2(gl_GlobalInvocationID.xy) / vec2(outputSize) - 1.0; + vec4 posViewSpace = uMatInvP * vec4(posClipSpace, 0.0, 1.0); + + // The primary component depicted above is a mixture between a vertical rotation and a radial + // rotation. A vertical rotation would require this axis: + vec3 rotAxisVertical = cross(posViewSpace.xyz, vec3(1, 0, 0)); + rotAxisVertical = cross(posViewSpace.xyz, rotAxisVertical); + + // A radial rotation would be around this axis: + vec3 rotAxisRadial = cross(posViewSpace.xyz, vec3(0, 0, -1)); + if (posViewSpace.y < 0) { + rotAxisRadial = -rotAxisRadial; + } + + // We mix those two axes with a factor which depends on the vertical position of our + // pixel on the screen. The magic factor of two determines how fast we change from one to + // another and is not very sensitive. A value of five would result in very similar images. + float alpha = clamp(2 * abs(posViewSpace.y / length(posViewSpace.xyz)), 0, 1); + vec3 rotAxis = mix(rotAxisVertical, rotAxisRadial, alpha); + + // The primary passes use the axis computed above, the secondary passes use an + // orthogonal rotation axis. + if (uPass == 0) { + rotAxis = normalize(rotAxis); + } else { + rotAxis = normalize(cross(rotAxis, posViewSpace.xyz)); + } + + // The angle covered by the gauss kernel increases quadratically with the mipmap level. + const float totalAngle = pow(2, uLevel) * PI / 180.0; + + // Rotate the view vector to the current pixel several times around the rotation axis + // in order to sample the vicinity. + for (float i = 0; i < samples; ++i) { + float angle = totalAngle * i / (samples - 1) - totalAngle * 0.5; + float sigma = totalAngle / MAX_LEVELS; + float weight = getGauss(sigma, angle); + + // Compute the rotated sample position in screen space. + vec4 pos = uMatP * vec4(rotate(posViewSpace.xyz, rotAxis, angle), 1.0); + pos /= pos.w; + + // Convert to texture space. + vec2 samplePos = (0.5 * pos.xy + 0.5) * outputSize; + + // If we are writing to level zero, we have to sample the input HDR buffer. For all + // successive levels we sample the previous level in the first passes and the same level + // in the second passes. + if (uPass == 0 && uLevel == 0) { + glare += sampleHDRBuffer(samplePos) * weight; + } else if (uPass == 0) { + glare += sampleHigherLevel(samplePos) * weight; + } else { + glare += sampleSameLevel(samplePos) * weight; + } + + totalWeight += weight; + } +#endif + + // Make sure that we do not add energy. + glare /= totalWeight; + + // Finally store the glare value in the output layer of the glare mipmap. + imageStore(uOutGlare, pixelPos, vec4(glare, 0.0)); +} \ No newline at end of file diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 4b7f9ce49..3b6df231f 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -8,6 +8,7 @@ #include "GlareMipMap.hpp" #include "../cs-utils/FrameStats.hpp" +#include "../cs-utils/filesystem.hpp" #include "logger.hpp" #include @@ -21,250 +22,6 @@ namespace cs::graphics { //////////////////////////////////////////////////////////////////////////////////////////////////// -static const char* sGlareShader = R"( - layout (local_size_x = 16, local_size_y = 16) in; - - #if NUM_MULTISAMPLES > 0 - layout (rgba32f, binding = 0) readonly uniform image2DMS uInHDRBuffer; - #else - layout (rgba32f, binding = 0) readonly uniform image2D uInHDRBuffer; - #endif - - layout (rgba32f, binding = 1) readonly uniform image2D uInGlare; - layout (rgba32f, binding = 2) writeonly uniform image2D uOutGlare; - - uniform int uPass; - uniform int uLevel; - uniform mat4 uMatP; - uniform mat4 uMatInvP; - - const float PI = 3.14159265359; - - // Makes four texture look-ups in the input HDRBuffer at the four pixels corresponding to - // the pixel position in the base layer of the output mipmap pyramid. - // For performance reasons, we only use one sample for multisample inputs. - vec3 sampleHDRBuffer(ivec2 pos) { - #if NUM_MULTISAMPLES > 0 - vec3 col = imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(0,0)), 0).rgb * 0.25 - + imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(1,0)), 0).rgb * 0.25 - + imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(0,1)), 0).rgb * 0.25 - + imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(1,1)), 0).rgb * 0.25; - #else - vec3 col = imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(0,0))).rgb * 0.25 - + imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(1,0))).rgb * 0.25 - + imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(0,1))).rgb * 0.25 - + imageLoad(uInHDRBuffer, ivec2(pos*2 + ivec2(1,1))).rgb * 0.25; - #endif - return col; - } - - // Calls the method above four times in order to allow for bilinear interpolation. This is - // required for floating point positions and results in sixteen texture look-ups. - vec3 sampleHDRBuffer(vec2 pos) { - ivec2 ipos = ivec2(pos); - vec3 tl = sampleHDRBuffer(ipos); - vec3 tr = sampleHDRBuffer(ipos + ivec2(1, 0)); - vec3 bl = sampleHDRBuffer(ipos + ivec2(0, 1)); - vec3 br = sampleHDRBuffer(ipos + ivec2(1, 1)); - vec2 f = fract(pos); - vec3 tA = mix(tl, tr, f.x); - vec3 tB = mix(bl, br, f.x); - return mix(tA, tB, f.y); - } - - - // Makes four texture look-ups in the input layer of the glare mipmap at the four pixels - // corresponding to the pixel position in the current layer of the mipmap pyramid. - vec3 sampleHigherLevel(ivec2 pos) { - vec3 col = imageLoad(uInGlare, ivec2(pos*2 + ivec2(0,0))).rgb * 0.25 - + imageLoad(uInGlare, ivec2(pos*2 + ivec2(1,0))).rgb * 0.25 - + imageLoad(uInGlare, ivec2(pos*2 + ivec2(0,1))).rgb * 0.25 - + imageLoad(uInGlare, ivec2(pos*2 + ivec2(1,1))).rgb * 0.25; - return col; - } - - // Calls the method above four times in order to allow for bilinear interpolation. This is - // required for floating point positions and results in sixteen texture look-ups. - vec3 sampleHigherLevel(vec2 pos) { - ivec2 ipos = ivec2(pos); - vec3 tl = sampleHigherLevel(ipos); - vec3 tr = sampleHigherLevel(ipos + ivec2(1, 0)); - vec3 bl = sampleHigherLevel(ipos + ivec2(0, 1)); - vec3 br = sampleHigherLevel(ipos + ivec2(1, 1)); - vec2 f = fract(pos); - vec3 tA = mix(tl, tr, f.x); - vec3 tB = mix(bl, br, f.x); - return mix(tA, tB, f.y); - } - - // Makes just one texture look-ups in the input layer of the glare mipmap at the given - // pixel position. - vec3 sampleSameLevel(ivec2 pos) { - return imageLoad(uInGlare, pos).rgb; - } - - // Calls the method above four times in order to allow for bilinear interpolation. This is - // required for floating point positions and results in four texture look-ups. - vec3 sampleSameLevel(vec2 pos) { - ivec2 ipos = ivec2(pos); - vec3 tl = sampleSameLevel(ipos); - vec3 tr = sampleSameLevel(ipos + ivec2(1, 0)); - vec3 bl = sampleSameLevel(ipos + ivec2(0, 1)); - vec3 br = sampleSameLevel(ipos + ivec2(1, 1)); - vec2 f = fract(pos); - vec3 tA = mix(tl, tr, f.x); - vec3 tB = mix(bl, br, f.x); - return mix(tA, tB, f.y); - } - - // Rotates the given vector around a given axis. - // Based on comment from http://www.neilmendoza.com/glsl-rotation-about-an-arbitrary-axis/ - vec3 rotate(vec3 v, vec3 axis, float angle) { - return mix(dot(axis, v) * axis, v, cos(angle)) + cross(axis, v) * sin(angle); - } - - // Evalutes the normal distribution function for the given value. - float getGauss(float sigma, float value) { - return 1.0 / (sigma*sqrt(2*PI)) * exp(-0.5*pow(value/sigma, 2)); - } - - - void main() { - - ivec2 pixelPos = ivec2(gl_GlobalInvocationID.xy); - ivec2 outputSize = imageSize(uOutGlare); - - // Discard any threads outside the output layer. - if (pixelPos.x >= outputSize.x || pixelPos.y >= outputSize.y) { - return; - } - - // Take an odd number of samples to make sure that we sample the center value. - const float samples = 2*(GLARE_QUALITY+1)+1; - - // These values will contain the accumulated glare values. - vec3 glare = vec3(0); - float totalWeight = 0; - - // The first glare variant computes a symmetrical gaussian blur in screen space. It is separated - // into a vertical and horizontal component which are computed in different passes. - // If uPass == 0, horizontal blurring happens, if uPass == 1, vertical blurring happens. - // This is not perspectively correct but very fast. - - #ifdef GLAREMODE_SYMMETRIC_GAUSS - for (float i = 0; i < samples; ++i) { - float offset = i - floor(samples/2); - float weight = getGauss(1, offset); - totalWeight += weight; - - // If we are writing to level zero, we have to sample the input HDR buffer. For all - // successive levels we sample the previous level in the first passes and the same level - // in the second passes. - if (uPass == 0 && uLevel == 0) { - glare += sampleHDRBuffer(pixelPos+ivec2(offset, 0)) * weight; - } else if (uPass == 0) { - glare += sampleHigherLevel(pixelPos+ivec2(offset, 0)) * weight; - } else { - glare += sampleSameLevel(pixelPos+ivec2(0, offset)) * weight; - } - } - #endif - - - // The second variant computes an asymmetric perspectively correct gaussian decomposed into two - // components which can be roughly described as radial and circular. A naive implementation with - // a strictly circular and a strictly radial component suffers from undefined behavior close to - // the focal point (usually in the middle of the screen) resulting in weird glow patterns in - // this area: - // - // Primary Component Secondary (orthogonal) Component - // - // \ | / / --- \ - // -- X -- | O | - // / | \ \ --- / - // radial circular - // - // - // Therefore the implementation below uses two components like the following: - // - // \ | / / -- \ - // | | | -- -- -- - // / | \ \ -- / - // vertical / radial horizontal / circular - - #ifdef GLAREMODE_ASYMMETRIC_GAUSS - - // Reproject the current pixel position to view space. - vec2 posClipSpace = 2.0 * vec2(gl_GlobalInvocationID.xy) / vec2(outputSize) - 1.0; - vec4 posViewSpace = uMatInvP * vec4(posClipSpace, 0.0, 1.0); - - // The primary component depicted above is a mixture between a vertical rotation and a radial - // rotation. A vertical rotation would require this axis: - vec3 rotAxisVertical = cross(posViewSpace.xyz, vec3(1, 0, 0)); - rotAxisVertical = cross(posViewSpace.xyz, rotAxisVertical); - - // A radial rotation would be around this axis: - vec3 rotAxisRadial = cross(posViewSpace.xyz, vec3(0, 0, -1)); - if (posViewSpace.y < 0) { - rotAxisRadial = -rotAxisRadial; - } - - // We mix those two axes with a factor which depends on the vertical position of our - // pixel on the screen. The magic factor of two determines how fast we change from one to - // another and is not very sensitive. A value of five would result in very similar images. - float alpha = clamp(2*abs(posViewSpace.y / length(posViewSpace.xyz)), 0, 1); - vec3 rotAxis = mix(rotAxisVertical, rotAxisRadial, alpha); - - // The primary passes use the axis computed above, the secondary passes use an - // orthogonal rotation axis. - if (uPass == 0) { - rotAxis = normalize(rotAxis); - } else { - rotAxis = normalize(cross(rotAxis, posViewSpace.xyz)); - } - - // The angle covered by the gauss kernel increases quadratically with the mipmap level. - const float totalAngle = pow(2, uLevel) * PI / 180.0; - - // Rotate the view vector to the current pixel several times around the rotation axis - // in order to sample the vicinity. - for (float i = 0; i < samples; ++i) { - float angle = totalAngle * i / (samples-1) - totalAngle * 0.5; - float sigma = totalAngle / MAX_LEVELS; - float weight = getGauss(sigma, angle); - - // Compute the rotated sample position in screen space. - vec4 pos = uMatP * vec4(rotate(posViewSpace.xyz, rotAxis, angle), 1.0); - pos /= pos.w; - - // Convert to texture space. - vec2 samplePos = (0.5*pos.xy + 0.5)*outputSize; - - // If we are writing to level zero, we have to sample the input HDR buffer. For all - // successive levels we sample the previous level in the first passes and the same level - // in the second passes. - if (uPass == 0 && uLevel == 0) { - glare += sampleHDRBuffer(samplePos) * weight; - } else if (uPass == 0) { - glare += sampleHigherLevel(samplePos) * weight; - } else { - glare += sampleSameLevel(samplePos) * weight; - } - - totalWeight += weight; - } - #endif - - // Make sure that we do not add energy. - glare /= totalWeight; - - // Finally store the glare value in the output layer of the glare mipmap. - imageStore(uOutGlare, pixelPos, vec4(glare, 0.0)); - } -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - GlareMipMap::GlareMipMap(uint32_t hdrBufferSamples, int hdrBufferWidth, int hdrBufferHeight) : VistaTexture(GL_TEXTURE_2D) , mHDRBufferSamples(hdrBufferSamples) @@ -323,7 +80,7 @@ void GlareMipMap::update( source += "#define GLAREMODE_ASYMMETRIC_GAUSS\n"; } - source += sGlareShader; + source += utils::filesystem::loadToString("../share/resources/shaders/glare.comp"); const char* pSource = source.c_str(); glShaderSource(shader, 1, &pSource, nullptr); glCompileShader(shader); From 66b446a0ad87887fc418479ced0e7e370cdc7fe6 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 12 Dec 2024 14:50:21 +0100 Subject: [PATCH 04/57] :wrench: Load star shaders from file --- plugins/csp-stars/CMakeLists.txt | 1 + plugins/csp-stars/shaders/starSnippets.glsl | 43 ++ .../csp-stars/shaders/starsBackground.frag | 27 ++ .../csp-stars/shaders/starsBackground.vert | 23 ++ plugins/csp-stars/shaders/starsBillboard.frag | 80 ++++ plugins/csp-stars/shaders/starsBillboard.geom | 74 ++++ plugins/csp-stars/shaders/starsBillboard.vert | 29 ++ plugins/csp-stars/shaders/starsOnePixel.frag | 57 +++ plugins/csp-stars/shaders/starsOnePixel.vert | 35 ++ plugins/csp-stars/src/Shaders.cpp | 373 ------------------ plugins/csp-stars/src/Stars.cpp | 31 +- plugins/csp-stars/src/Stars.hpp | 9 - 12 files changed, 393 insertions(+), 389 deletions(-) create mode 100644 plugins/csp-stars/shaders/starSnippets.glsl create mode 100644 plugins/csp-stars/shaders/starsBackground.frag create mode 100644 plugins/csp-stars/shaders/starsBackground.vert create mode 100644 plugins/csp-stars/shaders/starsBillboard.frag create mode 100644 plugins/csp-stars/shaders/starsBillboard.geom create mode 100644 plugins/csp-stars/shaders/starsBillboard.vert create mode 100644 plugins/csp-stars/shaders/starsOnePixel.frag create mode 100644 plugins/csp-stars/shaders/starsOnePixel.vert delete mode 100644 plugins/csp-stars/src/Shaders.cpp diff --git a/plugins/csp-stars/CMakeLists.txt b/plugins/csp-stars/CMakeLists.txt index b8e788b2a..bbb96696e 100644 --- a/plugins/csp-stars/CMakeLists.txt +++ b/plugins/csp-stars/CMakeLists.txt @@ -45,4 +45,5 @@ source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES install(TARGETS csp-stars DESTINATION "share/plugins") install(DIRECTORY "gui" DESTINATION "share/resources") +install(DIRECTORY "shaders" DESTINATION "share/resources") install(DIRECTORY "textures" DESTINATION "share/resources") \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starSnippets.glsl b/plugins/csp-stars/shaders/starSnippets.glsl new file mode 100644 index 000000000..520050773 --- /dev/null +++ b/plugins/csp-stars/shaders/starSnippets.glsl @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +float log10(float x) { + return log(x) / log(10.0); +} + +float getApparentMagnitude(float absMagnitude, float distInParsce) { + return absMagnitude + 5.0 * log10(distInParsce / 10.0); +} + +// formula from https://en.wikipedia.org/wiki/Surface_brightness +float magnitudeToLuminance(float apparentMagnitude, float solidAngle) { + const float steradiansToSquareArcSecs = 4.25e10; + float surfaceBrightness = apparentMagnitude + 2.5 * log10(solidAngle * steradiansToSquareArcSecs); + return 10.8e4 * pow(10, -0.4 * surfaceBrightness); +} + +vec3 SRGBtoLINEAR(vec3 srgbIn) { + vec3 bLess = step(vec3(0.04045), srgbIn); + return mix(srgbIn / vec3(12.92), pow((srgbIn + vec3(0.055)) / vec3(1.055), vec3(2.4)), bLess); +} + +float mapRange(float value, float oldMin, float oldMax, float newMin, float newMax) { + return newMin + (clamp(value, oldMin, oldMax) - oldMin) * (newMax - newMin) / (oldMax - oldMin); +} + +// http://filmicworlds.com/blog/filmic-tonemapping-operators/ +float A = 0.15; +float B = 0.50; +float C = 0.10; +float D = 0.20; +float E = 0.02; +float F = 0.30; +float W = 11.2; + +vec3 Uncharted2Tonemap(vec3 x) { + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsBackground.frag b/plugins/csp-stars/shaders/starsBackground.frag new file mode 100644 index 000000000..1e3b15b0c --- /dev/null +++ b/plugins/csp-stars/shaders/starsBackground.frag @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +// inputs +in vec3 vView; + +// uniforms +uniform sampler2D iTexture; +uniform vec4 cColor; + +// outputs +layout(location = 0) out vec3 vOutColor; + +float my_atan2(float a, float b) { + return 2.0 * atan(a / (sqrt(b * b + a * a) + b)); +} + +void main() { + const float PI = 3.14159265359; + vec3 view = normalize(vView); + vec2 texcoord = vec2(0.5 * my_atan2(view.x, -view.z) / PI, acos(view.y) / PI); + vOutColor = texture(iTexture, texcoord).rgb * cColor.rgb * cColor.a; +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsBackground.vert b/plugins/csp-stars/shaders/starsBackground.vert new file mode 100644 index 000000000..799977ca2 --- /dev/null +++ b/plugins/csp-stars/shaders/starsBackground.vert @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +// inputs +layout(location = 0) in vec2 vPosition; + +// uniforms +uniform mat4 uInvMVP; +uniform mat4 uInvMV; + +// outputs +out vec3 vView; + +void main() { + vec3 vRayOrigin = (uInvMV * vec4(0, 0, 0, 1)).xyz; + vec4 vRayEnd = uInvMVP * vec4(vPosition, 0, 1); + vView = vRayEnd.xyz / vRayEnd.w - vRayOrigin; + gl_Position = vec4(vPosition, 1, 1); +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsBillboard.frag b/plugins/csp-stars/shaders/starsBillboard.frag new file mode 100644 index 000000000..1cf553d64 --- /dev/null +++ b/plugins/csp-stars/shaders/starsBillboard.frag @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +// inputs +in vec3 iColor; +in float iMagnitude; +in vec2 iTexcoords; + +// uniforms +uniform sampler2D iTexture; +uniform float uSolidAngle; +uniform float uLuminanceMultiplicator; +uniform float uMinMagnitude; +uniform float uMaxMagnitude; + +// outputs +out vec4 oLuminance; + +void main() { + float dist = min(1, length(iTexcoords)); + float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); + +#ifdef DRAWMODE_DISC + float fac = dist < 1 ? luminance : 0; +#endif + +#ifdef DRAWMODE_SMOOTH_DISC + // the brightness is basically a cone from above - to achieve the + // same total brightness, we have to multiply it with three + float fac = luminance * clamp(1 - dist, 0, 1) * 3; +#endif + +#ifdef DRAWMODE_SCALED_DISC + // The stars were scaled in this mode so we have to incorporate this here to + // achieve the same total luminance. + float scaleFac = mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 3.0, 0.3); + float fac = luminance * clamp(1 - dist, 0, 1) * 3 / (scaleFac * scaleFac); +#endif + +#ifdef DRAWMODE_GLARE_DISC + float scaleFac = mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); + + // In this mode, 90% of the brightness is drawn using a small smooth disc in the center + // (just like DRAWMODE_SMOOTH_DISC) and the othe 10% are drawn as glare using an inverse + // quadratic falloff. + + // The billboard is scaled depending on the magnitude, but we want the disc to be the same + // size for all stars. So we have to scale the distance accordingly. + float disc = luminance * clamp(1 - dist * scaleFac, 0, 1) * 3; + + float falloff = max(0, 0.5 / pow(dist + 0.1, 2) - 0.5); + float glare = luminance * falloff / (scaleFac * scaleFac) * 0.5; + + float fac = 0.5 * glare + 0.5 * disc; +#endif + +#ifdef DRAWMODE_SPRITE + + // The stars were scaled in this mode so we have to incorporate this here to achieve the + // same total luminance. + float scaleFac = mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 10.0, 1.0); + float fac = texture(iTexture, iTexcoords * 0.5 + 0.5).r * luminance / (scaleFac * scaleFac); + + // A magic number here. This is the average brightness of the currently used + // star texture (identify -format "%[fx:mean]\n" star.png). + fac /= 0.0559036; +#endif + + vec3 vColor = iColor * fac * uLuminanceMultiplicator; + + oLuminance = vec4(vColor, 1.0); + +#ifndef ENABLE_HDR + oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); +#endif +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsBillboard.geom b/plugins/csp-stars/shaders/starsBillboard.geom new file mode 100644 index 000000000..2a30a1e08 --- /dev/null +++ b/plugins/csp-stars/shaders/starsBillboard.geom @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +layout(points) in; +layout(triangle_strip, max_vertices = 4) out; + +// inputs +in vec3 vColor[]; +in float vMagnitude[]; + +// uniforms +uniform mat4 uMatP; +uniform float uSolidAngle; +uniform float uMinMagnitude; +uniform float uMaxMagnitude; + +// outputs +out vec3 iColor; +out float iMagnitude; +out vec2 iTexcoords; + +void main() { + iColor = SRGBtoLINEAR(vColor[0]); + + iMagnitude = vMagnitude[0]; + + if (iMagnitude > uMaxMagnitude || iMagnitude < uMinMagnitude) { + return; + } + + float dist = length(gl_in[0].gl_Position.xyz); + vec3 y = vec3(0, 1, 0); + vec3 z = gl_in[0].gl_Position.xyz / dist; + vec3 x = normalize(cross(z, y)); + y = normalize(cross(z, x)); + + const float offset[2] = float[2](0.5, -0.5); + const float PI = 3.14159265359; + float diameter = sqrt(uSolidAngle / (4 * PI)) * 4.0; + float scale = dist * diameter; + +#ifdef DRAWMODE_SCALED_DISC + // We scale the stars up a little in this mode so that they look more similar to + // the other modes without the user having to move the star-size slider. + scale *= mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 3.0, 0.3); +#endif + +#ifdef DRAWMODE_GLARE_DISC + float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); + scale *= mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); +#endif + +#ifdef DRAWMODE_SPRITE + // We scale the stars up a little in this mode so that they look more similar to + // the other modes without the user having to move the star-size slider. + scale *= mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 10.0, 1.0); +#endif + + for (int j = 0; j != 2; ++j) { + for (int i = 0; i != 2; ++i) { + iTexcoords = vec2(offset[i], offset[j]) * 2; + vec3 pos = gl_in[0].gl_Position.xyz + (offset[i] * x + offset[j] * y) * scale; + + gl_Position = uMatP * vec4(pos, 1); + + EmitVertex(); + } + } + EndPrimitive(); +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsBillboard.vert b/plugins/csp-stars/shaders/starsBillboard.vert new file mode 100644 index 000000000..fd928cb9a --- /dev/null +++ b/plugins/csp-stars/shaders/starsBillboard.vert @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +// inputs +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec3 inColor; +layout(location = 2) in float inAbsMagnitude; + +// uniforms +uniform mat4 uMatMV; +uniform mat4 uInvMV; + +// outputs +out vec3 vColor; +out float vMagnitude; + +void main() { + const float parsecToMeter = 3.08567758e16; + vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; + + vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); + vColor = inColor; + + gl_Position = uMatMV * vec4(inPos * parsecToMeter, 1); +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsOnePixel.frag b/plugins/csp-stars/shaders/starsOnePixel.frag new file mode 100644 index 000000000..d02c548ee --- /dev/null +++ b/plugins/csp-stars/shaders/starsOnePixel.frag @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +// inputs +in vec3 vColor; +in vec4 vScreenSpacePos; +in float vMagnitude; + +// uniforms +uniform float uLuminanceMultiplicator; +uniform mat4 uInvP; +uniform vec2 uResolution; +uniform float uMinMagnitude; +uniform float uMaxMagnitude; +uniform float uSolidAngle; + +// outputs +out vec4 oLuminance; + +float getSolidAngle(vec3 a, vec3 b, vec3 c) { + return 2 * atan(abs(dot(a, cross(b, c))) / (1 + dot(a, b) + dot(a, c) + dot(b, c))); +} + +float getSolidAngleOfPixel(vec4 screenSpacePosition, vec2 resolution, mat4 invProjection) { + vec2 pixel = vec2(1.0) / resolution; + vec4 pixelCorners[4] = vec4[4](screenSpacePosition + vec4(-pixel.x, -pixel.y, 0, 0), + screenSpacePosition + vec4(+pixel.x, -pixel.y, 0, 0), + screenSpacePosition + vec4(+pixel.x, +pixel.y, 0, 0), + screenSpacePosition + vec4(-pixel.x, +pixel.y, 0, 0)); + + for (int i = 0; i < 4; ++i) { + pixelCorners[i] = invProjection * pixelCorners[i]; + pixelCorners[i].xyz = normalize(pixelCorners[i].xyz); + } + + return getSolidAngle(pixelCorners[0].xyz, pixelCorners[1].xyz, pixelCorners[2].xyz) + + getSolidAngle(pixelCorners[0].xyz, pixelCorners[2].xyz, pixelCorners[3].xyz); +} + +void main() { + if (vMagnitude > uMaxMagnitude || vMagnitude < uMinMagnitude) { + discard; + } + + float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); + float luminance = magnitudeToLuminance(vMagnitude, solidAngle); + + oLuminance = vec4(vColor * luminance * uLuminanceMultiplicator, 1.0); + +#ifndef ENABLE_HDR + oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); +#endif +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsOnePixel.vert b/plugins/csp-stars/shaders/starsOnePixel.vert new file mode 100644 index 000000000..f2e36c366 --- /dev/null +++ b/plugins/csp-stars/shaders/starsOnePixel.vert @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +// inputs +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec3 inColor; +layout(location = 2) in float inAbsMagnitude; + +// uniforms +uniform mat4 uMatMV; +uniform mat4 uMatP; +uniform mat4 uInvMV; + +// outputs +out vec3 vColor; +out vec4 vScreenSpacePos; +out float vMagnitude; + +void main() { + const float parsecToMeter = 3.08567758e16; + vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; + + vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); + + vColor = SRGBtoLINEAR(inColor); + + vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); + vScreenSpacePos /= vScreenSpacePos.w; + + gl_Position = vScreenSpacePos; +} \ No newline at end of file diff --git a/plugins/csp-stars/src/Shaders.cpp b/plugins/csp-stars/src/Shaders.cpp deleted file mode 100644 index 53d0c2a4a..000000000 --- a/plugins/csp-stars/src/Shaders.cpp +++ /dev/null @@ -1,373 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////////////////////////// -// This file is part of CosmoScout VR // -//////////////////////////////////////////////////////////////////////////////////////////////////// - -// SPDX-FileCopyrightText: German Aerospace Center (DLR) -// SPDX-License-Identifier: MIT - -#include "Stars.hpp" - -namespace csp::stars { - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cStarsSnippets = R"( -float log10(float x) { - return log(x) / log(10.0); -} - -float getApparentMagnitude(float absMagnitude, float distInParsce) { - return absMagnitude + 5.0*log10(distInParsce / 10.0); -} - -// formula from https://en.wikipedia.org/wiki/Surface_brightness -float magnitudeToLuminance(float apparentMagnitude, float solidAngle) { - const float steradiansToSquareArcSecs = 4.25e10; - float surfaceBrightness = apparentMagnitude + 2.5 * log10(solidAngle * steradiansToSquareArcSecs); - return 10.8e4 * pow(10, -0.4 * surfaceBrightness); -} - -vec3 SRGBtoLINEAR(vec3 srgbIn) { - vec3 bLess = step(vec3(0.04045),srgbIn); - return mix( srgbIn/vec3(12.92), pow((srgbIn+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); -} - -float mapRange(float value, float oldMin, float oldMax, float newMin, float newMax) { - return newMin + (clamp(value, oldMin, oldMax) - oldMin) * (newMax - newMin) / (oldMax - oldMin); -} - -// http://filmicworlds.com/blog/filmic-tonemapping-operators/ -float A = 0.15; -float B = 0.50; -float C = 0.10; -float D = 0.20; -float E = 0.02; -float F = 0.30; -float W = 11.2; - -vec3 Uncharted2Tonemap(vec3 x) { - return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; -} -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cStarsVert = R"( -// inputs -layout(location = 0) in vec3 inPos; -layout(location = 1) in vec3 inColor; -layout(location = 2) in float inAbsMagnitude; - -// uniforms -uniform mat4 uMatMV; -uniform mat4 uInvMV; - -// outputs -out vec3 vColor; -out float vMagnitude; - -void main() { - const float parsecToMeter = 3.08567758e16; - vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; - - vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); - vColor = inColor; - - gl_Position = uMatMV * vec4(inPos * parsecToMeter, 1); -} - -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cStarsGeom = R"( -layout(points) in; -layout(triangle_strip, max_vertices = 4) out; - -// inputs -in vec3 vColor[]; -in float vMagnitude[]; - -// uniforms -uniform mat4 uMatP; -uniform float uSolidAngle; -uniform float uMinMagnitude; -uniform float uMaxMagnitude; - -// outputs -out vec3 iColor; -out float iMagnitude; -out vec2 iTexcoords; - -void main() { - iColor = SRGBtoLINEAR(vColor[0]); - - iMagnitude = vMagnitude[0]; - - if (iMagnitude > uMaxMagnitude || iMagnitude < uMinMagnitude) { - return; - } - - float dist = length(gl_in[0].gl_Position.xyz); - vec3 y = vec3(0, 1, 0); - vec3 z = gl_in[0].gl_Position.xyz / dist; - vec3 x = normalize(cross(z, y)); - y = normalize(cross(z, x)); - - const float offset[2] = float[2](0.5, -0.5); - const float PI = 3.14159265359; - float diameter = sqrt(uSolidAngle / (4 * PI)) * 4.0; - float scale = dist * diameter; - - #ifdef DRAWMODE_SCALED_DISC - // We scale the stars up a little in this mode so that they look more similar to - // the other modes without the user having to move the star-size slider. - scale *= mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 3.0, 0.3); - #endif - - #ifdef DRAWMODE_GLARE_DISC - float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); - scale *= mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); - #endif - - #ifdef DRAWMODE_SPRITE - // We scale the stars up a little in this mode so that they look more similar to - // the other modes without the user having to move the star-size slider. - scale *= mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 10.0, 1.0); - #endif - - for(int j=0; j!=2; ++j) { - for(int i=0; i!=2; ++i) { - iTexcoords = vec2(offset[i], offset[j])*2; - vec3 pos = gl_in[0].gl_Position.xyz + (offset[i] * x + offset[j] * y) * scale; - - gl_Position = uMatP * vec4(pos, 1); - - EmitVertex(); - } - } - EndPrimitive(); -} - -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cStarsFrag = R"( -// inputs -in vec3 iColor; -in float iMagnitude; -in vec2 iTexcoords; - -// uniforms -uniform sampler2D iTexture; -uniform float uSolidAngle; -uniform float uLuminanceMultiplicator; -uniform float uMinMagnitude; -uniform float uMaxMagnitude; - -// outputs -out vec4 oLuminance; - -void main() { - float dist = min(1, length(iTexcoords)); - float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); - - #ifdef DRAWMODE_DISC - float fac = dist < 1 ? luminance : 0; - #endif - - #ifdef DRAWMODE_SMOOTH_DISC - // the brightness is basically a cone from above - to achieve the - // same total brightness, we have to multiply it with three - float fac = luminance * clamp(1 - dist, 0, 1) * 3; - #endif - - #ifdef DRAWMODE_SCALED_DISC - // The stars were scaled in this mode so we have to incorporate this here to - // achieve the same total luminance. - float scaleFac = mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 3.0, 0.3); - float fac = luminance * clamp(1 - dist, 0, 1) * 3 / (scaleFac * scaleFac); - #endif - - #ifdef DRAWMODE_GLARE_DISC - float scaleFac = mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); - - // In this mode, 90% of the brightness is drawn using a small smooth disc in the center - // (just like DRAWMODE_SMOOTH_DISC) and the othe 10% are drawn as glare using an inverse - // quadratic falloff. - - // The billboard is scaled depending on the magnitude, but we want the disc to be the same - // size for all stars. So we have to scale the distance accordingly. - float disc = luminance * clamp(1 - dist * scaleFac, 0, 1) * 3; - - float falloff = max(0, 0.5 / pow(dist+0.1, 2) - 0.5); - float glare = luminance * falloff / (scaleFac * scaleFac) * 0.5; - - float fac = 0.5 * glare + 0.5 * disc; - #endif - - #ifdef DRAWMODE_SPRITE - - // The stars were scaled in this mode so we have to incorporate this here to achieve the - // same total luminance. - float scaleFac = mapRange(iMagnitude, uMinMagnitude, uMaxMagnitude, 10.0, 1.0); - float fac = texture(iTexture, iTexcoords * 0.5 + 0.5).r * luminance / (scaleFac * scaleFac); - - // A magic number here. This is the average brightness of the currently used - // star texture (identify -format "%[fx:mean]\n" star.png). - fac /= 0.0559036; - #endif - - vec3 vColor = iColor * fac * uLuminanceMultiplicator; - - oLuminance = vec4(vColor, 1.0); - - #ifndef ENABLE_HDR - oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); - #endif -} - -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cStarsVertOnePixel = R"( - -// inputs -layout(location = 0) in vec3 inPos; -layout(location = 1) in vec3 inColor; -layout(location = 2) in float inAbsMagnitude; - -// uniforms -uniform mat4 uMatMV; -uniform mat4 uMatP; -uniform mat4 uInvMV; - -// outputs -out vec3 vColor; -out vec4 vScreenSpacePos; -out float vMagnitude; - -void main() { - const float parsecToMeter = 3.08567758e16; - vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; - - vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); - - vColor = SRGBtoLINEAR(inColor); - - vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); - vScreenSpacePos /= vScreenSpacePos.w; - - gl_Position = vScreenSpacePos; -} -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cStarsFragOnePixel = R"( - -// inputs -in vec3 vColor; -in vec4 vScreenSpacePos; -in float vMagnitude; - -// uniforms -uniform float uLuminanceMultiplicator; -uniform mat4 uInvP; -uniform vec2 uResolution; -uniform float uMinMagnitude; -uniform float uMaxMagnitude; -uniform float uSolidAngle; - -// outputs -out vec4 oLuminance; - -float getSolidAngle(vec3 a, vec3 b, vec3 c) { - return 2 * atan(abs(dot(a, cross(b, c))) / (1 + dot(a, b) + dot(a, c) + dot(b, c))); -} - -float getSolidAngleOfPixel(vec4 screenSpacePosition, vec2 resolution, mat4 invProjection) { - vec2 pixel = vec2(1.0) / resolution; - vec4 pixelCorners[4] = vec4[4]( - screenSpacePosition + vec4(- pixel.x, - pixel.y, 0, 0), - screenSpacePosition + vec4(+ pixel.x, - pixel.y, 0, 0), - screenSpacePosition + vec4(+ pixel.x, + pixel.y, 0, 0), - screenSpacePosition + vec4(- pixel.x, + pixel.y, 0, 0) - ); - - for (int i=0; i<4; ++i) { - pixelCorners[i] = invProjection * pixelCorners[i]; - pixelCorners[i].xyz = normalize(pixelCorners[i].xyz); - } - - return getSolidAngle(pixelCorners[0].xyz, pixelCorners[1].xyz, pixelCorners[2].xyz) - + getSolidAngle(pixelCorners[0].xyz, pixelCorners[2].xyz, pixelCorners[3].xyz); -} - -void main() { - if (vMagnitude > uMaxMagnitude || vMagnitude < uMinMagnitude) { - discard; - } - - float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); - float luminance = magnitudeToLuminance(vMagnitude, solidAngle); - - oLuminance = vec4(vColor * luminance * uLuminanceMultiplicator, 1.0); - - #ifndef ENABLE_HDR - oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); - #endif -} -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cBackgroundVert = R"( -// inputs -layout(location = 0) in vec2 vPosition; - -// uniforms -uniform mat4 uInvMVP; -uniform mat4 uInvMV; - -// outputs -out vec3 vView; - -void main() { - vec3 vRayOrigin = (uInvMV * vec4(0, 0, 0, 1)).xyz; - vec4 vRayEnd = uInvMVP * vec4(vPosition, 0, 1); - vView = vRayEnd.xyz / vRayEnd.w - vRayOrigin; - gl_Position = vec4(vPosition, 1, 1); -} -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -const char* Stars::cBackgroundFrag = R"( -// inputs -in vec3 vView; - -// uniforms -uniform sampler2D iTexture; -uniform vec4 cColor; - -// outputs -layout(location = 0) out vec3 vOutColor; - -float my_atan2(float a, float b) { - return 2.0 * atan(a/(sqrt(b*b + a*a) + b)); -} - -void main() { - const float PI = 3.14159265359; - vec3 view = normalize(vView); - vec2 texcoord = vec2(0.5*my_atan2(view.x, -view.z)/PI, acos(view.y)/PI); - vOutColor = texture(iTexture, texcoord).rgb * cColor.rgb * cColor.a; -} -)"; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -} // namespace csp::stars diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index 9007d62bc..ecae1610f 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -11,6 +11,7 @@ #include "../../../src/cs-graphics/TextureLoader.hpp" #include "../../../src/cs-utils/FrameStats.hpp" +#include "../../../src/cs-utils/filesystem.hpp" #ifdef _WIN32 #include @@ -354,21 +355,37 @@ bool Stars::Do() { defines += "#define DRAWMODE_SPRITE\n"; } + defines += cs::utils::filesystem::loadToString("../share/resources/shaders/starSnippets.glsl"); + mStarShader = VistaGLSLShader(); if (mDrawMode == DrawMode::ePoint || mDrawMode == DrawMode::eSmoothPoint) { - mStarShader.InitVertexShaderFromString(defines + cStarsSnippets + cStarsVertOnePixel); - mStarShader.InitFragmentShaderFromString(defines + cStarsSnippets + cStarsFragOnePixel); + mStarShader.InitVertexShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsOnePixel.vert")); + mStarShader.InitFragmentShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsOnePixel.frag")); } else { - mStarShader.InitVertexShaderFromString(defines + cStarsSnippets + cStarsVert); - mStarShader.InitGeometryShaderFromString(defines + cStarsSnippets + cStarsGeom); - mStarShader.InitFragmentShaderFromString(defines + cStarsSnippets + cStarsFrag); + mStarShader.InitVertexShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsBillboard.vert")); + mStarShader.InitGeometryShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsBillboard.geom")); + mStarShader.InitFragmentShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsBillboard.frag")); } mStarShader.Link(); mBackgroundShader = VistaGLSLShader(); - mBackgroundShader.InitVertexShaderFromString(defines + cBackgroundVert); - mBackgroundShader.InitFragmentShaderFromString(defines + cBackgroundFrag); + mBackgroundShader.InitVertexShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsBackground.vert")); + mBackgroundShader.InitFragmentShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsBackground.frag")); mBackgroundShader.Link(); mUniforms.bgInverseMVMatrix = mBackgroundShader.GetUniformLocation("uInvMV"); diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index 6d812bceb..3eba2e9b0 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -207,15 +207,6 @@ class Stars : public IVistaOpenGLDraw { static constexpr size_t NUM_COLUMNS = cs::utils::enumCast(CatalogColumn::eCount); static const std::array, NUM_CATALOGS> cColumnMapping; - - static const char* cStarsSnippets; - static const char* cStarsVertOnePixel; - static const char* cStarsFragOnePixel; - static const char* cStarsVert; - static const char* cStarsFrag; - static const char* cStarsGeom; - static const char* cBackgroundVert; - static const char* cBackgroundFrag; }; } // namespace csp::stars From ffde74c773ff24cc94379da565d89276181cd6a1 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 13 Dec 2024 14:19:06 +0100 Subject: [PATCH 05/57] :wrench: Add initial glare composition shader --- resources/shaders/glareComposite.comp | 27 ++++++ src/cs-graphics/GlareMipMap.cpp | 116 +++++++++++++++----------- src/cs-graphics/GlareMipMap.hpp | 3 +- 3 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 resources/shaders/glareComposite.comp diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp new file mode 100644 index 000000000..a8f61e139 --- /dev/null +++ b/resources/shaders/glareComposite.comp @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +#version 430 + +layout(local_size_x = 16, local_size_y = 16) in; + +layout(rgba32f, binding = 1) readonly uniform image2D uInGlare; +layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; + +void main() { + + ivec2 pixelPos = ivec2(gl_GlobalInvocationID.xy); + ivec2 outputSize = imageSize(uOutGlare); + + // Discard any threads outside the output layer. + if (pixelPos.x >= outputSize.x || pixelPos.y >= outputSize.y) { + return; + } + + // Finally store the glare value in the output layer of the glare mipmap. + imageStore(uOutGlare, pixelPos, vec4(1.0, 0.0, 0.0, 0.0)); +} \ No newline at end of file diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 3b6df231f..c068325ca 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -22,6 +22,46 @@ namespace cs::graphics { //////////////////////////////////////////////////////////////////////////////////////////////////// +namespace { +GLint createComputeShader(std::string const& source) { + auto shader = glCreateShader(GL_COMPUTE_SHADER); + auto pSource = source.c_str(); + glShaderSource(shader, 1, &pSource, nullptr); + glCompileShader(shader); + + auto val = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &val); + if (val != GL_TRUE) { + auto log_length = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + std::vector v(log_length); + glGetShaderInfoLog(shader, log_length, nullptr, v.data()); + std::string log(begin(v), end(v)); + glDeleteShader(shader); + logger().error("ERROR: Failed to compile shader: {}", log); + } + + auto program = glCreateProgram(); + glAttachShader(program, shader); + glLinkProgram(program); + glDeleteShader(shader); + + glGetProgramiv(program, GL_LINK_STATUS, &val); + if (!val) { + auto log_length = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); + std::vector v(log_length); + glGetProgramInfoLog(program, log_length, nullptr, v.data()); + std::string log(begin(v), end(v)); + logger().error("ERROR: Failed to link compute shader: {}", log); + } + + return program; +} +} // namespace + +//////////////////////////////////////////////////////////////////////////////////////////////////// + GlareMipMap::GlareMipMap(uint32_t hdrBufferSamples, int hdrBufferWidth, int hdrBufferHeight) : VistaTexture(GL_TEXTURE_2D) , mHDRBufferSamples(hdrBufferSamples) @@ -55,7 +95,8 @@ GlareMipMap::GlareMipMap(uint32_t hdrBufferSamples, int hdrBufferWidth, int hdrB //////////////////////////////////////////////////////////////////////////////////////////////////// GlareMipMap::~GlareMipMap() { - glDeleteProgram(mComputeProgram); + glDeleteProgram(mGlareProgram); + glDeleteProgram(mCompositeProgram); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -65,10 +106,9 @@ void GlareMipMap::update( utils::FrameStats::ScopedTimer timer("Compute Glare"); - if (mComputeProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality) { + if (mGlareProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality) { // Create the compute shader. - auto shader = glCreateShader(GL_COMPUTE_SHADER); std::string source = "#version 430\n"; source += "#define NUM_MULTISAMPLES " + std::to_string(mHDRBufferSamples) + "\n"; source += "#define GLARE_QUALITY " + std::to_string(glareQuality) + "\n"; @@ -81,43 +121,16 @@ void GlareMipMap::update( } source += utils::filesystem::loadToString("../share/resources/shaders/glare.comp"); - const char* pSource = source.c_str(); - glShaderSource(shader, 1, &pSource, nullptr); - glCompileShader(shader); - - auto val = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &val); - if (val != GL_TRUE) { - auto log_length = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); - std::vector v(log_length); - glGetShaderInfoLog(shader, log_length, nullptr, v.data()); - std::string log(begin(v), end(v)); - glDeleteShader(shader); - logger().error("ERROR: Failed to compile shader: {}", log); - } - mComputeProgram = glCreateProgram(); - glAttachShader(mComputeProgram, shader); - glLinkProgram(mComputeProgram); - glDeleteShader(shader); + mGlareProgram = createComputeShader(source); - auto rvalue = 0; - glGetProgramiv(mComputeProgram, GL_LINK_STATUS, &rvalue); - if (!rvalue) { - auto log_length = 0; - glGetProgramiv(mComputeProgram, GL_INFO_LOG_LENGTH, &log_length); - std::vector v(log_length); - glGetProgramInfoLog(mComputeProgram, log_length, nullptr, v.data()); - std::string log(begin(v), end(v)); + mUniforms.level = glGetUniformLocation(mGlareProgram, "uLevel"); + mUniforms.pass = glGetUniformLocation(mGlareProgram, "uPass"); + mUniforms.projectionMatrix = glGetUniformLocation(mGlareProgram, "uMatP"); + mUniforms.inverseProjectionMatrix = glGetUniformLocation(mGlareProgram, "uMatInvP"); - logger().error("ERROR: Failed to link compute shader: {}", log); - } - - mUniforms.level = glGetUniformLocation(mComputeProgram, "uLevel"); - mUniforms.pass = glGetUniformLocation(mComputeProgram, "uPass"); - mUniforms.projectionMatrix = glGetUniformLocation(mComputeProgram, "uMatP"); - mUniforms.inverseProjectionMatrix = glGetUniformLocation(mComputeProgram, "uMatInvP"); + mCompositeProgram = createComputeShader( + utils::filesystem::loadToString("../share/resources/shaders/glareComposite.comp")); mLastGlareMode = glareMode; mLastGlareQuality = glareQuality; @@ -129,7 +142,7 @@ void GlareMipMap::update( // In the asymmetric case, its not strictly horizontal and vertical - see the shader above for // details. - glUseProgram(mComputeProgram); + glUseProgram(mGlareProgram); glBindImageTexture(0, hdrBufferComposite->GetId(), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); @@ -153,22 +166,22 @@ void GlareMipMap::update( int outputLevel = level; // level pass input inputLevel output outputLevel blur samplesHigherLevel - // 0 0 hdrbuffer 0 temp 0 horizontal true - // 0 1 temp 0 this 0 vertical false - // 1 0 this 0 temp 1 horizontal true - // 1 1 temp 1 this 1 vertical false - // 2 0 this 1 temp 2 horizontal true - // 2 1 temp 2 this 2 vertical false - // 3 0 this 2 temp 3 horizontal true - // 3 1 temp 3 this 3 vertical false + // 0 0 hdrbuffer 0 this 0 horizontal true + // 0 1 this 0 temp 0 vertical false + // 1 0 temp 0 this 1 horizontal true + // 1 1 this 1 temp 1 vertical false + // 2 0 temp 1 this 2 horizontal true + // 2 1 this 2 temp 2 vertical false + // 3 0 temp 2 this 3 horizontal true + // 3 1 this 3 temp 3 vertical false if (pass == 0) { - output = mTemporaryTarget; + input = mTemporaryTarget; inputLevel = std::max(0, inputLevel - 1); } if (pass == 1) { - input = mTemporaryTarget; + output = mTemporaryTarget; } glUniform1i(mUniforms.pass, pass); @@ -188,6 +201,13 @@ void GlareMipMap::update( static_cast(std::ceil(1.0 * height / 16)), 1); } } + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + glUseProgram(mCompositeProgram); + // glBindImageTexture(1, mTemporaryTarget->GetId(), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + glBindImageTexture(2, this->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); + glDispatchCompute(static_cast(std::ceil(0.5 * mHDRBufferWidth / 16)), + static_cast(std::ceil(0.5 * mHDRBufferHeight / 16)), 1); glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index cf84cbfb5..9f721fafa 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -35,7 +35,8 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, uint32_t glareQuality); private: - GLuint mComputeProgram = 0; + GLuint mGlareProgram = 0; + GLuint mCompositeProgram = 0; uint32_t mHDRBufferSamples = 0; int mMaxLevels = 0; int mHDRBufferWidth = 0; From c3cad2406c99f9322c0a9b0ecdc74e334546e377 Mon Sep 17 00:00:00 2001 From: Nyran Date: Fri, 13 Dec 2024 19:55:06 +0100 Subject: [PATCH 06/57] :wrench: Make cs-graphics-related shader files discoverable for improved IDE integration --- src/cs-graphics/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cs-graphics/CMakeLists.txt b/src/cs-graphics/CMakeLists.txt index a16ff6538..4447fa172 100644 --- a/src/cs-graphics/CMakeLists.txt +++ b/src/cs-graphics/CMakeLists.txt @@ -12,9 +12,16 @@ file(GLOB SOURCE_FILES *.cpp */*.cpp) # Header files are only added in order to make them available in your IDE. file(GLOB HEADER_FILES *.hpp */*.hpp) +# Shader files are also only added in order to make them available in your IDE. +set(RESOURCE_SHADER_DIR ${CMAKE_SOURCE_DIR}/resources/shaders/) +file(GLOB SHADER_FILES ${RESOURCE_SHADER_DIR}* ${RESOURCE_SHADER_DIR}*/*) + +source_group("src/shaders" FILES ${SHADER_FILES}) + add_library(cs-graphics SHARED ${SOURCE_FILES} ${HEADER_FILES} + ${SHADER_FILES} ) target_link_libraries(cs-graphics From 5f8db27fed0e9dd54fc49ffdfcc65b5dc77e3cc2 Mon Sep 17 00:00:00 2001 From: Nyran Date: Fri, 13 Dec 2024 20:10:22 +0100 Subject: [PATCH 07/57] :wrench: Simplify weighting expression in glare MipMap computation --- resources/shaders/glare.comp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index 086d50ccc..92313453b 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -27,17 +27,20 @@ const float PI = 3.14159265359; // the pixel position in the base layer of the output mipmap pyramid. // For performance reasons, we only use one sample for multisample inputs. vec3 sampleHDRBuffer(ivec2 pos) { + ivec2 pos_times_two = pos << 1; + #if NUM_MULTISAMPLES > 0 - vec3 col = imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 0)), 0).rgb * 0.25 + - imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 0)), 0).rgb * 0.25 + - imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 1)), 0).rgb * 0.25 + - imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 1)), 0).rgb * 0.25; + vec3 col = imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 0)), 0).rgb + + imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 0)), 0).rgb + + imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 1)), 0).rgb + + imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 1)), 0).rgb; #else - vec3 col = imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 0))).rgb * 0.25 + - imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 0))).rgb * 0.25 + - imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(0, 1))).rgb * 0.25 + - imageLoad(uInHDRBuffer, ivec2(pos * 2 + ivec2(1, 1))).rgb * 0.25; + vec3 col = imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 0))).rgb + + imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 0))).rgb + + imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 1))).rgb + + imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 1))).rgb; #endif + col *= 0.25; return col; } @@ -58,10 +61,12 @@ vec3 sampleHDRBuffer(vec2 pos) { // Makes four texture look-ups in the input layer of the glare mipmap at the four pixels // corresponding to the pixel position in the current layer of the mipmap pyramid. vec3 sampleHigherLevel(ivec2 pos) { - vec3 col = imageLoad(uInGlare, ivec2(pos * 2 + ivec2(0, 0))).rgb * 0.25 + - imageLoad(uInGlare, ivec2(pos * 2 + ivec2(1, 0))).rgb * 0.25 + - imageLoad(uInGlare, ivec2(pos * 2 + ivec2(0, 1))).rgb * 0.25 + - imageLoad(uInGlare, ivec2(pos * 2 + ivec2(1, 1))).rgb * 0.25; + ivec2 pos_times_two = pos << 1; + vec3 col = imageLoad(uInGlare, ivec2(pos_times_two + ivec2(0, 0))).rgb + + imageLoad(uInGlare, ivec2(pos_times_two + ivec2(1, 0))).rgb + + imageLoad(uInGlare, ivec2(pos_times_two + ivec2(0, 1))).rgb + + imageLoad(uInGlare, ivec2(pos_times_two + ivec2(1, 1))).rgb; + col *= 0.25; return col; } From 4b76caa746d0390d7e7316af4865651a1fca7bfb Mon Sep 17 00:00:00 2001 From: Nyran Date: Fri, 13 Dec 2024 21:32:46 +0100 Subject: [PATCH 08/57] :wrench: Finish basic plumbing between glare.comp, glareComposite.comp and tonemapping.frag shaders --- resources/shaders/glareComposite.comp | 5 +++-- resources/shaders/tonemap.frag | 16 ++++++++++++---- src/cs-graphics/GlareMipMap.cpp | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index a8f61e139..7a2f7f616 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -9,7 +9,7 @@ layout(local_size_x = 16, local_size_y = 16) in; -layout(rgba32f, binding = 1) readonly uniform image2D uInGlare; +layout(binding = 0) uniform sampler2D uGlareMipMap; layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; void main() { @@ -22,6 +22,7 @@ void main() { return; } + vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; // Finally store the glare value in the output layer of the glare mipmap. - imageStore(uOutGlare, pixelPos, vec4(1.0, 0.0, 0.0, 0.0)); + imageStore(uOutGlare, pixelPos, vec4(sampled_glare_base_level_values, 0.0)); } \ No newline at end of file diff --git a/resources/shaders/tonemap.frag b/resources/shaders/tonemap.frag index cd67dc3f3..85acf50e0 100644 --- a/resources/shaders/tonemap.frag +++ b/resources/shaders/tonemap.frag @@ -130,6 +130,14 @@ void main() { #endif if (uGlareIntensity > 0) { +#ifdef BICUBIC_GLARE_FILTER + + vec3 glare = texture2D(uGlareMipMap, vTexcoords, 0).rgb; + + color = mix(color, glare, pow(uGlareIntensity, 2.0)); +#else + + vec3 glare = vec3(0); float maxLevels = textureQueryLevels(uGlareMipMap); @@ -140,17 +148,17 @@ void main() { for (int i = 0; i < maxLevels; ++i) { float weight = 1.0 / pow(2, i); -#ifdef BICUBIC_GLARE_FILTER - glare += texture2D_bicubic(uGlareMipMap, vTexcoords, i).rgb * weight; -#else + glare += texture2D(uGlareMipMap, vTexcoords, i).rgb * weight; -#endif + totalWeight += weight; } // To make sure that we do not add energy, we divide by the total weight. color = mix(color, glare / totalWeight, pow(uGlareIntensity, 2.0)); +#endif + } // Filmic diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index c068325ca..db503d66b 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -204,7 +204,7 @@ void GlareMipMap::update( glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glUseProgram(mCompositeProgram); - // glBindImageTexture(1, mTemporaryTarget->GetId(), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + this->Bind(GL_TEXTURE0); glBindImageTexture(2, this->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glDispatchCompute(static_cast(std::ceil(0.5 * mHDRBufferWidth / 16)), static_cast(std::ceil(0.5 * mHDRBufferHeight / 16)), 1); From d36d1971d28a52462153e4c87a6c38edc2ed441b Mon Sep 17 00:00:00 2001 From: Nyran Date: Fri, 13 Dec 2024 22:00:57 +0100 Subject: [PATCH 09/57] :wrench: Expose glareIntensity as shader define for glareComposite.comp shader --- resources/shaders/glareComposite.comp | 2 -- src/cs-core/GraphicsEngine.cpp | 3 ++- src/cs-graphics/GlareMipMap.cpp | 15 ++++++++++----- src/cs-graphics/GlareMipMap.hpp | 3 ++- src/cs-graphics/HDRBuffer.cpp | 14 +++++++++++++- src/cs-graphics/HDRBuffer.hpp | 6 ++++++ 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index 7a2f7f616..93d1b5d35 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -5,8 +5,6 @@ // SPDX-FileCopyrightText: German Aerospace Center (DLR) // SPDX-License-Identifier: MIT -#version 430 - layout(local_size_x = 16, local_size_y = 16) in; layout(binding = 0) uniform sampler2D uGlareMipMap; diff --git a/src/cs-core/GraphicsEngine.cpp b/src/cs-core/GraphicsEngine.cpp index e50cebb3a..6a5aedfc9 100644 --- a/src/cs-core/GraphicsEngine.cpp +++ b/src/cs-core/GraphicsEngine.cpp @@ -188,7 +188,8 @@ GraphicsEngine::GraphicsEngine(std::shared_ptr settings) toneMappingGLNode, static_cast(utils::DrawOrder::eToneMapping)); mSettings->mGraphics.pGlareIntensity.connectAndTouch( - [this](float val) { mToneMappingNode->setGlareIntensity(val); }); + [this](float val) { mToneMappingNode->setGlareIntensity(val); + mHDRBuffer->setGlareQuality(val);}); mSettings->mGraphics.pGlareQuality.connectAndTouch( [this](uint32_t val) { mHDRBuffer->setGlareQuality(val); }); diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index db503d66b..891e7df67 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -102,7 +102,7 @@ GlareMipMap::~GlareMipMap() { //////////////////////////////////////////////////////////////////////////////////////////////////// void GlareMipMap::update( - VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, uint32_t glareQuality) { + VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, uint32_t glareQuality, float glareIntensity) { utils::FrameStats::ScopedTimer timer("Compute Glare"); @@ -112,6 +112,7 @@ void GlareMipMap::update( std::string source = "#version 430\n"; source += "#define NUM_MULTISAMPLES " + std::to_string(mHDRBufferSamples) + "\n"; source += "#define GLARE_QUALITY " + std::to_string(glareQuality) + "\n"; + source += "#define GLARE_INTENSITY " + std::to_string(glareIntensity) + "\n"; source += "#define MAX_LEVELS " + std::to_string(mMaxLevels) + "\n"; if (glareMode == HDRBuffer::GlareMode::eSymmetricGauss) { @@ -120,20 +121,24 @@ void GlareMipMap::update( source += "#define GLAREMODE_ASYMMETRIC_GAUSS\n"; } - source += utils::filesystem::loadToString("../share/resources/shaders/glare.comp"); + std::string glareMipMapComputeShaderSource = source; + glareMipMapComputeShaderSource += utils::filesystem::loadToString("../share/resources/shaders/glare.comp"); - mGlareProgram = createComputeShader(source); + mGlareProgram = createComputeShader(glareMipMapComputeShaderSource); mUniforms.level = glGetUniformLocation(mGlareProgram, "uLevel"); mUniforms.pass = glGetUniformLocation(mGlareProgram, "uPass"); mUniforms.projectionMatrix = glGetUniformLocation(mGlareProgram, "uMatP"); mUniforms.inverseProjectionMatrix = glGetUniformLocation(mGlareProgram, "uMatInvP"); - mCompositeProgram = createComputeShader( - utils::filesystem::loadToString("../share/resources/shaders/glareComposite.comp")); + + std::string glareCompositeComputeShaderSource = source; + glareCompositeComputeShaderSource += utils::filesystem::loadToString("../share/resources/shaders/glareComposite.comp"); + mCompositeProgram = createComputeShader(glareCompositeComputeShaderSource); mLastGlareMode = glareMode; mLastGlareQuality = glareQuality; + mLastGlareIntensity = glareIntensity; } // We update the glare mipmap with several passes. First, the base level is filled with a diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index 9f721fafa..90126f60e 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -32,7 +32,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { /// Perform the glare calculation by parallel reduction of the HDR values. This is a costly /// operation and should only be called once a frame. void update( - VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, uint32_t glareQuality); + VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, uint32_t glareQuality, float glareIntensity); private: GLuint mGlareProgram = 0; @@ -43,6 +43,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { int mHDRBufferHeight = 0; HDRBuffer::GlareMode mLastGlareMode = HDRBuffer::GlareMode::eSymmetricGauss; uint32_t mLastGlareQuality = 0; + float mLastGlareIntensity = 0; struct { uint32_t level = 0; diff --git a/src/cs-graphics/HDRBuffer.cpp b/src/cs-graphics/HDRBuffer.cpp index 4797e1c3e..53cbc8342 100644 --- a/src/cs-graphics/HDRBuffer.cpp +++ b/src/cs-graphics/HDRBuffer.cpp @@ -262,7 +262,7 @@ void HDRBuffer::updateGlareMipMap() { composite = hdrBuffer.mColorAttachments.at(1).get(); } - hdrBuffer.mGlareMipMap->update(composite, mGlareMode, mGlareQuality); + hdrBuffer.mGlareMipMap->update(composite, mGlareMode, mGlareQuality, mGlareIntensity); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -298,4 +298,16 @@ uint32_t HDRBuffer::getGlareQuality() const { //////////////////////////////////////////////////////////////////////////////////////////////////// +void HDRBuffer::setGlareIntensity(float intensity) { + mGlareIntensity = intensity; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +float HDRBuffer::getGlareIntensity() const { + return mGlareIntensity; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + } // namespace cs::graphics diff --git a/src/cs-graphics/HDRBuffer.hpp b/src/cs-graphics/HDRBuffer.hpp index d716b2714..629bb28b5 100644 --- a/src/cs-graphics/HDRBuffer.hpp +++ b/src/cs-graphics/HDRBuffer.hpp @@ -90,6 +90,11 @@ class CS_GRAPHICS_EXPORT HDRBuffer { void setGlareQuality(uint32_t quality); uint32_t getGlareQuality() const; + /// Controls the amount of artificial glare. Should be in the range [0-1]. If set to zero, the + /// GlareMipMap will not be updated which will increase performance. + void setGlareIntensity(float intensity); + float getGlareIntensity() const; + /// Returns the depth attachment for the currently rendered viewport. Be aware, that this can be /// texture with the target GL_TEXTURE_2D_MULTISAMPLE if getMultiSamples() > 0. VistaTexture* getDepthAttachment() const; @@ -133,6 +138,7 @@ class CS_GRAPHICS_EXPORT HDRBuffer { GlareMode mGlareMode = GlareMode::eSymmetricGauss; uint32_t mGlareQuality = 0; + float mGlareIntensity = 0.F; std::unordered_map mHDRBufferData; float mTotalLuminance = 1.F; float mMaximumLuminance = 1.F; From 9edd44baa40f02cb679860704fc5b97f92fc8406 Mon Sep 17 00:00:00 2001 From: Nyran Date: Fri, 13 Dec 2024 22:15:53 +0100 Subject: [PATCH 10/57] :beetle: Fix propagation and caching of glareIntensity in GlareMipMap class --- resources/shaders/glareComposite.comp | 2 +- src/cs-core/GraphicsEngine.cpp | 2 +- src/cs-graphics/GlareMipMap.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index 93d1b5d35..957570ea7 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -22,5 +22,5 @@ void main() { vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; // Finally store the glare value in the output layer of the glare mipmap. - imageStore(uOutGlare, pixelPos, vec4(sampled_glare_base_level_values, 0.0)); + imageStore(uOutGlare, pixelPos, vec4(1.0*GLARE_INTENSITY, 0.0, 0.0, 0.0)); } \ No newline at end of file diff --git a/src/cs-core/GraphicsEngine.cpp b/src/cs-core/GraphicsEngine.cpp index 6a5aedfc9..fcf1bb4ad 100644 --- a/src/cs-core/GraphicsEngine.cpp +++ b/src/cs-core/GraphicsEngine.cpp @@ -189,7 +189,7 @@ GraphicsEngine::GraphicsEngine(std::shared_ptr settings) mSettings->mGraphics.pGlareIntensity.connectAndTouch( [this](float val) { mToneMappingNode->setGlareIntensity(val); - mHDRBuffer->setGlareQuality(val);}); + mHDRBuffer->setGlareIntensity(val);}); mSettings->mGraphics.pGlareQuality.connectAndTouch( [this](uint32_t val) { mHDRBuffer->setGlareQuality(val); }); diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 891e7df67..8f6acac8a 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -106,7 +106,7 @@ void GlareMipMap::update( utils::FrameStats::ScopedTimer timer("Compute Glare"); - if (mGlareProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality) { + if (mGlareProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality || glareIntensity != mLastGlareIntensity) { // Create the compute shader. std::string source = "#version 430\n"; From 7ef12361c3f097092668a41ea76df5edf6cfa12f Mon Sep 17 00:00:00 2001 From: Nyran Date: Fri, 13 Dec 2024 22:57:16 +0100 Subject: [PATCH 11/57] :wrench: Implement straight-forward glare computation in glareComposite.comp --- resources/shaders/glareComposite.comp | 101 +++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index 957570ea7..5ba12cd85 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -10,6 +10,71 @@ layout(local_size_x = 16, local_size_y = 16) in; layout(binding = 0) uniform sampler2D uGlareMipMap; layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; + +// 4x4 bicubic filter using 4 bilinear texture lookups +// See GPU Gems 2: "Fast Third-Order Texture Filtering", Sigg & Hadwiger: +// http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html + +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { + float lod = float(p_lod); + vec2 tex_size = textureSize(uGlareMipMap, p_lod); + vec2 pixel_size = 1.0 / tex_size; + uv = uv * tex_size + 0.5; + vec2 iuv = floor(uv); + vec2 fuv = fract(uv); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; + + return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); +} + + void main() { ivec2 pixelPos = ivec2(gl_GlobalInvocationID.xy); @@ -20,7 +85,37 @@ void main() { return; } - vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; - // Finally store the glare value in the output layer of the glare mipmap. - imageStore(uOutGlare, pixelPos, vec4(1.0*GLARE_INTENSITY, 0.0, 0.0, 0.0)); + + if (GLARE_INTENSITY > 0.0) { + + vec3 glare = vec3(0); + float maxLevels = textureQueryLevels(uGlareMipMap); + + float totalWeight = 0; + + + vec2 vTexcoords = (pixelPos + vec2(0.5)) / textureSize(uGlareMipMap, 0); + + // Each level contains a successively more blurred version of the scene. We have to + // accumulate them with an exponentially decreasing weight to get a proper glare distribution. + for (int i = 0; i < maxLevels; ++i) { + float weight = 1.0 / pow(2, i); + + + glare += texture2D_bicubic(uGlareMipMap, vTexcoords, i).rgb * weight; + + + totalWeight += weight; + } + + vec3 final_glare_value = glare / totalWeight; + + + + //vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; + // Finally store the glare value in the output layer of the glare mipmap. + imageStore(uOutGlare, pixelPos, vec4(final_glare_value, 0.0)); + } else { + imageStore(uOutGlare, pixelPos, vec4(0.0, 0.0, 0.0, 0.0)); + } } \ No newline at end of file From 389268c9e746d097e715f74ac79946be72e27e8d Mon Sep 17 00:00:00 2001 From: Nyran Date: Sat, 14 Dec 2024 00:30:39 +0100 Subject: [PATCH 12/57] :wrench: Remove unnecessary test statement in glareComposite.comp --- resources/shaders/glareComposite.comp | 42 ++++++++++----------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index 5ba12cd85..389d8d54f 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -85,37 +85,25 @@ void main() { return; } + vec3 glare = vec3(0); + float maxLevels = textureQueryLevels(uGlareMipMap); - if (GLARE_INTENSITY > 0.0) { + float totalWeight = 0; - vec3 glare = vec3(0); - float maxLevels = textureQueryLevels(uGlareMipMap); - float totalWeight = 0; - - - vec2 vTexcoords = (pixelPos + vec2(0.5)) / textureSize(uGlareMipMap, 0); - - // Each level contains a successively more blurred version of the scene. We have to - // accumulate them with an exponentially decreasing weight to get a proper glare distribution. - for (int i = 0; i < maxLevels; ++i) { - float weight = 1.0 / pow(2, i); - - - glare += texture2D_bicubic(uGlareMipMap, vTexcoords, i).rgb * weight; - - - totalWeight += weight; - } - - vec3 final_glare_value = glare / totalWeight; + vec2 vTexcoords = (pixelPos + vec2(0.5)) / textureSize(uGlareMipMap, 0); + // Each level contains a successively more blurred version of the scene. We have to + // accumulate them with an exponentially decreasing weight to get a proper glare distribution. + for (int i = 0; i < maxLevels; ++i) { + float weight = 1.0 / (1 << i); + glare += texture2D_bicubic(uGlareMipMap, vTexcoords, i).rgb * weight; + totalWeight += weight; + } + vec3 final_glare_value = glare / totalWeight; - //vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; - // Finally store the glare value in the output layer of the glare mipmap. - imageStore(uOutGlare, pixelPos, vec4(final_glare_value, 0.0)); - } else { - imageStore(uOutGlare, pixelPos, vec4(0.0, 0.0, 0.0, 0.0)); - } + //vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; + // Finally store the glare value in the output layer of the glare mipmap. + imageStore(uOutGlare, pixelPos, vec4(final_glare_value, 0.0)); } \ No newline at end of file From 22e48cee8e25c0c96323c7270500f632b14b9b85 Mon Sep 17 00:00:00 2001 From: Nyran Date: Sun, 15 Dec 2024 20:55:31 +0100 Subject: [PATCH 13/57] :beetle: Fix glare shape caused by missing result of last vertical filter pass --- src/cs-graphics/GlareMipMap.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 8f6acac8a..cd9c5fcee 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -88,6 +88,11 @@ GlareMipMap::GlareMipMap(uint32_t hdrBufferSamples, int hdrBufferWidth, int hdrB // Create storage for temporary glare target (this is used for the vertical blurring passes). mTemporaryTarget->Bind(); + mTemporaryTarget->SetMinFilter(GL_LINEAR_MIPMAP_LINEAR); + mTemporaryTarget->SetMagFilter(GL_LINEAR); + mTemporaryTarget->SetWrapS(GL_CLAMP_TO_EDGE); + mTemporaryTarget->SetWrapT(GL_CLAMP_TO_EDGE); + glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, GL_RGBA32F, iWidth, iHeight); mTemporaryTarget->Unbind(); } @@ -209,7 +214,7 @@ void GlareMipMap::update( glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glUseProgram(mCompositeProgram); - this->Bind(GL_TEXTURE0); + mTemporaryTarget->Bind(GL_TEXTURE0); glBindImageTexture(2, this->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glDispatchCompute(static_cast(std::ceil(0.5 * mHDRBufferWidth / 16)), static_cast(std::ceil(0.5 * mHDRBufferHeight / 16)), 1); From e570cbd1c8f7b57d29050804525e63f67b905994 Mon Sep 17 00:00:00 2001 From: Nyran Date: Sun, 15 Dec 2024 22:56:54 +0100 Subject: [PATCH 14/57] :wrench: Prepare glare computation stage for hardware-interpolation using dual imageand texture binding --- resources/shaders/glare.comp | 20 +++++++++++++++++++- src/cs-graphics/GlareMipMap.cpp | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index 92313453b..cc61668a3 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -13,6 +13,7 @@ layout(rgba32f, binding = 0) readonly uniform image2DMS uInHDRBuffer; layout(rgba32f, binding = 0) readonly uniform image2D uInHDRBuffer; #endif +layout(binding = 0) uniform sampler2D uInGlareMipMap; layout(rgba32f, binding = 1) readonly uniform image2D uInGlare; layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; @@ -62,11 +63,27 @@ vec3 sampleHDRBuffer(vec2 pos) { // corresponding to the pixel position in the current layer of the mipmap pyramid. vec3 sampleHigherLevel(ivec2 pos) { ivec2 pos_times_two = pos << 1; + + + +#if 1 vec3 col = imageLoad(uInGlare, ivec2(pos_times_two + ivec2(0, 0))).rgb + imageLoad(uInGlare, ivec2(pos_times_two + ivec2(1, 0))).rgb + imageLoad(uInGlare, ivec2(pos_times_two + ivec2(0, 1))).rgb + imageLoad(uInGlare, ivec2(pos_times_two + ivec2(1, 1))).rgb; col *= 0.25; +#else + + int input_level = uLevel; + if(0 == uPass) { + input_level = max(0, input_level - 1); + } + ivec2 texture_size_of_lod = textureSize(uInGlareMipMap, input_level); + + vec2 normalized_sampling_pos = (pos_times_two + 0.5) / vec2(texture_size_of_lod); + + vec3 col = texture2D(uInGlareMipMap, normalized_sampling_pos, input_level).rgb; +#endif return col; } @@ -87,7 +104,8 @@ vec3 sampleHigherLevel(vec2 pos) { // Makes just one texture look-ups in the input layer of the glare mipmap at the given // pixel position. vec3 sampleSameLevel(ivec2 pos) { - return imageLoad(uInGlare, pos).rgb; + return texelFetch(uInGlareMipMap, pos, uLevel).rgb; + //return imageLoad(uInGlare, pos).rgb; } // Calls the method above four times in order to allow for bilinear interpolation. This is diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index cd9c5fcee..01d3a567d 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -203,6 +203,7 @@ void GlareMipMap::update( std::max(1.0, std::floor(static_cast(static_cast(mHDRBufferHeight / 2)) / std::pow(2, level)))); + input->Bind(GL_TEXTURE0); glBindImageTexture(1, input->GetId(), inputLevel, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); glBindImageTexture(2, output->GetId(), outputLevel, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); From c76a6e1d82df98c019b3286c5c9c64d194c987e8 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 08:45:42 +0100 Subject: [PATCH 15/57] :beetle: Fix culling of one-puixel stars behind the observer --- plugins/csp-stars/shaders/starsOnePixel.vert | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/csp-stars/shaders/starsOnePixel.vert b/plugins/csp-stars/shaders/starsOnePixel.vert index f2e36c366..1acfb3df0 100644 --- a/plugins/csp-stars/shaders/starsOnePixel.vert +++ b/plugins/csp-stars/shaders/starsOnePixel.vert @@ -29,7 +29,8 @@ void main() { vColor = SRGBtoLINEAR(inColor); vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); - vScreenSpacePos /= vScreenSpacePos.w; gl_Position = vScreenSpacePos; + + vScreenSpacePos /= vScreenSpacePos.w; } \ No newline at end of file From b068acd44ab8dc9c146bbf292cb93d7af35f50b2 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 09:12:22 +0100 Subject: [PATCH 16/57] :wrench: Make bicubic glare filter optional again --- resources/shaders/glareComposite.comp | 13 ++-- resources/shaders/tonemap.frag | 96 +-------------------------- src/cs-core/GraphicsEngine.cpp | 5 +- src/cs-graphics/GlareMipMap.cpp | 21 +++--- src/cs-graphics/GlareMipMap.hpp | 8 +-- src/cs-graphics/HDRBuffer.cpp | 10 +-- src/cs-graphics/HDRBuffer.hpp | 12 ++-- src/cs-graphics/ToneMappingNode.cpp | 19 ------ src/cs-graphics/ToneMappingNode.hpp | 28 +++----- 9 files changed, 51 insertions(+), 161 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index 389d8d54f..215419a2b 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -10,7 +10,6 @@ layout(local_size_x = 16, local_size_y = 16) in; layout(binding = 0) uniform sampler2D uGlareMipMap; layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; - // 4x4 bicubic filter using 4 bilinear texture lookups // See GPU Gems 2: "Fast Third-Order Texture Filtering", Sigg & Hadwiger: // http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html @@ -74,7 +73,6 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); } - void main() { ivec2 pixelPos = ivec2(gl_GlobalInvocationID.xy); @@ -90,20 +88,23 @@ void main() { float totalWeight = 0; - - vec2 vTexcoords = (pixelPos + vec2(0.5)) / textureSize(uGlareMipMap, 0); + vec2 vTexcoords = (pixelPos + vec2(0.5)) / textureSize(uGlareMipMap, 0); // Each level contains a successively more blurred version of the scene. We have to // accumulate them with an exponentially decreasing weight to get a proper glare distribution. for (int i = 0; i < maxLevels; ++i) { float weight = 1.0 / (1 << i); +#ifdef BICUBIC_GLARE_FILTER glare += texture2D_bicubic(uGlareMipMap, vTexcoords, i).rgb * weight; +#else + glare += textureLod(uGlareMipMap, vTexcoords, float(i)).rgb * weight; +#endif totalWeight += weight; } vec3 final_glare_value = glare / totalWeight; - //vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; - // Finally store the glare value in the output layer of the glare mipmap. + // vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; + // Finally store the glare value in the output layer of the glare mipmap. imageStore(uOutGlare, pixelPos, vec4(final_glare_value, 0.0)); } \ No newline at end of file diff --git a/resources/shaders/tonemap.frag b/resources/shaders/tonemap.frag index 85acf50e0..37c967a10 100644 --- a/resources/shaders/tonemap.frag +++ b/resources/shaders/tonemap.frag @@ -48,69 +48,6 @@ vec3 linear_to_srgb(vec3 c) { return vec3(linear_to_srgb(c.r), linear_to_srgb(c.g), linear_to_srgb(c.b)); } -// 4x4 bicubic filter using 4 bilinear texture lookups -// See GPU Gems 2: "Fast Third-Order Texture Filtering", Sigg & Hadwiger: -// http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html - -// w0, w1, w2, and w3 are the four cubic B-spline basis functions -float w0(float a) { - return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); -} - -float w1(float a) { - return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); -} - -float w2(float a) { - return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); -} - -float w3(float a) { - return (1.0 / 6.0) * (a * a * a); -} - -// g0 and g1 are the two amplitude functions -float g0(float a) { - return w0(a) + w1(a); -} - -float g1(float a) { - return w2(a) + w3(a); -} - -// h0 and h1 are the two offset functions -float h0(float a) { - return -1.0 + w1(a) / (w0(a) + w1(a)); -} - -float h1(float a) { - return 1.0 + w3(a) / (w2(a) + w3(a)); -} - -vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { - float lod = float(p_lod); - vec2 tex_size = textureSize(uGlareMipMap, p_lod); - vec2 pixel_size = 1.0 / tex_size; - uv = uv * tex_size + 0.5; - vec2 iuv = floor(uv); - vec2 fuv = fract(uv); - - float g0x = g0(fuv.x); - float g1x = g1(fuv.x); - float h0x = h0(fuv.x); - float h1x = h1(fuv.x); - float h0y = h0(fuv.y); - float h1y = h1(fuv.y); - - vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size; - vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; - - return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + - (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); -} - void main() { #if NUM_MULTISAMPLES > 0 vec3 color = vec3(0.0); @@ -130,35 +67,8 @@ void main() { #endif if (uGlareIntensity > 0) { -#ifdef BICUBIC_GLARE_FILTER - - vec3 glare = texture2D(uGlareMipMap, vTexcoords, 0).rgb; - - color = mix(color, glare, pow(uGlareIntensity, 2.0)); -#else - - - vec3 glare = vec3(0); - float maxLevels = textureQueryLevels(uGlareMipMap); - - float totalWeight = 0; - - // Each level contains a successively more blurred version of the scene. We have to - // accumulate them with an exponentially decreasing weight to get a proper glare distribution. - for (int i = 0; i < maxLevels; ++i) { - float weight = 1.0 / pow(2, i); - - - glare += texture2D(uGlareMipMap, vTexcoords, i).rgb * weight; - - - totalWeight += weight; - } - - // To make sure that we do not add energy, we divide by the total weight. - color = mix(color, glare / totalWeight, pow(uGlareIntensity, 2.0)); -#endif - + vec3 glare = texture2D(uGlareMipMap, vTexcoords, 0).rgb; + color = mix(color, glare, pow(uGlareIntensity, 2.0)); } // Filmic @@ -169,7 +79,7 @@ void main() { // Gamma only #elif TONE_MAPPING_MODE == 1 - oColor = linear_to_srgb(uExposure * color); + oColor = linear_to_srgb(uExposure * color); // None #else diff --git a/src/cs-core/GraphicsEngine.cpp b/src/cs-core/GraphicsEngine.cpp index fcf1bb4ad..d1380a584 100644 --- a/src/cs-core/GraphicsEngine.cpp +++ b/src/cs-core/GraphicsEngine.cpp @@ -188,8 +188,7 @@ GraphicsEngine::GraphicsEngine(std::shared_ptr settings) toneMappingGLNode, static_cast(utils::DrawOrder::eToneMapping)); mSettings->mGraphics.pGlareIntensity.connectAndTouch( - [this](float val) { mToneMappingNode->setGlareIntensity(val); - mHDRBuffer->setGlareIntensity(val);}); + [this](float val) { mToneMappingNode->setGlareIntensity(val); }); mSettings->mGraphics.pGlareQuality.connectAndTouch( [this](uint32_t val) { mHDRBuffer->setGlareQuality(val); }); @@ -203,7 +202,7 @@ GraphicsEngine::GraphicsEngine(std::shared_ptr settings) }); mSettings->mGraphics.pEnableBicubicGlareFilter.connectAndTouch( - [this](bool enable) { mToneMappingNode->setEnableBicubicGlareFilter(enable); }); + [this](bool enable) { mHDRBuffer->setEnableBicubicGlareFilter(enable); }); mSettings->mGraphics.pExposureCompensation.connectAndTouch( [this](float val) { mToneMappingNode->setExposureCompensation(val); }); diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 01d3a567d..21fb17324 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -106,18 +106,18 @@ GlareMipMap::~GlareMipMap() { //////////////////////////////////////////////////////////////////////////////////////////////////// -void GlareMipMap::update( - VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, uint32_t glareQuality, float glareIntensity) { +void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, + uint32_t glareQuality, bool bicubicGlareFilter) { utils::FrameStats::ScopedTimer timer("Compute Glare"); - if (mGlareProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality || glareIntensity != mLastGlareIntensity) { + if (mGlareProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality || + bicubicGlareFilter != mLastGlareBicubic) { // Create the compute shader. std::string source = "#version 430\n"; source += "#define NUM_MULTISAMPLES " + std::to_string(mHDRBufferSamples) + "\n"; source += "#define GLARE_QUALITY " + std::to_string(glareQuality) + "\n"; - source += "#define GLARE_INTENSITY " + std::to_string(glareIntensity) + "\n"; source += "#define MAX_LEVELS " + std::to_string(mMaxLevels) + "\n"; if (glareMode == HDRBuffer::GlareMode::eSymmetricGauss) { @@ -126,8 +126,13 @@ void GlareMipMap::update( source += "#define GLAREMODE_ASYMMETRIC_GAUSS\n"; } + if (bicubicGlareFilter) { + source += "#define BICUBIC_GLARE_FILTER\n"; + } + std::string glareMipMapComputeShaderSource = source; - glareMipMapComputeShaderSource += utils::filesystem::loadToString("../share/resources/shaders/glare.comp"); + glareMipMapComputeShaderSource += + utils::filesystem::loadToString("../share/resources/shaders/glare.comp"); mGlareProgram = createComputeShader(glareMipMapComputeShaderSource); @@ -136,14 +141,14 @@ void GlareMipMap::update( mUniforms.projectionMatrix = glGetUniformLocation(mGlareProgram, "uMatP"); mUniforms.inverseProjectionMatrix = glGetUniformLocation(mGlareProgram, "uMatInvP"); - std::string glareCompositeComputeShaderSource = source; - glareCompositeComputeShaderSource += utils::filesystem::loadToString("../share/resources/shaders/glareComposite.comp"); + glareCompositeComputeShaderSource += + utils::filesystem::loadToString("../share/resources/shaders/glareComposite.comp"); mCompositeProgram = createComputeShader(glareCompositeComputeShaderSource); mLastGlareMode = glareMode; mLastGlareQuality = glareQuality; - mLastGlareIntensity = glareIntensity; + mLastGlareBicubic = bicubicGlareFilter; } // We update the glare mipmap with several passes. First, the base level is filled with a diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index 90126f60e..cd22468fc 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -27,12 +27,12 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { GlareMipMap(GlareMipMap&& other) = delete; GlareMipMap& operator=(GlareMipMap const& other) = delete; - GlareMipMap& operator=(GlareMipMap&& other) = delete; + GlareMipMap& operator=(GlareMipMap&& other) = delete; /// Perform the glare calculation by parallel reduction of the HDR values. This is a costly /// operation and should only be called once a frame. - void update( - VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, uint32_t glareQuality, float glareIntensity); + void update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, + uint32_t glareQuality, bool bicubicGlareFilter); private: GLuint mGlareProgram = 0; @@ -43,7 +43,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { int mHDRBufferHeight = 0; HDRBuffer::GlareMode mLastGlareMode = HDRBuffer::GlareMode::eSymmetricGauss; uint32_t mLastGlareQuality = 0; - float mLastGlareIntensity = 0; + bool mLastGlareBicubic = false; struct { uint32_t level = 0; diff --git a/src/cs-graphics/HDRBuffer.cpp b/src/cs-graphics/HDRBuffer.cpp index 53cbc8342..b30f8ce81 100644 --- a/src/cs-graphics/HDRBuffer.cpp +++ b/src/cs-graphics/HDRBuffer.cpp @@ -262,7 +262,7 @@ void HDRBuffer::updateGlareMipMap() { composite = hdrBuffer.mColorAttachments.at(1).get(); } - hdrBuffer.mGlareMipMap->update(composite, mGlareMode, mGlareQuality, mGlareIntensity); + hdrBuffer.mGlareMipMap->update(composite, mGlareMode, mGlareQuality, mEnableBicubicGlareFilter); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -298,14 +298,14 @@ uint32_t HDRBuffer::getGlareQuality() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -void HDRBuffer::setGlareIntensity(float intensity) { - mGlareIntensity = intensity; +void HDRBuffer::setEnableBicubicGlareFilter(bool enable) { + mEnableBicubicGlareFilter = enable; } //////////////////////////////////////////////////////////////////////////////////////////////////// -float HDRBuffer::getGlareIntensity() const { - return mGlareIntensity; +bool HDRBuffer::getEnableBicubicGlareFilter() const { + return mEnableBicubicGlareFilter; } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cs-graphics/HDRBuffer.hpp b/src/cs-graphics/HDRBuffer.hpp index 629bb28b5..e7fa9bfb3 100644 --- a/src/cs-graphics/HDRBuffer.hpp +++ b/src/cs-graphics/HDRBuffer.hpp @@ -44,7 +44,7 @@ class CS_GRAPHICS_EXPORT HDRBuffer { HDRBuffer(HDRBuffer&& other) = delete; HDRBuffer& operator=(HDRBuffer const& other) = delete; - HDRBuffer& operator=(HDRBuffer&& other) = delete; + HDRBuffer& operator=(HDRBuffer&& other) = delete; virtual ~HDRBuffer(); @@ -90,10 +90,10 @@ class CS_GRAPHICS_EXPORT HDRBuffer { void setGlareQuality(uint32_t quality); uint32_t getGlareQuality() const; - /// Controls the amount of artificial glare. Should be in the range [0-1]. If set to zero, the - /// GlareMipMap will not be updated which will increase performance. - void setGlareIntensity(float intensity); - float getGlareIntensity() const; + /// If enabled, the more expensive but much smoother manual bicubic texture filtering is used + /// for the glare. + void setEnableBicubicGlareFilter(bool enable); + bool getEnableBicubicGlareFilter() const; /// Returns the depth attachment for the currently rendered viewport. Be aware, that this can be /// texture with the target GL_TEXTURE_2D_MULTISAMPLE if getMultiSamples() > 0. @@ -138,7 +138,7 @@ class CS_GRAPHICS_EXPORT HDRBuffer { GlareMode mGlareMode = GlareMode::eSymmetricGauss; uint32_t mGlareQuality = 0; - float mGlareIntensity = 0.F; + bool mEnableBicubicGlareFilter = true; std::unordered_map mHDRBufferData; float mTotalLuminance = 1.F; float mMaximumLuminance = 1.F; diff --git a/src/cs-graphics/ToneMappingNode.cpp b/src/cs-graphics/ToneMappingNode.cpp index 6e2273062..d5aa0df58 100644 --- a/src/cs-graphics/ToneMappingNode.cpp +++ b/src/cs-graphics/ToneMappingNode.cpp @@ -235,21 +235,6 @@ float ToneMappingNode::getGlareIntensity() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -void ToneMappingNode::setEnableBicubicGlareFilter(bool enable) { - if (mEnableBicubicGlareFilter != enable) { - mEnableBicubicGlareFilter = enable; - mShaderDirty = true; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -bool ToneMappingNode::getEnableBicubicGlareFilter() const { - return mEnableBicubicGlareFilter; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - void ToneMappingNode::setToneMappingMode(ToneMappingNode::ToneMappingMode mode) { if (mToneMappingMode != mode) { mToneMappingMode = mode; @@ -293,10 +278,6 @@ bool ToneMappingNode::ToneMappingNode::Do() { std::string defines = "#version 430\n"; defines += "#define NUM_MULTISAMPLES " + std::to_string(mHDRBuffer->getMultiSamples()) + "\n"; - if (mEnableBicubicGlareFilter) { - defines += "#define BICUBIC_GLARE_FILTER\n"; - } - defines += "#define TONE_MAPPING_MODE " + std::to_string(static_cast(mToneMappingMode)) + "\n"; diff --git a/src/cs-graphics/ToneMappingNode.hpp b/src/cs-graphics/ToneMappingNode.hpp index 4e5e7771c..c926b0f96 100644 --- a/src/cs-graphics/ToneMappingNode.hpp +++ b/src/cs-graphics/ToneMappingNode.hpp @@ -39,7 +39,7 @@ class CS_GRAPHICS_EXPORT ToneMappingNode : public IVistaOpenGLDraw, public Vista ToneMappingNode(ToneMappingNode&& other) = delete; ToneMappingNode& operator=(ToneMappingNode const& other) = delete; - ToneMappingNode& operator=(ToneMappingNode&& other) = delete; + ToneMappingNode& operator=(ToneMappingNode&& other) = delete; ~ToneMappingNode() override; @@ -77,11 +77,6 @@ class CS_GRAPHICS_EXPORT ToneMappingNode : public IVistaOpenGLDraw, public Vista void setGlareIntensity(float intensity); float getGlareIntensity() const; - /// If enabled, the more expensive but much smoother manual bicubic texture filtering is used - /// for the glare. - void setEnableBicubicGlareFilter(bool enable); - bool getEnableBicubicGlareFilter() const; - /// Sets the tone mapping mode to be used. void setToneMappingMode(ToneMappingMode mode); ToneMappingMode getToneMappingMode() const; @@ -98,17 +93,16 @@ class CS_GRAPHICS_EXPORT ToneMappingNode : public IVistaOpenGLDraw, public Vista private: std::shared_ptr mHDRBuffer; - bool mShaderDirty = true; - float mExposureCompensation = 0.F; - bool mEnableAutoExposure = false; - float mExposure = 0.F; - float mAutoExposure = 0.F; - float mMinAutoExposure = -15.F; - float mMaxAutoExposure = 15.F; - float mExposureAdaptionSpeed = 1.F; - float mGlareIntensity = 0.F; - bool mEnableBicubicGlareFilter = true; - ToneMappingMode mToneMappingMode = ToneMappingMode::eFilmic; + bool mShaderDirty = true; + float mExposureCompensation = 0.F; + bool mEnableAutoExposure = false; + float mExposure = 0.F; + float mAutoExposure = 0.F; + float mMinAutoExposure = -15.F; + float mMaxAutoExposure = 15.F; + float mExposureAdaptionSpeed = 1.F; + float mGlareIntensity = 0.F; + ToneMappingMode mToneMappingMode = ToneMappingMode::eFilmic; std::unique_ptr mShader; From 956c0ba1926e7f3053f20e1397fcfe0be2e4fd9d Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 12:30:36 +0100 Subject: [PATCH 17/57] :wrench: Use more hardware interpolation in glare computation --- resources/shaders/glare.comp | 64 +++++++++------------------------ src/cs-graphics/GlareMipMap.cpp | 7 ++-- src/cs-graphics/HDRBuffer.cpp | 4 +-- 3 files changed, 22 insertions(+), 53 deletions(-) diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index cc61668a3..6804435c8 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -8,12 +8,12 @@ layout(local_size_x = 16, local_size_y = 16) in; #if NUM_MULTISAMPLES > 0 -layout(rgba32f, binding = 0) readonly uniform image2DMS uInHDRBuffer; +layout(binding = 0) uniform sampler2DMS uInHDRBuffer; #else -layout(rgba32f, binding = 0) readonly uniform image2D uInHDRBuffer; +layout(binding = 0) uniform sampler2D uInHDRBuffer; #endif -layout(binding = 0) uniform sampler2D uInGlareMipMap; +layout(binding = 1) uniform sampler2D uInGlareMipMap; layout(rgba32f, binding = 1) readonly uniform image2D uInGlare; layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; @@ -28,21 +28,18 @@ const float PI = 3.14159265359; // the pixel position in the base layer of the output mipmap pyramid. // For performance reasons, we only use one sample for multisample inputs. vec3 sampleHDRBuffer(ivec2 pos) { - ivec2 pos_times_two = pos << 1; + ivec2 posTimesTwo = pos << 1; #if NUM_MULTISAMPLES > 0 - vec3 col = imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 0)), 0).rgb + - imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 0)), 0).rgb + - imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 1)), 0).rgb + - imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 1)), 0).rgb; + vec3 col = texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(0, 0)), 0).rgb + + texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(1, 0)), 0).rgb + + texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(0, 1)), 0).rgb + + texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(1, 1)), 0).rgb; + return col * 0.25; #else - vec3 col = imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 0))).rgb + - imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 0))).rgb + - imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(0, 1))).rgb + - imageLoad(uInHDRBuffer, ivec2(pos_times_two + ivec2(1, 1))).rgb; + ivec2 size = textureSize(uInHDRBuffer, 0); + return texture(uInHDRBuffer, vec2(posTimesTwo) / size).rgb; #endif - col *= 0.25; - return col; } // Calls the method above four times in order to allow for bilinear interpolation. This is @@ -62,29 +59,10 @@ vec3 sampleHDRBuffer(vec2 pos) { // Makes four texture look-ups in the input layer of the glare mipmap at the four pixels // corresponding to the pixel position in the current layer of the mipmap pyramid. vec3 sampleHigherLevel(ivec2 pos) { - ivec2 pos_times_two = pos << 1; - - - -#if 1 - vec3 col = imageLoad(uInGlare, ivec2(pos_times_two + ivec2(0, 0))).rgb + - imageLoad(uInGlare, ivec2(pos_times_two + ivec2(1, 0))).rgb + - imageLoad(uInGlare, ivec2(pos_times_two + ivec2(0, 1))).rgb + - imageLoad(uInGlare, ivec2(pos_times_two + ivec2(1, 1))).rgb; - col *= 0.25; -#else - - int input_level = uLevel; - if(0 == uPass) { - input_level = max(0, input_level - 1); - } - ivec2 texture_size_of_lod = textureSize(uInGlareMipMap, input_level); - - vec2 normalized_sampling_pos = (pos_times_two + 0.5) / vec2(texture_size_of_lod); - - vec3 col = texture2D(uInGlareMipMap, normalized_sampling_pos, input_level).rgb; -#endif - return col; + ivec2 posTimesTwo = pos << 1; + int inputLevel = uPass == 0 ? max(0, uLevel - 1) : uLevel; + ivec2 size = textureSize(uInGlareMipMap, inputLevel); + return textureLod(uInGlareMipMap, (posTimesTwo + 1.0) / vec2(size), inputLevel).rgb; } // Calls the method above four times in order to allow for bilinear interpolation. This is @@ -105,21 +83,13 @@ vec3 sampleHigherLevel(vec2 pos) { // pixel position. vec3 sampleSameLevel(ivec2 pos) { return texelFetch(uInGlareMipMap, pos, uLevel).rgb; - //return imageLoad(uInGlare, pos).rgb; } // Calls the method above four times in order to allow for bilinear interpolation. This is // required for floating point positions and results in four texture look-ups. vec3 sampleSameLevel(vec2 pos) { - ivec2 ipos = ivec2(pos); - vec3 tl = sampleSameLevel(ipos); - vec3 tr = sampleSameLevel(ipos + ivec2(1, 0)); - vec3 bl = sampleSameLevel(ipos + ivec2(0, 1)); - vec3 br = sampleSameLevel(ipos + ivec2(1, 1)); - vec2 f = fract(pos); - vec3 tA = mix(tl, tr, f.x); - vec3 tB = mix(bl, br, f.x); - return mix(tA, tB, f.y); + ivec2 size = textureSize(uInGlareMipMap, uLevel); + return textureLod(uInGlareMipMap, (pos + 0.5) / vec2(size), uLevel).rgb; } // Rotates the given vector around a given axis. diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 21fb17324..8c2dbe66b 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -159,7 +159,7 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glUseProgram(mGlareProgram); - glBindImageTexture(0, hdrBufferComposite->GetId(), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + hdrBufferComposite->Bind(GL_TEXTURE0); // The asymmetric variant requires the projection and the inverse projection matrices. if (glareMode == HDRBuffer::GlareMode::eAsymmetricGauss) { @@ -208,16 +208,15 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode std::max(1.0, std::floor(static_cast(static_cast(mHDRBufferHeight / 2)) / std::pow(2, level)))); - input->Bind(GL_TEXTURE0); + input->Bind(GL_TEXTURE1); glBindImageTexture(1, input->GetId(), inputLevel, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); glBindImageTexture(2, output->GetId(), outputLevel, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glDispatchCompute(static_cast(std::ceil(1.0 * width / 16)), static_cast(std::ceil(1.0 * height / 16)), 1); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } } - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glUseProgram(mCompositeProgram); mTemporaryTarget->Bind(GL_TEXTURE0); diff --git a/src/cs-graphics/HDRBuffer.cpp b/src/cs-graphics/HDRBuffer.cpp index b30f8ce81..d6e54dff9 100644 --- a/src/cs-graphics/HDRBuffer.cpp +++ b/src/cs-graphics/HDRBuffer.cpp @@ -72,8 +72,8 @@ void HDRBuffer::bind() { glTexImage2DMultisample( target, mMultiSamples, internalFormat, hdrBuffer.mWidth, hdrBuffer.mHeight, false); } else { - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(target, 0, internalFormat, hdrBuffer.mWidth, hdrBuffer.mHeight, 0, format, From 22bfec1bcc2e9cfee18ca9cbfa19dd0367e07b6b Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 12:47:11 +0100 Subject: [PATCH 18/57] :beetle: Fix HDR-buffer lookup --- resources/shaders/glare.comp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index 6804435c8..3a73bf080 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -38,7 +38,7 @@ vec3 sampleHDRBuffer(ivec2 pos) { return col * 0.25; #else ivec2 size = textureSize(uInHDRBuffer, 0); - return texture(uInHDRBuffer, vec2(posTimesTwo) / size).rgb; + return texture(uInHDRBuffer, vec2(posTimesTwo + 1.0) / size).rgb; #endif } From c42d0da12df21777ea55c2a78cc6171b175538c9 Mon Sep 17 00:00:00 2001 From: Nyran Date: Mon, 16 Dec 2024 13:14:03 +0100 Subject: [PATCH 19/57] :wrench: Implement reference exact bicubic filter in glare compositing pass as performance reference --- resources/shaders/glareComposite.comp | 79 ++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index 215419a2b..7da4acfe5 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -14,6 +14,9 @@ layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; // See GPU Gems 2: "Fast Third-Order Texture Filtering", Sigg & Hadwiger: // http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html + + + // w0, w1, w2, and w3 are the four cubic B-spline basis functions float w0(float a) { return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); @@ -49,14 +52,53 @@ float h1(float a) { return 1.0 + w3(a) / (w2(a) + w3(a)); } -vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { +vec3 cubic_filter(float x, vec3 c0, vec3 c1, vec3 c2, vec3 c3) { + vec3 result = c0 * w0(x); + result += c1 * w1(x); + result += c2 * w2(x); + result += c3 * w3(x); + + return result; +} + +vec3 texture2D_exact_bicubic(sampler2D tex, vec2 uv, int p_lod) { float lod = float(p_lod); vec2 tex_size = textureSize(uGlareMipMap, p_lod); vec2 pixel_size = 1.0 / tex_size; uv = uv * tex_size + 0.5; vec2 iuv = floor(uv); + ivec2 iuv_as_int = ivec2(uv - 1.0); vec2 fuv = fract(uv); - + + return cubic_filter( + fuv.y, cubic_filter( + fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1,-1), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, -1), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, -1), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, -1), p_lod).rgb + ), + cubic_filter( + fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, 0), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, 0), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, 0), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, 0), p_lod).rgb + ), + cubic_filter( + fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, 1), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, 1), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, 1), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, 1), p_lod).rgb + ), + cubic_filter( + fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, 2), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, 2), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, 2), p_lod).rgb, + texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, 2), p_lod).rgb + ) + + ); + + /* float g0x = g0(fuv.x); float g1x = g1(fuv.x); float h0x = h0(fuv.x); @@ -71,6 +113,31 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); + */ +} + +vec3 texture2D_fast_bicubic(sampler2D tex, vec2 uv, int p_lod) { + float lod = float(p_lod); + vec2 tex_size = textureSize(uGlareMipMap, p_lod); + vec2 pixel_size = 1.0 / tex_size; + uv = uv * tex_size + 0.5; + vec2 iuv = floor(uv); + vec2 fuv = fract(uv); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; + + return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod).rgb + g1x * textureLod(tex, p1, lod).rgb)) + + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod).rgb + g1x * textureLod(tex, p3, lod).rgb)); } void main() { @@ -95,7 +162,13 @@ void main() { for (int i = 0; i < maxLevels; ++i) { float weight = 1.0 / (1 << i); #ifdef BICUBIC_GLARE_FILTER - glare += texture2D_bicubic(uGlareMipMap, vTexcoords, i).rgb * weight; + +#if 1 + glare += texture2D_fast_bicubic(uGlareMipMap, vTexcoords, i) * weight; +#else + glare += texture2D_exact_bicubic(uGlareMipMap, vTexcoords, i) * weight; +#endif + #else glare += textureLod(uGlareMipMap, vTexcoords, float(i)).rgb * weight; #endif From 9f7121bba98c70f79d8bcec00381a75d705232f8 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 13:35:55 +0100 Subject: [PATCH 20/57] :wrench: Increase default glare quality --- src/cs-core/Settings.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cs-core/Settings.hpp b/src/cs-core/Settings.hpp index b3152ac4c..83da4152c 100644 --- a/src/cs-core/Settings.hpp +++ b/src/cs-core/Settings.hpp @@ -446,7 +446,7 @@ class CS_CORE_EXPORT Settings { utils::DefaultProperty pGlareIntensity{0.2F}; /// Higher values produce a smoother glare. - utils::DefaultProperty pGlareQuality{0}; + utils::DefaultProperty pGlareQuality{1}; /// If enabled, the more expensive but much smoother manual bicubic texture filtering is used /// for the glare. From a3c0601092f645cabd201145c0855ce1efaf3ab2 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 13:38:03 +0100 Subject: [PATCH 21/57] :wrench: Add separate timings for glare computations and glare composition --- src/cs-graphics/GlareMipMap.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 8c2dbe66b..b763f2d9e 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -156,6 +156,8 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode // vertically. Then it's downsampled and horizontally blurred once more. And so on. // In the asymmetric case, its not strictly horizontal and vertical - see the shader above for // details. + { + utils::FrameStats::ScopedTimer timer("Compute Glare"); glUseProgram(mGlareProgram); @@ -212,15 +214,13 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glBindImageTexture(1, input->GetId(), inputLevel, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); glBindImageTexture(2, output->GetId(), outputLevel, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); - glDispatchCompute(static_cast(std::ceil(1.0 * width / 16)), - static_cast(std::ceil(1.0 * height / 16)), 1); - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); - } } + utils::FrameStats::ScopedTimer timer("Composite Glare"); + glUseProgram(mCompositeProgram); mTemporaryTarget->Bind(GL_TEXTURE0); - glBindImageTexture(2, this->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); + glBindImageTexture(2, this->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); glDispatchCompute(static_cast(std::ceil(0.5 * mHDRBufferWidth / 16)), static_cast(std::ceil(0.5 * mHDRBufferHeight / 16)), 1); From 0a5c5ca72ae5bfefb32feb7d67cc89cc4c54e8c4 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 13:38:51 +0100 Subject: [PATCH 22/57] :wrench: Use 16 bit for glare computation --- resources/shaders/glare.comp | 17 ++-- resources/shaders/tonemap.frag | 9 +- src/cs-graphics/GlareMipMap.cpp | 125 +++++++++++++++------------- src/cs-graphics/GlareMipMap.hpp | 3 +- src/cs-graphics/HDRBuffer.cpp | 5 +- src/cs-graphics/HDRBuffer.hpp | 2 +- src/cs-graphics/ToneMappingNode.cpp | 8 +- 7 files changed, 90 insertions(+), 79 deletions(-) diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index 3a73bf080..6d31970bc 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -14,13 +14,14 @@ layout(binding = 0) uniform sampler2D uInHDRBuffer; #endif layout(binding = 1) uniform sampler2D uInGlareMipMap; -layout(rgba32f, binding = 1) readonly uniform image2D uInGlare; -layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; +layout(rgba16f, binding = 1) readonly uniform image2D uInGlare; +layout(rgba16f, binding = 2) writeonly uniform image2D uOutGlare; -uniform int uPass; -uniform int uLevel; -uniform mat4 uMatP; -uniform mat4 uMatInvP; +uniform int uPass; +uniform int uLevel; +uniform float uExposure; +uniform mat4 uMatP; +uniform mat4 uMatInvP; const float PI = 3.14159265359; @@ -35,10 +36,10 @@ vec3 sampleHDRBuffer(ivec2 pos) { texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(1, 0)), 0).rgb + texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(0, 1)), 0).rgb + texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(1, 1)), 0).rgb; - return col * 0.25; + return col * 0.25 * uExposure; #else ivec2 size = textureSize(uInHDRBuffer, 0); - return texture(uInHDRBuffer, vec2(posTimesTwo + 1.0) / size).rgb; + return texture(uInHDRBuffer, vec2(posTimesTwo + 1.0) / size).rgb * uExposure; #endif } diff --git a/resources/shaders/tonemap.frag b/resources/shaders/tonemap.frag index 37c967a10..03d5ccab0 100644 --- a/resources/shaders/tonemap.frag +++ b/resources/shaders/tonemap.frag @@ -66,6 +66,9 @@ void main() { gl_FragDepth = texelFetch(uDepth, ivec2(vTexcoords * textureSize(uDepth, 0)), 0).r; #endif + color *= uExposure; + + // Glare is pre-exposed. if (uGlareIntensity > 0) { vec3 glare = texture2D(uGlareMipMap, vTexcoords, 0).rgb; color = mix(color, glare, pow(uGlareIntensity, 2.0)); @@ -73,16 +76,16 @@ void main() { // Filmic #if TONE_MAPPING_MODE == 2 - color = Uncharted2Tonemap(uExposure * color); + color = Uncharted2Tonemap(color); vec3 whiteScale = vec3(1.0) / Uncharted2Tonemap(vec3(W)); oColor = linear_to_srgb(color * whiteScale); // Gamma only #elif TONE_MAPPING_MODE == 1 - oColor = linear_to_srgb(uExposure * color); + oColor = linear_to_srgb(color); // None #else - oColor = uExposure * color; + oColor = color; #endif } \ No newline at end of file diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index b763f2d9e..0169f4c30 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -84,7 +84,7 @@ GlareMipMap::GlareMipMap(uint32_t hdrBufferSamples, int hdrBufferWidth, int hdrB glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, GL_RGBA32F, iWidth, iHeight); + glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, GL_RGBA16F, iWidth, iHeight); // Create storage for temporary glare target (this is used for the vertical blurring passes). mTemporaryTarget->Bind(); @@ -93,7 +93,7 @@ GlareMipMap::GlareMipMap(uint32_t hdrBufferSamples, int hdrBufferWidth, int hdrB mTemporaryTarget->SetWrapS(GL_CLAMP_TO_EDGE); mTemporaryTarget->SetWrapT(GL_CLAMP_TO_EDGE); - glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, GL_RGBA32F, iWidth, iHeight); + glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, GL_RGBA16F, iWidth, iHeight); mTemporaryTarget->Unbind(); } @@ -106,10 +106,8 @@ GlareMipMap::~GlareMipMap() { //////////////////////////////////////////////////////////////////////////////////////////////////// -void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, - uint32_t glareQuality, bool bicubicGlareFilter) { - - utils::FrameStats::ScopedTimer timer("Compute Glare"); +void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, + HDRBuffer::GlareMode glareMode, uint32_t glareQuality, bool bicubicGlareFilter) { if (mGlareProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality || bicubicGlareFilter != mLastGlareBicubic) { @@ -138,6 +136,7 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode mUniforms.level = glGetUniformLocation(mGlareProgram, "uLevel"); mUniforms.pass = glGetUniformLocation(mGlareProgram, "uPass"); + mUniforms.exposure = glGetUniformLocation(mGlareProgram, "uExposure"); mUniforms.projectionMatrix = glGetUniformLocation(mGlareProgram, "uMatP"); mUniforms.inverseProjectionMatrix = glGetUniformLocation(mGlareProgram, "uMatInvP"); @@ -159,61 +158,67 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode { utils::FrameStats::ScopedTimer timer("Compute Glare"); - glUseProgram(mGlareProgram); - - hdrBufferComposite->Bind(GL_TEXTURE0); + glUseProgram(mGlareProgram); + glUniform1f(mUniforms.exposure, exposure); - // The asymmetric variant requires the projection and the inverse projection matrices. - if (glareMode == HDRBuffer::GlareMode::eAsymmetricGauss) { - std::array glMatP{}; - glGetFloatv(GL_PROJECTION_MATRIX, glMatP.data()); - glm::mat4 matP = glm::make_mat4x4(glMatP.data()); - glm::mat4 matInvP = glm::inverse(matP); - glUniformMatrix4fv(mUniforms.projectionMatrix, 1, GL_FALSE, glm::value_ptr(matP)); - glUniformMatrix4fv(mUniforms.inverseProjectionMatrix, 1, GL_FALSE, glm::value_ptr(matInvP)); - } + hdrBufferComposite->Bind(GL_TEXTURE0); - for (int level(0); level < mMaxLevels; ++level) { - glUniform1i(mUniforms.level, level); - - for (int pass(0); pass < 2; ++pass) { - VistaTexture* input = this; - VistaTexture* output = this; - int inputLevel = level; - int outputLevel = level; - - // level pass input inputLevel output outputLevel blur samplesHigherLevel - // 0 0 hdrbuffer 0 this 0 horizontal true - // 0 1 this 0 temp 0 vertical false - // 1 0 temp 0 this 1 horizontal true - // 1 1 this 1 temp 1 vertical false - // 2 0 temp 1 this 2 horizontal true - // 2 1 this 2 temp 2 vertical false - // 3 0 temp 2 this 3 horizontal true - // 3 1 this 3 temp 3 vertical false - - if (pass == 0) { - input = mTemporaryTarget; - inputLevel = std::max(0, inputLevel - 1); - } + // The asymmetric variant requires the projection and the inverse projection matrices. + if (glareMode == HDRBuffer::GlareMode::eAsymmetricGauss) { + std::array glMatP{}; + glGetFloatv(GL_PROJECTION_MATRIX, glMatP.data()); + glm::mat4 matP = glm::make_mat4x4(glMatP.data()); + glm::mat4 matInvP = glm::inverse(matP); + glUniformMatrix4fv(mUniforms.projectionMatrix, 1, GL_FALSE, glm::value_ptr(matP)); + glUniformMatrix4fv(mUniforms.inverseProjectionMatrix, 1, GL_FALSE, glm::value_ptr(matInvP)); + } - if (pass == 1) { - output = mTemporaryTarget; + for (int level(0); level < mMaxLevels; ++level) { + glUniform1i(mUniforms.level, level); + + for (int pass(0); pass < 2; ++pass) { + VistaTexture* input = this; + VistaTexture* output = this; + int inputLevel = level; + int outputLevel = level; + + // level pass input inputLevel output outputLevel blur samplesHigherLevel + // 0 0 hdrbuffer 0 this 0 horizontal true + // 0 1 this 0 temp 0 vertical false + // 1 0 temp 0 this 1 horizontal true + // 1 1 this 1 temp 1 vertical false + // 2 0 temp 1 this 2 horizontal true + // 2 1 this 2 temp 2 vertical false + // 3 0 temp 2 this 3 horizontal true + // 3 1 this 3 temp 3 vertical false + + if (pass == 0) { + input = mTemporaryTarget; + inputLevel = std::max(0, inputLevel - 1); + } + + if (pass == 1) { + output = mTemporaryTarget; + } + + glUniform1i(mUniforms.pass, pass); + + int width = static_cast( + std::max(1.0, std::floor(static_cast(static_cast(mHDRBufferWidth / 2)) / + std::pow(2, level)))); + int height = static_cast( + std::max(1.0, std::floor(static_cast(static_cast(mHDRBufferHeight / 2)) / + std::pow(2, level)))); + + input->Bind(GL_TEXTURE1); + glBindImageTexture(1, input->GetId(), inputLevel, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F); + glBindImageTexture(2, output->GetId(), outputLevel, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + + glDispatchCompute(static_cast(std::ceil(1.0 * width / 16)), + static_cast(std::ceil(1.0 * height / 16)), 1); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } - - glUniform1i(mUniforms.pass, pass); - - int width = static_cast( - std::max(1.0, std::floor(static_cast(static_cast(mHDRBufferWidth / 2)) / - std::pow(2, level)))); - int height = static_cast( - std::max(1.0, std::floor(static_cast(static_cast(mHDRBufferHeight / 2)) / - std::pow(2, level)))); - - input->Bind(GL_TEXTURE1); - glBindImageTexture(1, input->GetId(), inputLevel, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); - glBindImageTexture(2, output->GetId(), outputLevel, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); - + } } utils::FrameStats::ScopedTimer timer("Composite Glare"); @@ -224,9 +229,9 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glDispatchCompute(static_cast(std::ceil(0.5 * mHDRBufferWidth / 16)), static_cast(std::ceil(0.5 * mHDRBufferHeight / 16)), 1); - glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); - glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); - glBindImageTexture(2, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F); + glBindImageTexture(2, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index cd22468fc..98d62551f 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -31,7 +31,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { /// Perform the glare calculation by parallel reduction of the HDR values. This is a costly /// operation and should only be called once a frame. - void update(VistaTexture* hdrBufferComposite, HDRBuffer::GlareMode glareMode, + void update(VistaTexture* hdrBufferComposite, float exposure, HDRBuffer::GlareMode glareMode, uint32_t glareQuality, bool bicubicGlareFilter); private: @@ -48,6 +48,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { struct { uint32_t level = 0; uint32_t pass = 0; + uint32_t exposure = 0; uint32_t projectionMatrix = 0; uint32_t inverseProjectionMatrix = 0; } mUniforms; diff --git a/src/cs-graphics/HDRBuffer.cpp b/src/cs-graphics/HDRBuffer.cpp index d6e54dff9..32e1ce696 100644 --- a/src/cs-graphics/HDRBuffer.cpp +++ b/src/cs-graphics/HDRBuffer.cpp @@ -252,7 +252,7 @@ float HDRBuffer::getMaximumLuminance() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -void HDRBuffer::updateGlareMipMap() { +void HDRBuffer::updateGlareMipMap(float exposure) { auto& hdrBuffer = getCurrentHDRBuffer(); VistaTexture* composite = nullptr; @@ -262,7 +262,8 @@ void HDRBuffer::updateGlareMipMap() { composite = hdrBuffer.mColorAttachments.at(1).get(); } - hdrBuffer.mGlareMipMap->update(composite, mGlareMode, mGlareQuality, mEnableBicubicGlareFilter); + hdrBuffer.mGlareMipMap->update( + composite, exposure, mGlareMode, mGlareQuality, mEnableBicubicGlareFilter); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cs-graphics/HDRBuffer.hpp b/src/cs-graphics/HDRBuffer.hpp index e7fa9bfb3..6243a8f93 100644 --- a/src/cs-graphics/HDRBuffer.hpp +++ b/src/cs-graphics/HDRBuffer.hpp @@ -77,7 +77,7 @@ class CS_GRAPHICS_EXPORT HDRBuffer { float getMaximumLuminance() const; /// Update and access the GlareMipMap. - void updateGlareMipMap(); + void updateGlareMipMap(float exposure); VistaTexture* getGlareMipMap() const; /// Specifies how the glare should be computed. diff --git a/src/cs-graphics/ToneMappingNode.cpp b/src/cs-graphics/ToneMappingNode.cpp index d5aa0df58..8c18080da 100644 --- a/src/cs-graphics/ToneMappingNode.cpp +++ b/src/cs-graphics/ToneMappingNode.cpp @@ -321,16 +321,16 @@ bool ToneMappingNode::ToneMappingNode::Do() { } } - if (mGlareIntensity > 0) { - mHDRBuffer->updateGlareMipMap(); - } - if (doCalculateExposure && mEnableAutoExposure) { mExposure = glm::clamp(mAutoExposure, mMinAutoExposure, mMaxAutoExposure); } float exposure = std::pow(2.F, mExposure + mExposureCompensation); + if (mGlareIntensity > 0) { + mHDRBuffer->updateGlareMipMap(exposure); + } + mHDRBuffer->unbind(); mHDRBuffer->getCurrentWriteAttachment()->Bind(GL_TEXTURE0); mHDRBuffer->getDepthAttachment()->Bind(GL_TEXTURE1); From 6f93e2aa113365034cd0679e83e4902346e337bb Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2024 15:21:13 +0100 Subject: [PATCH 23/57] :wrench: Make 32bit glare optional --- resources/gui/cosmoscout.html | 10 ++++ resources/shaders/glare.comp | 6 +++ src/cosmoscout/Application.cpp | 8 ++++ src/cs-core/GraphicsEngine.cpp | 3 ++ src/cs-core/Settings.cpp | 6 ++- src/cs-core/Settings.hpp | 3 ++ src/cs-graphics/GlareMipMap.cpp | 83 ++++++++++++++++++++------------- src/cs-graphics/GlareMipMap.hpp | 3 +- src/cs-graphics/HDRBuffer.cpp | 14 +++++- src/cs-graphics/HDRBuffer.hpp | 5 ++ 10 files changed, 105 insertions(+), 36 deletions(-) diff --git a/resources/gui/cosmoscout.html b/resources/gui/cosmoscout.html index c9263edc2..e366500d9 100644 --- a/resources/gui/cosmoscout.html +++ b/resources/gui/cosmoscout.html @@ -553,6 +553,16 @@ +
+
+ +
+
+
Tone Mapping Mode diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index 6d31970bc..b55c65061 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -14,8 +14,14 @@ layout(binding = 0) uniform sampler2D uInHDRBuffer; #endif layout(binding = 1) uniform sampler2D uInGlareMipMap; + +#ifdef ENABLE_32BIT_GLARE +layout(rgba32f, binding = 1) readonly uniform image2D uInGlare; +layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; +#else layout(rgba16f, binding = 1) readonly uniform image2D uInGlare; layout(rgba16f, binding = 2) writeonly uniform image2D uOutGlare; +#endif uniform int uPass; uniform int uLevel; diff --git a/src/cosmoscout/Application.cpp b/src/cosmoscout/Application.cpp index 3eb38ffa7..ae55a08c2 100644 --- a/src/cosmoscout/Application.cpp +++ b/src/cosmoscout/Application.cpp @@ -1142,6 +1142,14 @@ void Application::registerGuiCallbacks() { mSettings->mGraphics.pGlareQuality.connect( [this](uint32_t val) { mGuiManager->setSliderValue("graphics.setGlareQuality", val); }); + // Enables 32bit glare computation. + mGuiManager->getGui()->registerCallback("graphics.setEnable32BitGlare", + "Enables or disables 32bit glare computation.", + std::function([this](bool enable) { mSettings->mGraphics.pEnable32BitGlare = enable; })); + mSettings->mGraphics.pEnable32BitGlare.connectAndTouch([this](bool enable) { + mGuiManager->setCheckboxValue("graphics.setEnable32BitGlare", enable); + }); + // Enables bicubic glare filtering. mGuiManager->getGui()->registerCallback("graphics.setEnableBicubicGlareFilter", "Enables or disables bicubic glare filtering.", std::function([this](bool enable) { diff --git a/src/cs-core/GraphicsEngine.cpp b/src/cs-core/GraphicsEngine.cpp index d1380a584..e5fc8313a 100644 --- a/src/cs-core/GraphicsEngine.cpp +++ b/src/cs-core/GraphicsEngine.cpp @@ -204,6 +204,9 @@ GraphicsEngine::GraphicsEngine(std::shared_ptr settings) mSettings->mGraphics.pEnableBicubicGlareFilter.connectAndTouch( [this](bool enable) { mHDRBuffer->setEnableBicubicGlareFilter(enable); }); + mSettings->mGraphics.pEnable32BitGlare.connectAndTouch( + [this](bool enable) { mHDRBuffer->setEnable32BitGlare(enable); }); + mSettings->mGraphics.pExposureCompensation.connectAndTouch( [this](float val) { mToneMappingNode->setExposureCompensation(val); }); diff --git a/src/cs-core/Settings.cpp b/src/cs-core/Settings.cpp index 5c16e1ccc..04c0f904d 100644 --- a/src/cs-core/Settings.cpp +++ b/src/cs-core/Settings.cpp @@ -317,7 +317,8 @@ void from_json(nlohmann::json const& j, Settings::Graphics& o) { Settings::deserialize(j, "ambientBrightness", o.pAmbientBrightness); Settings::deserialize(j, "ambientOcclusion", o.pAmbientOcclusion); Settings::deserialize(j, "glareIntensity", o.pGlareIntensity); - Settings::deserialize(j, "glareRadius", o.pGlareQuality); + Settings::deserialize(j, "glareQuality", o.pGlareQuality); + Settings::deserialize(j, "enable32BitGlare", o.pEnable32BitGlare); Settings::deserialize(j, "glareMode", o.pGlareMode); Settings::deserialize(j, "toneMappingMode", o.pToneMappingMode); Settings::deserialize(j, "enableBicubicGlareFiltering", o.pEnableBicubicGlareFilter); @@ -353,7 +354,8 @@ void to_json(nlohmann::json& j, Settings::Graphics const& o) { Settings::serialize(j, "ambientBrightness", o.pAmbientBrightness); Settings::serialize(j, "ambientOcclusion", o.pAmbientOcclusion); Settings::serialize(j, "glareIntensity", o.pGlareIntensity); - Settings::serialize(j, "glareRadius", o.pGlareQuality); + Settings::serialize(j, "glareQuality", o.pGlareQuality); + Settings::serialize(j, "enable32BitGlare", o.pEnable32BitGlare); Settings::serialize(j, "glareMode", o.pGlareMode); Settings::serialize(j, "toneMappingMode", o.pToneMappingMode); Settings::serialize(j, "enableBicubicGlareFiltering", o.pEnableBicubicGlareFilter); diff --git a/src/cs-core/Settings.hpp b/src/cs-core/Settings.hpp index 83da4152c..2a657413b 100644 --- a/src/cs-core/Settings.hpp +++ b/src/cs-core/Settings.hpp @@ -448,6 +448,9 @@ class CS_CORE_EXPORT Settings { /// Higher values produce a smoother glare. utils::DefaultProperty pGlareQuality{1}; + /// If set to true, the glare will be computed in 32 bit precision. + utils::DefaultProperty pEnable32BitGlare{false}; + /// If enabled, the more expensive but much smoother manual bicubic texture filtering is used /// for the glare. utils::DefaultProperty pEnableBicubicGlareFilter{true}; diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index 0169f4c30..bc76aa43e 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -66,35 +66,13 @@ GlareMipMap::GlareMipMap(uint32_t hdrBufferSamples, int hdrBufferWidth, int hdrB : VistaTexture(GL_TEXTURE_2D) , mHDRBufferSamples(hdrBufferSamples) , mHDRBufferWidth(hdrBufferWidth) - , mHDRBufferHeight(hdrBufferHeight) - , mTemporaryTarget(new VistaTexture(GL_TEXTURE_2D)) { + , mHDRBufferHeight(hdrBufferHeight) { - // Create glare mipmap storage. The texture has half the size of the HDR buffer (rounded down) in - // both directions. + // Compute the number of available mipmap levels. int iWidth = mHDRBufferWidth / 2; int iHeight = mHDRBufferHeight / 2; - - // Compute the number of available mipmap levels. mMaxLevels = static_cast(std::max(1.0, std::floor(std::log2(std::max(iWidth, iHeight))) + 1)); - - Bind(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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); - - glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, GL_RGBA16F, iWidth, iHeight); - - // Create storage for temporary glare target (this is used for the vertical blurring passes). - mTemporaryTarget->Bind(); - mTemporaryTarget->SetMinFilter(GL_LINEAR_MIPMAP_LINEAR); - mTemporaryTarget->SetMagFilter(GL_LINEAR); - mTemporaryTarget->SetWrapS(GL_CLAMP_TO_EDGE); - mTemporaryTarget->SetWrapT(GL_CLAMP_TO_EDGE); - - glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, GL_RGBA16F, iWidth, iHeight); - mTemporaryTarget->Unbind(); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -107,10 +85,44 @@ GlareMipMap::~GlareMipMap() { //////////////////////////////////////////////////////////////////////////////////////////////////// void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, - HDRBuffer::GlareMode glareMode, uint32_t glareQuality, bool bicubicGlareFilter) { + HDRBuffer::GlareMode glareMode, uint32_t glareQuality, bool bicubicGlareFilter, + bool enable32BitGlare) { + + GLint internalFormat = enable32BitGlare ? GL_RGBA32F : GL_RGBA16F; + + if (mLast32BitGlare != enable32BitGlare || mTemporaryTarget == nullptr) { + // Create glare mipmap storage. The texture has half the size of the HDR buffer (rounded down) + // in both directions. + int iWidth = mHDRBufferWidth / 2; + int iHeight = mHDRBufferHeight / 2; + + glDeleteTextures(1, &m_uiId); + glGenTextures(1, &m_uiId); + + Bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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); + glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, internalFormat, iWidth, iHeight); + + if (mTemporaryTarget != nullptr) { + delete mTemporaryTarget; + } + + // Create storage for temporary glare target (this is used for the vertical blurring passes). + mTemporaryTarget = new VistaTexture(GL_TEXTURE_2D); + mTemporaryTarget->Bind(); + mTemporaryTarget->SetMinFilter(GL_LINEAR_MIPMAP_LINEAR); + mTemporaryTarget->SetMagFilter(GL_LINEAR); + mTemporaryTarget->SetWrapS(GL_CLAMP_TO_EDGE); + mTemporaryTarget->SetWrapT(GL_CLAMP_TO_EDGE); + glTexStorage2D(GL_TEXTURE_2D, mMaxLevels, internalFormat, iWidth, iHeight); + mTemporaryTarget->Unbind(); + } if (mGlareProgram == 0 || glareMode != mLastGlareMode || glareQuality != mLastGlareQuality || - bicubicGlareFilter != mLastGlareBicubic) { + bicubicGlareFilter != mLastGlareBicubic || mLast32BitGlare != enable32BitGlare) { // Create the compute shader. std::string source = "#version 430\n"; @@ -128,6 +140,10 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, source += "#define BICUBIC_GLARE_FILTER\n"; } + if (enable32BitGlare) { + source += "#define ENABLE_32BIT_GLARE\n"; + } + std::string glareMipMapComputeShaderSource = source; glareMipMapComputeShaderSource += utils::filesystem::loadToString("../share/resources/shaders/glare.comp"); @@ -148,6 +164,7 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, mLastGlareMode = glareMode; mLastGlareQuality = glareQuality; mLastGlareBicubic = bicubicGlareFilter; + mLast32BitGlare = enable32BitGlare; } // We update the glare mipmap with several passes. First, the base level is filled with a @@ -211,8 +228,10 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, std::pow(2, level)))); input->Bind(GL_TEXTURE1); - glBindImageTexture(1, input->GetId(), inputLevel, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F); - glBindImageTexture(2, output->GetId(), outputLevel, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glBindImageTexture( + 1, input->GetId(), inputLevel, GL_FALSE, 0, GL_READ_ONLY, internalFormat); + glBindImageTexture( + 2, output->GetId(), outputLevel, GL_FALSE, 0, GL_WRITE_ONLY, internalFormat); glDispatchCompute(static_cast(std::ceil(1.0 * width / 16)), static_cast(std::ceil(1.0 * height / 16)), 1); @@ -225,13 +244,13 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, glUseProgram(mCompositeProgram); mTemporaryTarget->Bind(GL_TEXTURE0); - glBindImageTexture(2, this->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glBindImageTexture(2, this->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, internalFormat); glDispatchCompute(static_cast(std::ceil(0.5 * mHDRBufferWidth / 16)), static_cast(std::ceil(0.5 * mHDRBufferHeight / 16)), 1); - glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F); - glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F); - glBindImageTexture(2, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F); + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, internalFormat); + glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_READ_ONLY, internalFormat); + glBindImageTexture(2, 0, 0, GL_FALSE, 0, GL_READ_ONLY, internalFormat); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index 98d62551f..9313c7ad6 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -32,7 +32,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { /// Perform the glare calculation by parallel reduction of the HDR values. This is a costly /// operation and should only be called once a frame. void update(VistaTexture* hdrBufferComposite, float exposure, HDRBuffer::GlareMode glareMode, - uint32_t glareQuality, bool bicubicGlareFilter); + uint32_t glareQuality, bool enableBicubicGlareFilter, bool enable32BitGlare); private: GLuint mGlareProgram = 0; @@ -44,6 +44,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { HDRBuffer::GlareMode mLastGlareMode = HDRBuffer::GlareMode::eSymmetricGauss; uint32_t mLastGlareQuality = 0; bool mLastGlareBicubic = false; + bool mLast32BitGlare = false; struct { uint32_t level = 0; diff --git a/src/cs-graphics/HDRBuffer.cpp b/src/cs-graphics/HDRBuffer.cpp index 32e1ce696..3f826bf6d 100644 --- a/src/cs-graphics/HDRBuffer.cpp +++ b/src/cs-graphics/HDRBuffer.cpp @@ -263,7 +263,7 @@ void HDRBuffer::updateGlareMipMap(float exposure) { } hdrBuffer.mGlareMipMap->update( - composite, exposure, mGlareMode, mGlareQuality, mEnableBicubicGlareFilter); + composite, exposure, mGlareMode, mGlareQuality, mEnableBicubicGlareFilter, mEnable32BitGlare); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -311,4 +311,16 @@ bool HDRBuffer::getEnableBicubicGlareFilter() const { //////////////////////////////////////////////////////////////////////////////////////////////////// +void HDRBuffer::setEnable32BitGlare(bool enable) { + mEnable32BitGlare = enable; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +bool HDRBuffer::getEnable32BitGlare() const { + return mEnable32BitGlare; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + } // namespace cs::graphics diff --git a/src/cs-graphics/HDRBuffer.hpp b/src/cs-graphics/HDRBuffer.hpp index 6243a8f93..16361318d 100644 --- a/src/cs-graphics/HDRBuffer.hpp +++ b/src/cs-graphics/HDRBuffer.hpp @@ -95,6 +95,10 @@ class CS_GRAPHICS_EXPORT HDRBuffer { void setEnableBicubicGlareFilter(bool enable); bool getEnableBicubicGlareFilter() const; + /// If enabled, the glare will be computed in 32 bit precision. + void setEnable32BitGlare(bool enable); + bool getEnable32BitGlare() const; + /// Returns the depth attachment for the currently rendered viewport. Be aware, that this can be /// texture with the target GL_TEXTURE_2D_MULTISAMPLE if getMultiSamples() > 0. VistaTexture* getDepthAttachment() const; @@ -139,6 +143,7 @@ class CS_GRAPHICS_EXPORT HDRBuffer { GlareMode mGlareMode = GlareMode::eSymmetricGauss; uint32_t mGlareQuality = 0; bool mEnableBicubicGlareFilter = true; + bool mEnable32BitGlare = false; std::unordered_map mHDRBufferData; float mTotalLuminance = 1.F; float mMaximumLuminance = 1.F; From 9ada6b17f7328c5b24bc1d80659e561b5ffd49f0 Mon Sep 17 00:00:00 2001 From: Nyran Date: Mon, 16 Dec 2024 23:59:41 +0100 Subject: [PATCH 24/57] :wrench: Implement initial alternative bicubic interpolation paths in glareComposite compute shader --- resources/shaders/glareComposite.comp | 123 ++++++++++++++++++++------ 1 file changed, 98 insertions(+), 25 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index 7da4acfe5..a7e334da4 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -61,18 +61,98 @@ vec3 cubic_filter(float x, vec3 c0, vec3 c1, vec3 c2, vec3 c3) { return result; } -vec3 texture2D_exact_bicubic(sampler2D tex, vec2 uv, int p_lod) { + +const int NUM_PADDING_CELLS_OFFSET = 1; +const int NUM_PADDING_CELLS_IN_TOTAL = 3; +const int NUM_SHARED_MEMORY_CELLS_ONE_D = 16+NUM_PADDING_CELLS_IN_TOTAL; +shared vec3 sharedGlareSamples[NUM_SHARED_MEMORY_CELLS_ONE_D][NUM_SHARED_MEMORY_CELLS_ONE_D]; +const int totalNumSharedSamples = NUM_SHARED_MEMORY_CELLS_ONE_D*NUM_SHARED_MEMORY_CELLS_ONE_D; +const int totalNumThreadsInWorkgroup = 16 * 16; + + +vec3 texture2D_exact_bicubic_with_shared_memory(sampler2D tex, vec2 uv, int p_lod) { float lod = float(p_lod); vec2 tex_size = textureSize(uGlareMipMap, p_lod); - vec2 pixel_size = 1.0 / tex_size; uv = uv * tex_size + 0.5; vec2 iuv = floor(uv); ivec2 iuv_as_int = ivec2(uv - 1.0); vec2 fuv = fract(uv); + + + ivec2 globalWorkgroupStartOffset = ivec2(gl_WorkGroupID.xy * (gl_WorkGroupSize.xy) ) ;// * (tex_size / textureSize(uGlareMipMap, 0)) ); + + + + barrier(); + + int total_num_cells_to_sample_per_level = (( int(gl_WorkGroupSize.x) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL) * + (( int(gl_WorkGroupSize.y) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL); + + for(int flatGlareSampleWriteIndex = int(gl_LocalInvocationIndex); + flatGlareSampleWriteIndex < totalNumSharedSamples; + flatGlareSampleWriteIndex += total_num_cells_to_sample_per_level) { + + ivec2 unflattenedGlareSampleWriteIndex = + ivec2(flatGlareSampleWriteIndex % NUM_SHARED_MEMORY_CELLS_ONE_D, + flatGlareSampleWriteIndex / NUM_SHARED_MEMORY_CELLS_ONE_D); + + //calculate the offset of the shared sample array with respect to the threads local position + ivec2 globalCellID = max(ivec2(0),(unflattenedGlareSampleWriteIndex + globalWorkgroupStartOffset) - NUM_PADDING_CELLS_OFFSET); + + vec2 vTexcoords = (globalCellID + vec2(0.5)) / textureSize(uGlareMipMap, 0); + + vTexcoords = vTexcoords * tex_size + 0.5; + ivec2 iuv_as_int = ivec2(vTexcoords - 1.0); + sharedGlareSamples[unflattenedGlareSampleWriteIndex.x][unflattenedGlareSampleWriteIndex.y] + = texelFetch(uGlareMipMap, iuv_as_int, p_lod).rgb; + + + } + + barrier(); + + ivec2 shared_memory_sampling_base_offset = (ivec2(gl_LocalInvocationID.xy)) + NUM_PADDING_CELLS_OFFSET; + + return cubic_filter( + fuv.y, cubic_filter( + fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y - 1], + sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y - 1], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y - 1], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y - 1] + ), + cubic_filter( + fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y ], + sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y ], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y ], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y ] + ), + cubic_filter( + fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y + 1], + sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y + 1], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y + 1], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y + 1] + ), + cubic_filter( + fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y + 2], + sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y + 2], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y + 2], + sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y + 2] + ) + ); +} + +vec3 texture2D_exact_bicubic(sampler2D tex, vec2 uv, int p_lod) { + float lod = float(p_lod); + vec2 tex_size = textureSize(uGlareMipMap, p_lod); + uv = uv * tex_size - 0.5; + vec2 iuv = floor(uv); + ivec2 iuv_as_int = ivec2(uv); + vec2 fuv = fract(uv); + return cubic_filter( fuv.y, cubic_filter( - fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1,-1), p_lod).rgb, + fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, -1), p_lod).rgb, texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, -1), p_lod).rgb, texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, -1), p_lod).rgb, texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, -1), p_lod).rgb @@ -97,23 +177,6 @@ vec3 texture2D_exact_bicubic(sampler2D tex, vec2 uv, int p_lod) { ) ); - - /* - float g0x = g0(fuv.x); - float g1x = g1(fuv.x); - float h0x = h0(fuv.x); - float h1x = h1(fuv.x); - float h0y = h0(fuv.y); - float h1y = h1(fuv.y); - - vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size; - vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; - - return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + - (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); - */ } vec3 texture2D_fast_bicubic(sampler2D tex, vec2 uv, int p_lod) { @@ -163,11 +226,21 @@ void main() { float weight = 1.0 / (1 << i); #ifdef BICUBIC_GLARE_FILTER -#if 1 - glare += texture2D_fast_bicubic(uGlareMipMap, vTexcoords, i) * weight; -#else - glare += texture2D_exact_bicubic(uGlareMipMap, vTexcoords, i) * weight; -#endif + //#define USE_EXACT_PATH_WITHOUT_SHARED_MEMORY + //#define USE_EXACT_PATH_WITH_SHARED_MEMORY + #define USE_APPROXIMATE_FAST_PATH + + #ifdef USE_APPROXIMATE_FAST_PATH + glare += texture2D_fast_bicubic(uGlareMipMap, vTexcoords, i) * weight; + #else + + #ifdef USE_EXACT_PATH_WITHOUT_SHARED_MEMORY + glare += texture2D_exact_bicubic(uGlareMipMap, vTexcoords, i) * weight; + #else + glare += texture2D_exact_bicubic_with_shared_memory(uGlareMipMap, vTexcoords, i) * weight; + #endif + + #endif #else glare += textureLod(uGlareMipMap, vTexcoords, float(i)).rgb * weight; From e84896158cecf55b81337260cdb01400316e9490 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 17 Dec 2024 08:00:09 +0100 Subject: [PATCH 25/57] :wrench: Scale glare by maximum luminance instead of exposure --- resources/shaders/glare.comp | 6 +++--- resources/shaders/tonemap.frag | 11 ++++++----- src/cs-graphics/GlareMipMap.cpp | 6 +++--- src/cs-graphics/GlareMipMap.hpp | 4 ++-- src/cs-graphics/HDRBuffer.cpp | 6 +++--- src/cs-graphics/HDRBuffer.hpp | 2 +- src/cs-graphics/ToneMappingNode.cpp | 30 +++++++++++++++-------------- src/cs-graphics/ToneMappingNode.hpp | 1 + 8 files changed, 35 insertions(+), 31 deletions(-) diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index b55c65061..51df5badd 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -25,7 +25,7 @@ layout(rgba16f, binding = 2) writeonly uniform image2D uOutGlare; uniform int uPass; uniform int uLevel; -uniform float uExposure; +uniform float uMaxLuminance; uniform mat4 uMatP; uniform mat4 uMatInvP; @@ -42,10 +42,10 @@ vec3 sampleHDRBuffer(ivec2 pos) { texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(1, 0)), 0).rgb + texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(0, 1)), 0).rgb + texelFetch(uInHDRBuffer, ivec2(posTimesTwo + ivec2(1, 1)), 0).rgb; - return col * 0.25 * uExposure; + return col * 0.25 / uMaxLuminance * 65500; #else ivec2 size = textureSize(uInHDRBuffer, 0); - return texture(uInHDRBuffer, vec2(posTimesTwo + 1.0) / size).rgb * uExposure; + return texture(uInHDRBuffer, vec2(posTimesTwo + 1.0) / size).rgb / uMaxLuminance * 65500; #endif } diff --git a/resources/shaders/tonemap.frag b/resources/shaders/tonemap.frag index 03d5ccab0..d688d1ed4 100644 --- a/resources/shaders/tonemap.frag +++ b/resources/shaders/tonemap.frag @@ -20,6 +20,7 @@ layout(binding = 1) uniform sampler2D uDepth; layout(binding = 2) uniform sampler2D uGlareMipMap; uniform float uExposure; +uniform float uMaxLuminance; uniform float uGlareIntensity; layout(location = 0) out vec3 oColor; @@ -66,14 +67,14 @@ void main() { gl_FragDepth = texelFetch(uDepth, ivec2(vTexcoords * textureSize(uDepth, 0)), 0).r; #endif - color *= uExposure; - - // Glare is pre-exposed. + // Glare is scaled by max luminance. if (uGlareIntensity > 0) { - vec3 glare = texture2D(uGlareMipMap, vTexcoords, 0).rgb; + vec3 glare = texture(uGlareMipMap, vTexcoords, 0).rgb * uMaxLuminance / 65500; color = mix(color, glare, pow(uGlareIntensity, 2.0)); } + color *= uExposure; + // Filmic #if TONE_MAPPING_MODE == 2 color = Uncharted2Tonemap(color); @@ -82,7 +83,7 @@ void main() { // Gamma only #elif TONE_MAPPING_MODE == 1 - oColor = linear_to_srgb(color); + oColor = linear_to_srgb(color); // None #else diff --git a/src/cs-graphics/GlareMipMap.cpp b/src/cs-graphics/GlareMipMap.cpp index bc76aa43e..2b2963c2d 100644 --- a/src/cs-graphics/GlareMipMap.cpp +++ b/src/cs-graphics/GlareMipMap.cpp @@ -84,7 +84,7 @@ GlareMipMap::~GlareMipMap() { //////////////////////////////////////////////////////////////////////////////////////////////////// -void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, +void GlareMipMap::update(VistaTexture* hdrBufferComposite, float maxLuminance, HDRBuffer::GlareMode glareMode, uint32_t glareQuality, bool bicubicGlareFilter, bool enable32BitGlare) { @@ -152,7 +152,7 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, mUniforms.level = glGetUniformLocation(mGlareProgram, "uLevel"); mUniforms.pass = glGetUniformLocation(mGlareProgram, "uPass"); - mUniforms.exposure = glGetUniformLocation(mGlareProgram, "uExposure"); + mUniforms.maxLuminance = glGetUniformLocation(mGlareProgram, "uMaxLuminance"); mUniforms.projectionMatrix = glGetUniformLocation(mGlareProgram, "uMatP"); mUniforms.inverseProjectionMatrix = glGetUniformLocation(mGlareProgram, "uMatInvP"); @@ -176,7 +176,7 @@ void GlareMipMap::update(VistaTexture* hdrBufferComposite, float exposure, utils::FrameStats::ScopedTimer timer("Compute Glare"); glUseProgram(mGlareProgram); - glUniform1f(mUniforms.exposure, exposure); + glUniform1f(mUniforms.maxLuminance, maxLuminance); hdrBufferComposite->Bind(GL_TEXTURE0); diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index 9313c7ad6..f1bc95000 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -31,7 +31,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { /// Perform the glare calculation by parallel reduction of the HDR values. This is a costly /// operation and should only be called once a frame. - void update(VistaTexture* hdrBufferComposite, float exposure, HDRBuffer::GlareMode glareMode, + void update(VistaTexture* hdrBufferComposite, float maxLuminance, HDRBuffer::GlareMode glareMode, uint32_t glareQuality, bool enableBicubicGlareFilter, bool enable32BitGlare); private: @@ -49,7 +49,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { struct { uint32_t level = 0; uint32_t pass = 0; - uint32_t exposure = 0; + uint32_t maxLuminance = 0; uint32_t projectionMatrix = 0; uint32_t inverseProjectionMatrix = 0; } mUniforms; diff --git a/src/cs-graphics/HDRBuffer.cpp b/src/cs-graphics/HDRBuffer.cpp index 3f826bf6d..fc3113b0a 100644 --- a/src/cs-graphics/HDRBuffer.cpp +++ b/src/cs-graphics/HDRBuffer.cpp @@ -252,7 +252,7 @@ float HDRBuffer::getMaximumLuminance() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -void HDRBuffer::updateGlareMipMap(float exposure) { +void HDRBuffer::updateGlareMipMap(float maxLuminance) { auto& hdrBuffer = getCurrentHDRBuffer(); VistaTexture* composite = nullptr; @@ -262,8 +262,8 @@ void HDRBuffer::updateGlareMipMap(float exposure) { composite = hdrBuffer.mColorAttachments.at(1).get(); } - hdrBuffer.mGlareMipMap->update( - composite, exposure, mGlareMode, mGlareQuality, mEnableBicubicGlareFilter, mEnable32BitGlare); + hdrBuffer.mGlareMipMap->update(composite, maxLuminance, mGlareMode, mGlareQuality, + mEnableBicubicGlareFilter, mEnable32BitGlare); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cs-graphics/HDRBuffer.hpp b/src/cs-graphics/HDRBuffer.hpp index 16361318d..2a848a010 100644 --- a/src/cs-graphics/HDRBuffer.hpp +++ b/src/cs-graphics/HDRBuffer.hpp @@ -77,7 +77,7 @@ class CS_GRAPHICS_EXPORT HDRBuffer { float getMaximumLuminance() const; /// Update and access the GlareMipMap. - void updateGlareMipMap(float exposure); + void updateGlareMipMap(float maxLuminance); VistaTexture* getGlareMipMap() const; /// Specifies how the glare should be computed. diff --git a/src/cs-graphics/ToneMappingNode.cpp b/src/cs-graphics/ToneMappingNode.cpp index 8c18080da..6132efec1 100644 --- a/src/cs-graphics/ToneMappingNode.cpp +++ b/src/cs-graphics/ToneMappingNode.cpp @@ -290,16 +290,17 @@ bool ToneMappingNode::ToneMappingNode::Do() { mShader->Link(); mUniforms.exposure = mShader->GetUniformLocation("uExposure"); + mUniforms.maxLuminance = mShader->GetUniformLocation("uMaxLuminance"); mUniforms.glareIntensity = mShader->GetUniformLocation("uGlareIntensity"); mShaderDirty = false; } - bool doCalculateExposure = - GetVistaSystem()->GetDisplayManager()->GetCurrentRenderInfo()->m_eEyeRenderMode != - VistaDisplayManager::RenderInfo::ERM_RIGHT; + bool leftEye = GetVistaSystem()->GetDisplayManager()->GetCurrentRenderInfo()->m_eEyeRenderMode != + VistaDisplayManager::RenderInfo::ERM_RIGHT; + bool calculateFrameLuminance = leftEye && (mEnableAutoExposure || mGlareIntensity > 0); - if (doCalculateExposure && mEnableAutoExposure) { + if (calculateFrameLuminance) { mHDRBuffer->calculateLuminance(); // We accumulate all luminance values of this frame (can be multiple viewports and / or multiple @@ -313,22 +314,22 @@ bool ToneMappingNode::ToneMappingNode::Do() { // Calculate exposure based on last frame's average luminance Time-dependent visual adaptation // for fast realistic image display (https://dl.acm.org/citation.cfm?id=344810). - if (mGlobalLuminanceData.mPixelCount > 0 && mGlobalLuminanceData.mTotalLuminance > 0) { - auto frameTime = static_cast(GetVistaSystem()->GetFrameLoop()->GetAverageLoopTime()); - float averageLuminance = getLastAverageLuminance(); - mAutoExposure += (std::log2(1.F / averageLuminance) - mAutoExposure) * - (1.F - std::exp(-mExposureAdaptionSpeed * frameTime)); + if (mEnableAutoExposure) { + if (mGlobalLuminanceData.mPixelCount > 0 && mGlobalLuminanceData.mTotalLuminance > 0) { + auto frameTime = static_cast(GetVistaSystem()->GetFrameLoop()->GetAverageLoopTime()); + float averageLuminance = getLastAverageLuminance(); + mAutoExposure += (std::log2(1.F / averageLuminance) - mAutoExposure) * + (1.F - std::exp(-mExposureAdaptionSpeed * frameTime)); + } + + mExposure = glm::clamp(mAutoExposure, mMinAutoExposure, mMaxAutoExposure); } } - if (doCalculateExposure && mEnableAutoExposure) { - mExposure = glm::clamp(mAutoExposure, mMinAutoExposure, mMaxAutoExposure); - } - float exposure = std::pow(2.F, mExposure + mExposureCompensation); if (mGlareIntensity > 0) { - mHDRBuffer->updateGlareMipMap(exposure); + mHDRBuffer->updateGlareMipMap(mGlobalLuminanceData.mMaximumLuminance); } mHDRBuffer->unbind(); @@ -338,6 +339,7 @@ bool ToneMappingNode::ToneMappingNode::Do() { mShader->Bind(); mShader->SetUniform(mUniforms.exposure, exposure); + mShader->SetUniform(mUniforms.maxLuminance, mGlobalLuminanceData.mMaximumLuminance); mShader->SetUniform(mUniforms.glareIntensity, mGlareIntensity); glDrawArrays(GL_TRIANGLES, 0, 3); diff --git a/src/cs-graphics/ToneMappingNode.hpp b/src/cs-graphics/ToneMappingNode.hpp index c926b0f96..8ff5f1c48 100644 --- a/src/cs-graphics/ToneMappingNode.hpp +++ b/src/cs-graphics/ToneMappingNode.hpp @@ -114,6 +114,7 @@ class CS_GRAPHICS_EXPORT ToneMappingNode : public IVistaOpenGLDraw, public Vista struct { uint32_t exposure = 0; + uint32_t maxLuminance = 0; uint32_t glareIntensity = 0; uint32_t glareQuality = 0; } mUniforms; From 08426a050368baf69af6ec43184a84307f430574 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 17 Dec 2024 10:20:28 +0100 Subject: [PATCH 26/57] :wrench: Tweak new star mode --- plugins/csp-stars/gui/stars_settings.html | 2 +- plugins/csp-stars/shaders/starsBillboard.frag | 8 ++++---- plugins/csp-stars/shaders/starsBillboard.geom | 2 +- plugins/csp-stars/src/Plugin.hpp | 2 +- plugins/csp-stars/src/Stars.hpp | 2 +- resources/gui/cosmoscout.html | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/csp-stars/gui/stars_settings.html b/plugins/csp-stars/gui/stars_settings.html index 6298e68f4..0a149de03 100644 --- a/plugins/csp-stars/gui/stars_settings.html +++ b/plugins/csp-stars/gui/stars_settings.html @@ -65,7 +65,7 @@
diff --git a/plugins/csp-stars/shaders/starsBillboard.frag b/plugins/csp-stars/shaders/starsBillboard.frag index 1cf553d64..a9adda9f1 100644 --- a/plugins/csp-stars/shaders/starsBillboard.frag +++ b/plugins/csp-stars/shaders/starsBillboard.frag @@ -42,10 +42,10 @@ void main() { #endif #ifdef DRAWMODE_GLARE_DISC - float scaleFac = mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); + float scaleFac = mapRange(sqrt(luminance), 0, 5, 1.0, 100.0); - // In this mode, 90% of the brightness is drawn using a small smooth disc in the center - // (just like DRAWMODE_SMOOTH_DISC) and the othe 10% are drawn as glare using an inverse + // In this mode, 20% of the brightness is drawn using a small smooth disc in the center + // (just like DRAWMODE_SMOOTH_DISC) and the other 80% are drawn as glare using an inverse // quadratic falloff. // The billboard is scaled depending on the magnitude, but we want the disc to be the same @@ -55,7 +55,7 @@ void main() { float falloff = max(0, 0.5 / pow(dist + 0.1, 2) - 0.5); float glare = luminance * falloff / (scaleFac * scaleFac) * 0.5; - float fac = 0.5 * glare + 0.5 * disc; + float fac = 0.2 * glare + 0.8 * disc; #endif #ifdef DRAWMODE_SPRITE diff --git a/plugins/csp-stars/shaders/starsBillboard.geom b/plugins/csp-stars/shaders/starsBillboard.geom index 2a30a1e08..d21cba435 100644 --- a/plugins/csp-stars/shaders/starsBillboard.geom +++ b/plugins/csp-stars/shaders/starsBillboard.geom @@ -51,7 +51,7 @@ void main() { #ifdef DRAWMODE_GLARE_DISC float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); - scale *= mapRange(sqrt(luminance), 0, 5, 1.0, 500.0); + scale *= mapRange(sqrt(luminance), 0, 5, 1.0, 100.0); #endif #ifdef DRAWMODE_SPRITE diff --git a/plugins/csp-stars/src/Plugin.hpp b/plugins/csp-stars/src/Plugin.hpp index 4ac638e1f..aa3b4a84a 100644 --- a/plugins/csp-stars/src/Plugin.hpp +++ b/plugins/csp-stars/src/Plugin.hpp @@ -37,7 +37,7 @@ class Plugin : public cs::core::PluginBase { cs::utils::DefaultProperty mEnableCelestialGrid{false}; cs::utils::DefaultProperty mEnableStarFigures{false}; cs::utils::DefaultProperty mLuminanceMultiplicator{0.F}; - cs::utils::DefaultProperty mDrawMode{Stars::DrawMode::eSmoothDisc}; + cs::utils::DefaultProperty mDrawMode{Stars::DrawMode::eGlareDisc}; cs::utils::DefaultProperty mSize{0.05F}; cs::utils::DefaultProperty mMagnitudeRange{glm::vec2(-5.F, 15.F)}; }; diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index 3eba2e9b0..eb91ee974 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -172,7 +172,7 @@ class Stars : public IVistaOpenGLDraw { std::vector mStars; std::map mCatalogs; - DrawMode mDrawMode = DrawMode::eScaledDisc; + DrawMode mDrawMode = DrawMode::eGlareDisc; bool mShaderDirty = true; bool mEnableHDR = true; diff --git a/resources/gui/cosmoscout.html b/resources/gui/cosmoscout.html index e366500d9..97fff3f72 100644 --- a/resources/gui/cosmoscout.html +++ b/resources/gui/cosmoscout.html @@ -935,7 +935,7 @@ CosmoScout.gui.initSlider("graphics.setAmbientOcclusion", 0.0, 1.0, 0.001, [0.5]); CosmoScout.gui.initSlider("graphics.setExposureRange", -30.0, 30, 0.1, [-12, 9]); CosmoScout.gui.initSlider("graphics.setGlareIntensity", 0.0, 1, 0.01, [0.2]); - CosmoScout.gui.initSlider("graphics.setGlareQuality", 0, 5, 1, [0]); + CosmoScout.gui.initSlider("graphics.setGlareQuality", 0, 5, 1, [1]); $(document).on('click', '.item-create-button', function () { $(this).addClass('active'); From 78f261b6109256dec77ee39367d9a65f69444fb2 Mon Sep 17 00:00:00 2001 From: Nyran Date: Wed, 18 Dec 2024 02:00:28 +0100 Subject: [PATCH 27/57] :wrench: Fix main artefacts with respect to shared memory --- resources/shaders/glareComposite.comp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index a7e334da4..d409bfe3e 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -80,7 +80,7 @@ vec3 texture2D_exact_bicubic_with_shared_memory(sampler2D tex, vec2 uv, int p_lo - ivec2 globalWorkgroupStartOffset = ivec2(gl_WorkGroupID.xy * (gl_WorkGroupSize.xy) ) ;// * (tex_size / textureSize(uGlareMipMap, 0)) ); + ivec2 globalWorkgroupStartOffset = ivec2(gl_WorkGroupID.xy * (gl_WorkGroupSize.xy) / (1 << p_lod) ) ; @@ -90,17 +90,17 @@ vec3 texture2D_exact_bicubic_with_shared_memory(sampler2D tex, vec2 uv, int p_lo (( int(gl_WorkGroupSize.y) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL); for(int flatGlareSampleWriteIndex = int(gl_LocalInvocationIndex); - flatGlareSampleWriteIndex < totalNumSharedSamples; - flatGlareSampleWriteIndex += total_num_cells_to_sample_per_level) { + flatGlareSampleWriteIndex < total_num_cells_to_sample_per_level; + flatGlareSampleWriteIndex += totalNumThreadsInWorkgroup) { ivec2 unflattenedGlareSampleWriteIndex = - ivec2(flatGlareSampleWriteIndex % NUM_SHARED_MEMORY_CELLS_ONE_D, - flatGlareSampleWriteIndex / NUM_SHARED_MEMORY_CELLS_ONE_D); + ivec2(flatGlareSampleWriteIndex % (( int(gl_WorkGroupSize.x) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL), + flatGlareSampleWriteIndex / (( int(gl_WorkGroupSize.x) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL)); //calculate the offset of the shared sample array with respect to the threads local position ivec2 globalCellID = max(ivec2(0),(unflattenedGlareSampleWriteIndex + globalWorkgroupStartOffset) - NUM_PADDING_CELLS_OFFSET); - vec2 vTexcoords = (globalCellID + vec2(0.5)) / textureSize(uGlareMipMap, 0); + vec2 vTexcoords = (globalCellID + vec2(0.5)) / textureSize(uGlareMipMap, p_lod); vTexcoords = vTexcoords * tex_size + 0.5; ivec2 iuv_as_int = ivec2(vTexcoords - 1.0); @@ -112,7 +112,7 @@ vec3 texture2D_exact_bicubic_with_shared_memory(sampler2D tex, vec2 uv, int p_lo barrier(); - ivec2 shared_memory_sampling_base_offset = (ivec2(gl_LocalInvocationID.xy)) + NUM_PADDING_CELLS_OFFSET; + ivec2 shared_memory_sampling_base_offset = (ivec2(gl_LocalInvocationID.xy / (1 << p_lod) )) + NUM_PADDING_CELLS_OFFSET; return cubic_filter( fuv.y, cubic_filter( From 06e4ab19db63f5692f54e471229a8b1cb3f2a675 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 18 Dec 2024 16:30:19 +0100 Subject: [PATCH 28/57] :tada: Add experimental software rasterized stars --- plugins/csp-stars/gui/stars_settings.html | 6 ++ plugins/csp-stars/shaders/starsSRBlit.frag | 23 ++++ plugins/csp-stars/shaders/starsSRBlit.vert | 13 +++ plugins/csp-stars/shaders/starsSRPoint.comp | 114 ++++++++++++++++++++ plugins/csp-stars/src/Plugin.cpp | 5 + plugins/csp-stars/src/Stars.cpp | 97 ++++++++++++++++- plugins/csp-stars/src/Stars.hpp | 18 +++- 7 files changed, 270 insertions(+), 6 deletions(-) create mode 100644 plugins/csp-stars/shaders/starsSRBlit.frag create mode 100644 plugins/csp-stars/shaders/starsSRBlit.vert create mode 100644 plugins/csp-stars/shaders/starsSRPoint.comp diff --git a/plugins/csp-stars/gui/stars_settings.html b/plugins/csp-stars/gui/stars_settings.html index 0a149de03..b16befea9 100644 --- a/plugins/csp-stars/gui/stars_settings.html +++ b/plugins/csp-stars/gui/stars_settings.html @@ -75,6 +75,12 @@ Textured Sprites
+
+ +
diff --git a/plugins/csp-stars/shaders/starsSRBlit.frag b/plugins/csp-stars/shaders/starsSRBlit.frag new file mode 100644 index 000000000..590b49b9f --- /dev/null +++ b/plugins/csp-stars/shaders/starsSRBlit.frag @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +in vec2 vTexcoords; + +layout(pixel_center_integer) in vec4 gl_FragCoord; + +layout(binding = 0) uniform sampler2D uR; +layout(binding = 1) uniform sampler2D uG; +layout(binding = 2) uniform sampler2D uB; + +layout(location = 0) out vec3 oColor; + +void main() { + vec3 color = + vec3(texture(uR, vTexcoords).r, texture(uG, vTexcoords).r, texture(uB, vTexcoords).r); + + oColor = color; +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsSRBlit.vert b/plugins/csp-stars/shaders/starsSRBlit.vert new file mode 100644 index 000000000..f35a53e8f --- /dev/null +++ b/plugins/csp-stars/shaders/starsSRBlit.vert @@ -0,0 +1,13 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +out vec2 vTexcoords; + +void main() { + vTexcoords = vec2(gl_VertexID & 2, (gl_VertexID << 1) & 2); + gl_Position = vec4(vTexcoords * 2.0 - 1.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp new file mode 100644 index 000000000..276f3d30f --- /dev/null +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -0,0 +1,114 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This file is part of CosmoScout VR // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: German Aerospace Center (DLR) +// SPDX-License-Identifier: MIT + +#line 9 + +layout(local_size_x = 256) in; + +layout(r32f, binding = 0) coherent uniform image2D uOutRed; +layout(r32f, binding = 1) coherent uniform image2D uOutGreen; +layout(r32f, binding = 2) coherent uniform image2D uOutBlue; + +// input SSBO +struct Star { + float posX; + float posY; + float posZ; + float colorR; + float colorG; + float colorB; + float absMagnitude; +}; + +layout(std430, binding = 0) buffer StarSSBO { + Star stars[]; +}; + +// uniforms +uniform int uStarCount; +uniform mat4 uMatMV; +uniform mat4 uMatP; +uniform mat4 uInvMV; + +uniform float uLuminanceMultiplicator; +uniform mat4 uInvP; +uniform vec2 uResolution; +uniform float uMinMagnitude; +uniform float uMaxMagnitude; +uniform float uSolidAngle; + +float getSolidAngle(vec3 a, vec3 b, vec3 c) { + return 2 * atan(abs(dot(a, cross(b, c))) / (1 + dot(a, b) + dot(a, c) + dot(b, c))); +} + +float getSolidAngleOfPixel(vec4 screenSpacePosition, vec2 resolution, mat4 invProjection) { + vec2 pixel = vec2(1.0) / resolution; + vec4 pixelCorners[4] = vec4[4](screenSpacePosition + vec4(-pixel.x, -pixel.y, 0, 0), + screenSpacePosition + vec4(+pixel.x, -pixel.y, 0, 0), + screenSpacePosition + vec4(+pixel.x, +pixel.y, 0, 0), + screenSpacePosition + vec4(-pixel.x, +pixel.y, 0, 0)); + + for (int i = 0; i < 4; ++i) { + pixelCorners[i] = invProjection * pixelCorners[i]; + pixelCorners[i].xyz = normalize(pixelCorners[i].xyz); + } + + return getSolidAngle(pixelCorners[0].xyz, pixelCorners[1].xyz, pixelCorners[2].xyz) + + getSolidAngle(pixelCorners[0].xyz, pixelCorners[2].xyz, pixelCorners[3].xyz); +} + +void main() { + int index = int(gl_GlobalInvocationID.x); + + // Discard any threads outside the output layer. + if (index >= uStarCount) { + return; + } + + const float parsecToMeter = 3.08567758e16; + vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; + + Star star = stars[index]; + vec3 inPos = vec3(star.posX, star.posY, star.posZ); + vec3 inColor = vec3(star.colorR, star.colorG, star.colorB); + float inAbsMagnitude = star.absMagnitude; + + float vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); + vec3 vColor = SRGBtoLINEAR(inColor); + vec4 vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); + + if (vScreenSpacePos.w <= 0) { + return; + } + + vScreenSpacePos /= vScreenSpacePos.w; + + if (vScreenSpacePos.x < -1 || vScreenSpacePos.x > 1 || vScreenSpacePos.y < -1 || + vScreenSpacePos.y > 1) { + return; + } + + if (vMagnitude > uMaxMagnitude || vMagnitude < uMinMagnitude) { + return; + } + + float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); + float luminance = magnitudeToLuminance(vMagnitude, solidAngle); + + vec4 oLuminance = vec4(vColor * luminance * uLuminanceMultiplicator, 1.0); + +#ifndef ENABLE_HDR + oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); +#endif + + ivec2 outSize = imageSize(uOutRed); + ivec2 coords = ivec2(((vScreenSpacePos.xy + 1) / 2) * vec2(outSize)); + + imageAtomicAdd(uOutRed, coords, oLuminance.r); + imageAtomicAdd(uOutGreen, coords, oLuminance.g); + imageAtomicAdd(uOutBlue, coords, oLuminance.b); +} \ No newline at end of file diff --git a/plugins/csp-stars/src/Plugin.cpp b/plugins/csp-stars/src/Plugin.cpp index 33a42c08c..dda0e76bd 100644 --- a/plugins/csp-stars/src/Plugin.cpp +++ b/plugins/csp-stars/src/Plugin.cpp @@ -171,6 +171,9 @@ void Plugin::init() { mGuiManager->getGui()->registerCallback("stars.setDrawMode6", "Enables sprite draw mode for the stars.", std::function([this]() { mPluginSettings.mDrawMode = Stars::DrawMode::eSprite; })); + mGuiManager->getGui()->registerCallback("stars.setDrawMode7", + "Enables software rasterized point draw mode for the stars.", + std::function([this]() { mPluginSettings.mDrawMode = Stars::DrawMode::eSRPoint; })); mPluginSettings.mDrawMode.connectAndTouch([this](Stars::DrawMode drawMode) { if (drawMode == Stars::DrawMode::ePoint) { mGuiManager->setRadioChecked("stars.setDrawMode0"); @@ -186,6 +189,8 @@ void Plugin::init() { mGuiManager->setRadioChecked("stars.setDrawMode5"); } else if (drawMode == Stars::DrawMode::eSprite) { mGuiManager->setRadioChecked("stars.setDrawMode6"); + } else if (drawMode == Stars::DrawMode::eSRPoint) { + mGuiManager->setRadioChecked("stars.setDrawMode7"); } }); diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index ecae1610f..26d1f44c4 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -19,9 +19,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -85,6 +87,32 @@ const int Stars::cCacheVersion = 3; //////////////////////////////////////////////////////////////////////////////////////////////////// +Stars::Stars() { + + for (auto const& viewport : GetVistaSystem()->GetDisplayManager()->GetViewports()) { + SoftwareRasterizerTargets srTarget; + + int width, height; + viewport.second->GetViewportProperties()->GetSize(width, height); + + srTarget.mR = std::make_unique(GL_TEXTURE_2D); + srTarget.mR->Bind(); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); + + srTarget.mG = std::make_unique(GL_TEXTURE_2D); + srTarget.mG->Bind(); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); + + srTarget.mB = std::make_unique(GL_TEXTURE_2D); + srTarget.mB->Bind(); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); + + mSRTargets.emplace(viewport.second, std::move(srTarget)); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + void Stars::setCatalogs(std::map catalogs) { if (mCatalogs != catalogs) { @@ -333,7 +361,14 @@ bool Stars::Do() { VistaTransformMatrix matProjection(glMat.data(), true); if (mShaderDirty) { - std::string defines = "#version 330\n"; + std::string defines; + + if (mDrawMode == DrawMode::eSRPoint) { + defines += "#version 430\n"; + defines += "#extension GL_NV_shader_atomic_float : enable\n"; + } else { + defines += "#version 330\n"; + } if (mEnableHDR) { defines += "#define ENABLE_HDR\n"; @@ -353,6 +388,8 @@ bool Stars::Do() { defines += "#define DRAWMODE_GLARE_DISC\n"; } else if (mDrawMode == DrawMode::eSprite) { defines += "#define DRAWMODE_SPRITE\n"; + } else if (mDrawMode == DrawMode::eSRPoint) { + defines += "#define DRAWMODE_SRPOINT\n"; } defines += cs::utils::filesystem::loadToString("../share/resources/shaders/starSnippets.glsl"); @@ -365,6 +402,19 @@ bool Stars::Do() { mStarShader.InitFragmentShaderFromString( defines + cs::utils::filesystem::loadToString("../share/resources/shaders/starsOnePixel.frag")); + } else if (mDrawMode == DrawMode::eSRPoint) { + mStarShader.InitComputeShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsSRPoint.comp")); + + mSRBlitShader = VistaGLSLShader(); + mSRBlitShader.InitVertexShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsSRBlit.vert")); + mSRBlitShader.InitFragmentShaderFromString( + defines + + cs::utils::filesystem::loadToString("../share/resources/shaders/starsSRBlit.frag")); + mSRBlitShader.Link(); } else { mStarShader.InitVertexShaderFromString( defines + @@ -405,6 +455,10 @@ bool Stars::Do() { mUniforms.starInverseMVMatrix = mStarShader.GetUniformLocation("uInvMV"); mUniforms.starInversePMatrix = mStarShader.GetUniformLocation("uInvP"); + if (mDrawMode == DrawMode::eSRPoint) { + mUniforms.starCount = mStarShader.GetUniformLocation("uStarCount"); + } + mShaderDirty = false; } @@ -452,8 +506,13 @@ bool Stars::Do() { mBackgroundVAO.Release(); } - // draw stars - mStarVAO.Bind(); + // Draw stars. In software rasterization mode, we need to bind the VBO as SSBO. + if (mDrawMode == DrawMode::eSRPoint) { + mStarVBO.BindBufferBase(GL_SHADER_STORAGE_BUFFER, 0); + } else { + mStarVAO.Bind(); + } + mStarShader.Bind(); if (mDrawMode == DrawMode::ePoint || mDrawMode == DrawMode::eSmoothPoint) { @@ -492,12 +551,40 @@ bool Stars::Do() { glUniformMatrix4fv(mUniforms.starInverseMVMatrix, 1, GL_FALSE, matInverseMV.GetData()); glUniformMatrix4fv(mUniforms.starInversePMatrix, 1, GL_FALSE, matInverseP.GetData()); - glDrawArrays(GL_POINTS, 0, static_cast(mStars.size())); + if (mDrawMode == DrawMode::eSRPoint) { + glUniform1i(mUniforms.starCount, static_cast(mStars.size())); + + auto* viewport = GetVistaSystem()->GetDisplayManager()->GetCurrentRenderInfo()->m_pViewport; + auto const& data = mSRTargets[viewport]; + + glClearTexImage(data.mR->GetId(), 0, GL_RED, GL_FLOAT, nullptr); + glClearTexImage(data.mG->GetId(), 0, GL_RED, GL_FLOAT, nullptr); + glClearTexImage(data.mB->GetId(), 0, GL_RED, GL_FLOAT, nullptr); + + glBindImageTexture(0, data.mR->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + glBindImageTexture(1, data.mG->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + glBindImageTexture(2, data.mB->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + + glDispatchCompute(static_cast(std::ceil(1.0 * mStars.size() / 256)), 1, 1); + + // Blit the result to the screen. + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + mSRBlitShader.Bind(); + data.mR->Bind(GL_TEXTURE0); + data.mG->Bind(GL_TEXTURE1); + data.mB->Bind(GL_TEXTURE2); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + } else { + glDrawArrays(GL_POINTS, 0, static_cast(mStars.size())); + mStarVAO.Release(); + } mStarTexture->Unbind(GL_TEXTURE0); mStarShader.Release(); - mStarVAO.Release(); glDepthMask(GL_TRUE); glPopAttrib(); diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index eb91ee974..0236aa310 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -9,6 +9,7 @@ #define CSP_STARS_VISTA_STARS_HPP #include +#include #include #include #include @@ -55,9 +56,13 @@ class Stars : public IVistaOpenGLDraw { eSmoothDisc, eScaledDisc, eGlareDisc, - eSprite + eSprite, + eSRPoint }; + Stars(); + ~Stars() = default; + /// It is possible to load multiple catalogs, currently Hipparcos and any of Tycho or Tycho2 can /// be loaded together. Stars which are in both catalogs will be loaded from Hipparcos. Once /// loaded, the stars will be written to a binary cache file. Subsequent instantiations of this @@ -161,6 +166,7 @@ class Stars : public IVistaOpenGLDraw { std::string mCacheFile = "star_cache.dat"; VistaGLSLShader mStarShader; + VistaGLSLShader mSRBlitShader; VistaGLSLShader mBackgroundShader; VistaColor mBackgroundColor1; VistaColor mBackgroundColor2; @@ -199,8 +205,18 @@ class Stars : public IVistaOpenGLDraw { uint32_t starPMatrix = 0; uint32_t starInverseMVMatrix = 0; uint32_t starInversePMatrix = 0; + + uint32_t starCount = 0; } mUniforms; + struct SoftwareRasterizerTargets { + std::unique_ptr mR; + std::unique_ptr mG; + std::unique_ptr mB; + }; + + std::unordered_map mSRTargets; + static const int cCacheVersion; static constexpr size_t NUM_CATALOGS = cs::utils::enumCast(CatalogType::eCount); From 7d4cea309d61648304efca29227ccf005b8b67b8 Mon Sep 17 00:00:00 2001 From: Nyran Date: Wed, 18 Dec 2024 18:23:10 +0100 Subject: [PATCH 29/57] :wrench: Filter within csp-star plugin for shader files to improve IDE compatability --- plugins/csp-stars/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/csp-stars/CMakeLists.txt b/plugins/csp-stars/CMakeLists.txt index bbb96696e..7a9d50c9b 100644 --- a/plugins/csp-stars/CMakeLists.txt +++ b/plugins/csp-stars/CMakeLists.txt @@ -19,10 +19,17 @@ file(GLOB SOURCE_FILES src/*.cpp) file(GLOB HEADER_FILES src/*.hpp) file(GLOB_RECURSE RESOUCRE_FILES gui/* textures/*) +# Shader files are also only added in order to make them available in your IDE. +set(CSP_STARS_SHADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/shaders/) +file(GLOB SHADER_FILES ${CSP_STARS_SHADER_DIR}* ${CSP_STARS_SHADER_DIR}*/*) + +source_group("src/shaders" FILES ${SHADER_FILES}) + add_library(csp-stars SHARED ${SOURCE_FILES} ${HEADER_FILES} ${RESOUCRE_FILES} + ${SHADER_FILES} ) target_link_libraries(csp-stars From b34248c9097b08792c40178dbc26e57b6f4759c0 Mon Sep 17 00:00:00 2001 From: Nyran Date: Wed, 18 Dec 2024 18:23:53 +0100 Subject: [PATCH 30/57] :wrench: Distribute software-rasterized stars' luminance to four closest pixels --- plugins/csp-stars/shaders/starsSRPoint.comp | 42 +++++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index 276f3d30f..61f5e27d2 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -106,9 +106,43 @@ void main() { #endif ivec2 outSize = imageSize(uOutRed); - ivec2 coords = ivec2(((vScreenSpacePos.xy + 1) / 2) * vec2(outSize)); + + vec2 float_star_coords = ((vScreenSpacePos.xy + 1) / 2) * vec2(outSize); + + + //shift the exact projected star coordinates by -.5 such that one refers to the four nearest pixels in the following + vec2 ref_weights = float_star_coords - 0.5; + ivec2 integer_ref_star_coords = ivec2(ref_weights); + ref_weights = fract(ref_weights); - imageAtomicAdd(uOutRed, coords, oLuminance.r); - imageAtomicAdd(uOutGreen, coords, oLuminance.g); - imageAtomicAdd(uOutBlue, coords, oLuminance.b); + //four nearest pixels + ivec2 target_star_coords[4] = {integer_ref_star_coords , integer_ref_star_coords + ivec2(1, 0), + integer_ref_star_coords + ivec2(0, 1), integer_ref_star_coords + ivec2(1, 1)}; + + //weighting related to bilinear interpolation, but not correct apparently, since the perceived luminance is odd + float pixel_weights[4] = {(1.0 - ref_weights.x) * (1.0 - ref_weights.y), + (ref_weights.x) * (1.0 - ref_weights.y), + (1.0 - ref_weights.x) * (ref_weights.y), + (ref_weights.x) * ( ref_weights.y)}; + + //calculate accumulated weights for subsequent weight normalization + float accumulated_weights = 0.0; + for(int i = 0; i < 4; ++i) { + accumulated_weights += pixel_weights[i]; + } + + //normalize weights and add + for(int i = 0; i < 4; ++i) { + pixel_weights[i] /= accumulated_weights; + + ivec2 current_target_position = target_star_coords[i]; + + vec3 oPixelWeightedLuminance = pixel_weights[i] * oLuminance.rgb; + + imageAtomicAdd(uOutRed, current_target_position, oPixelWeightedLuminance.r); + imageAtomicAdd(uOutGreen, current_target_position, oPixelWeightedLuminance.g); + imageAtomicAdd(uOutBlue, current_target_position, oPixelWeightedLuminance.b); + } + + } \ No newline at end of file From 7ddb50438eb1d2c52e4a3ba38b74335b053bcbcf Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 05:51:54 +0100 Subject: [PATCH 31/57] :wrench: Use nearest neighbour filtering --- plugins/csp-stars/src/Stars.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index 26d1f44c4..b03aa9ed4 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -97,14 +97,26 @@ Stars::Stars() { srTarget.mR = std::make_unique(GL_TEXTURE_2D); srTarget.mR->Bind(); + srTarget.mR->SetWrapS(GL_CLAMP_TO_EDGE); + srTarget.mR->SetWrapT(GL_CLAMP_TO_EDGE); + srTarget.mR->SetMinFilter(GL_NEAREST); + srTarget.mR->SetMagFilter(GL_NEAREST); glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); srTarget.mG = std::make_unique(GL_TEXTURE_2D); srTarget.mG->Bind(); + srTarget.mG->SetWrapS(GL_CLAMP_TO_EDGE); + srTarget.mG->SetWrapT(GL_CLAMP_TO_EDGE); + srTarget.mG->SetMinFilter(GL_NEAREST); + srTarget.mG->SetMagFilter(GL_NEAREST); glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); srTarget.mB = std::make_unique(GL_TEXTURE_2D); srTarget.mB->Bind(); + srTarget.mB->SetWrapS(GL_CLAMP_TO_EDGE); + srTarget.mB->SetWrapT(GL_CLAMP_TO_EDGE); + srTarget.mB->SetMinFilter(GL_NEAREST); + srTarget.mB->SetMagFilter(GL_NEAREST); glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); mSRTargets.emplace(viewport.second, std::move(srTarget)); From b10b1b0941221799b3d2856d75a8f6e886846ceb Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 05:53:12 +0100 Subject: [PATCH 32/57] :fire: Remove weight normalization --- plugins/csp-stars/shaders/starsSRPoint.comp | 53 ++++++++------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index 61f5e27d2..604806bf2 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -72,14 +72,10 @@ void main() { const float parsecToMeter = 3.08567758e16; vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; - Star star = stars[index]; - vec3 inPos = vec3(star.posX, star.posY, star.posZ); - vec3 inColor = vec3(star.colorR, star.colorG, star.colorB); - float inAbsMagnitude = star.absMagnitude; + Star star = stars[index]; + vec3 inPos = vec3(star.posX, star.posY, star.posZ); - float vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); - vec3 vColor = SRGBtoLINEAR(inColor); - vec4 vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); + vec4 vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); if (vScreenSpacePos.w <= 0) { return; @@ -92,6 +88,7 @@ void main() { return; } + float vMagnitude = getApparentMagnitude(star.absMagnitude, length(inPos - observerPos)); if (vMagnitude > uMaxMagnitude || vMagnitude < uMinMagnitude) { return; } @@ -99,6 +96,7 @@ void main() { float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); float luminance = magnitudeToLuminance(vMagnitude, solidAngle); + vec3 vColor = SRGBtoLINEAR(vec3(star.colorR, star.colorG, star.colorB)); vec4 oLuminance = vec4(vColor * luminance * uLuminanceMultiplicator, 1.0); #ifndef ENABLE_HDR @@ -109,40 +107,27 @@ void main() { vec2 float_star_coords = ((vScreenSpacePos.xy + 1) / 2) * vec2(outSize); + // shift the exact projected star coordinates by -.5 such that one refers to the four nearest + // pixels in the following + vec2 ref_weights = float_star_coords - 0.5; + ivec2 integer_ref_star_coords = ivec2(ref_weights); + ref_weights = fract(ref_weights); - //shift the exact projected star coordinates by -.5 such that one refers to the four nearest pixels in the following - vec2 ref_weights = float_star_coords - 0.5; - ivec2 integer_ref_star_coords = ivec2(ref_weights); - ref_weights = fract(ref_weights); - - //four nearest pixels - ivec2 target_star_coords[4] = {integer_ref_star_coords , integer_ref_star_coords + ivec2(1, 0), - integer_ref_star_coords + ivec2(0, 1), integer_ref_star_coords + ivec2(1, 1)}; - - //weighting related to bilinear interpolation, but not correct apparently, since the perceived luminance is odd - float pixel_weights[4] = {(1.0 - ref_weights.x) * (1.0 - ref_weights.y), - (ref_weights.x) * (1.0 - ref_weights.y), - (1.0 - ref_weights.x) * (ref_weights.y), - (ref_weights.x) * ( ref_weights.y)}; - - //calculate accumulated weights for subsequent weight normalization - float accumulated_weights = 0.0; - for(int i = 0; i < 4; ++i) { - accumulated_weights += pixel_weights[i]; - } + // four nearest pixels + ivec2 target_star_coords[4] = {integer_ref_star_coords, integer_ref_star_coords + ivec2(1, 0), + integer_ref_star_coords + ivec2(0, 1), integer_ref_star_coords + ivec2(1, 1)}; - //normalize weights and add - for(int i = 0; i < 4; ++i) { - pixel_weights[i] /= accumulated_weights; + float weights[4] = {(1 - ref_weights.x) * (1 - ref_weights.y), + ref_weights.x * (1 - ref_weights.y), (1 - ref_weights.x) * ref_weights.y, + ref_weights.x * ref_weights.y}; + // normalize weights and add + for (int i = 0; i < 4; ++i) { ivec2 current_target_position = target_star_coords[i]; - - vec3 oPixelWeightedLuminance = pixel_weights[i] * oLuminance.rgb; + vec3 oPixelWeightedLuminance = weights[i] * oLuminance.rgb; imageAtomicAdd(uOutRed, current_target_position, oPixelWeightedLuminance.r); imageAtomicAdd(uOutGreen, current_target_position, oPixelWeightedLuminance.g); imageAtomicAdd(uOutBlue, current_target_position, oPixelWeightedLuminance.b); } - - } \ No newline at end of file From f2ad4b4f5b07956937c9fa222d5321b182e884f5 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 07:39:06 +0100 Subject: [PATCH 33/57] :tada: Allow window resizing with SR stars --- plugins/csp-stars/src/Stars.cpp | 71 +++++++++++++++++---------------- plugins/csp-stars/src/Stars.hpp | 2 + 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index b03aa9ed4..dabfbcc4a 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -88,38 +88,8 @@ const int Stars::cCacheVersion = 3; //////////////////////////////////////////////////////////////////////////////////////////////////// Stars::Stars() { - for (auto const& viewport : GetVistaSystem()->GetDisplayManager()->GetViewports()) { - SoftwareRasterizerTargets srTarget; - - int width, height; - viewport.second->GetViewportProperties()->GetSize(width, height); - - srTarget.mR = std::make_unique(GL_TEXTURE_2D); - srTarget.mR->Bind(); - srTarget.mR->SetWrapS(GL_CLAMP_TO_EDGE); - srTarget.mR->SetWrapT(GL_CLAMP_TO_EDGE); - srTarget.mR->SetMinFilter(GL_NEAREST); - srTarget.mR->SetMagFilter(GL_NEAREST); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); - - srTarget.mG = std::make_unique(GL_TEXTURE_2D); - srTarget.mG->Bind(); - srTarget.mG->SetWrapS(GL_CLAMP_TO_EDGE); - srTarget.mG->SetWrapT(GL_CLAMP_TO_EDGE); - srTarget.mG->SetMinFilter(GL_NEAREST); - srTarget.mG->SetMagFilter(GL_NEAREST); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); - - srTarget.mB = std::make_unique(GL_TEXTURE_2D); - srTarget.mB->Bind(); - srTarget.mB->SetWrapS(GL_CLAMP_TO_EDGE); - srTarget.mB->SetWrapT(GL_CLAMP_TO_EDGE); - srTarget.mB->SetMinFilter(GL_NEAREST); - srTarget.mB->SetMagFilter(GL_NEAREST); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); - - mSRTargets.emplace(viewport.second, std::move(srTarget)); + mSRTargets[viewport.second] = {}; } } @@ -564,10 +534,43 @@ bool Stars::Do() { glUniformMatrix4fv(mUniforms.starInversePMatrix, 1, GL_FALSE, matInverseP.GetData()); if (mDrawMode == DrawMode::eSRPoint) { - glUniform1i(mUniforms.starCount, static_cast(mStars.size())); + // Recreate the render targets if the viewport size changed. + auto* viewport = GetVistaSystem()->GetDisplayManager()->GetCurrentRenderInfo()->m_pViewport; + auto& data = mSRTargets[viewport]; - auto* viewport = GetVistaSystem()->GetDisplayManager()->GetCurrentRenderInfo()->m_pViewport; - auto const& data = mSRTargets[viewport]; + int width, height; + viewport->GetViewportProperties()->GetSize(width, height); + + if (data.mWidth != width || data.mHeight != height) { + data.mWidth = width; + data.mHeight = height; + + data.mR = std::make_unique(GL_TEXTURE_2D); + data.mR->Bind(); + data.mR->SetWrapS(GL_CLAMP_TO_EDGE); + data.mR->SetWrapT(GL_CLAMP_TO_EDGE); + data.mR->SetMinFilter(GL_NEAREST); + data.mR->SetMagFilter(GL_NEAREST); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); + + data.mG = std::make_unique(GL_TEXTURE_2D); + data.mG->Bind(); + data.mG->SetWrapS(GL_CLAMP_TO_EDGE); + data.mG->SetWrapT(GL_CLAMP_TO_EDGE); + data.mG->SetMinFilter(GL_NEAREST); + data.mG->SetMagFilter(GL_NEAREST); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); + + data.mB = std::make_unique(GL_TEXTURE_2D); + data.mB->Bind(); + data.mB->SetWrapS(GL_CLAMP_TO_EDGE); + data.mB->SetWrapT(GL_CLAMP_TO_EDGE); + data.mB->SetMinFilter(GL_NEAREST); + data.mB->SetMagFilter(GL_NEAREST); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); + } + + glUniform1i(mUniforms.starCount, static_cast(mStars.size())); glClearTexImage(data.mR->GetId(), 0, GL_RED, GL_FLOAT, nullptr); glClearTexImage(data.mG->GetId(), 0, GL_RED, GL_FLOAT, nullptr); diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index 0236aa310..6ed0aa8fc 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -213,6 +213,8 @@ class Stars : public IVistaOpenGLDraw { std::unique_ptr mR; std::unique_ptr mG; std::unique_ptr mB; + int mWidth = 0; + int mHeight = 0; }; std::unordered_map mSRTargets; From d089a8c7a9ed2697f46ef8351bd5b854eb7f0abf Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 07:41:27 +0100 Subject: [PATCH 34/57] :wrench: Simplify shader --- plugins/csp-stars/shaders/starsSRBlit.frag | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/csp-stars/shaders/starsSRBlit.frag b/plugins/csp-stars/shaders/starsSRBlit.frag index 590b49b9f..c2337f237 100644 --- a/plugins/csp-stars/shaders/starsSRBlit.frag +++ b/plugins/csp-stars/shaders/starsSRBlit.frag @@ -7,8 +7,6 @@ in vec2 vTexcoords; -layout(pixel_center_integer) in vec4 gl_FragCoord; - layout(binding = 0) uniform sampler2D uR; layout(binding = 1) uniform sampler2D uG; layout(binding = 2) uniform sampler2D uB; @@ -16,8 +14,5 @@ layout(binding = 2) uniform sampler2D uB; layout(location = 0) out vec3 oColor; void main() { - vec3 color = - vec3(texture(uR, vTexcoords).r, texture(uG, vTexcoords).r, texture(uB, vTexcoords).r); - - oColor = color; + oColor = vec3(texture(uR, vTexcoords).r, texture(uG, vTexcoords).r, texture(uB, vTexcoords).r); } \ No newline at end of file From 108134cc7ae97705a250952d1c56acaf03dde746 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 07:41:40 +0100 Subject: [PATCH 35/57] :beetle: Fix edge sampling issue --- plugins/csp-stars/shaders/starsSRPoint.comp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index 604806bf2..ce6c1516e 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -110,7 +110,7 @@ void main() { // shift the exact projected star coordinates by -.5 such that one refers to the four nearest // pixels in the following vec2 ref_weights = float_star_coords - 0.5; - ivec2 integer_ref_star_coords = ivec2(ref_weights); + ivec2 integer_ref_star_coords = ivec2(floor(ref_weights)); ref_weights = fract(ref_weights); // four nearest pixels From 8aecd66e611cb346b6a290331991d164ee68fbf5 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 08:08:13 +0100 Subject: [PATCH 36/57] :wrench: Improve star settings layout --- plugins/csp-stars/gui/stars_settings.html | 96 ++++++++++++++--------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/plugins/csp-stars/gui/stars_settings.html b/plugins/csp-stars/gui/stars_settings.html index b16befea9..fc1bae3c4 100644 --- a/plugins/csp-stars/gui/stars_settings.html +++ b/plugins/csp-stars/gui/stars_settings.html @@ -30,85 +30,107 @@
+
Draw Mode
- Draw Mode +
-
+
-
+
-
+
-
+
-
- -
-
- Display Magnitude -
+
Display Magnitude
-
-
+
-
- Star Size -
+
Star Size
-
-
+
-
- Luminance Boost -
+
Luminance Boost
-
-
+
-
\ No newline at end of file +
From cdab64b3cdef8a2007ee2eb850f0870516c4f7d5 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 08:18:19 +0100 Subject: [PATCH 37/57] :wrench: Add separate timers for new star mode --- plugins/csp-stars/src/Stars.cpp | 38 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index dabfbcc4a..20a7fa8aa 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -572,25 +572,29 @@ bool Stars::Do() { glUniform1i(mUniforms.starCount, static_cast(mStars.size())); - glClearTexImage(data.mR->GetId(), 0, GL_RED, GL_FLOAT, nullptr); - glClearTexImage(data.mG->GetId(), 0, GL_RED, GL_FLOAT, nullptr); - glClearTexImage(data.mB->GetId(), 0, GL_RED, GL_FLOAT, nullptr); - - glBindImageTexture(0, data.mR->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); - glBindImageTexture(1, data.mG->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); - glBindImageTexture(2, data.mB->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); - - glDispatchCompute(static_cast(std::ceil(1.0 * mStars.size() / 256)), 1, 1); - - // Blit the result to the screen. - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + { + cs::utils::FrameStats::ScopedTimer timer("Software Rasterizer"); + glClearTexImage(data.mR->GetId(), 0, GL_RED, GL_FLOAT, nullptr); + glClearTexImage(data.mG->GetId(), 0, GL_RED, GL_FLOAT, nullptr); + glClearTexImage(data.mB->GetId(), 0, GL_RED, GL_FLOAT, nullptr); + + glBindImageTexture(0, data.mR->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + glBindImageTexture(1, data.mG->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + glBindImageTexture(2, data.mB->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + + glDispatchCompute(static_cast(std::ceil(1.0 * mStars.size() / 256)), 1, 1); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + } - mSRBlitShader.Bind(); - data.mR->Bind(GL_TEXTURE0); - data.mG->Bind(GL_TEXTURE1); - data.mB->Bind(GL_TEXTURE2); + { + cs::utils::FrameStats::ScopedTimer timer("Blit Results"); + mSRBlitShader.Bind(); + data.mR->Bind(GL_TEXTURE0); + data.mG->Bind(GL_TEXTURE1); + data.mB->Bind(GL_TEXTURE2); - glDrawArrays(GL_TRIANGLES, 0, 3); + glDrawArrays(GL_TRIANGLES, 0, 3); + } } else { glDrawArrays(GL_POINTS, 0, static_cast(mStars.size())); From b67d0306df87cc31cf264455e07ac85f39d79f6a Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 13:55:39 +0100 Subject: [PATCH 38/57] :wrench: Compute star color in shader --- plugins/csp-stars/shaders/starSnippets.glsl | 36 ++++++++++++ plugins/csp-stars/shaders/starsBillboard.geom | 4 +- plugins/csp-stars/shaders/starsBillboard.vert | 8 +-- plugins/csp-stars/shaders/starsOnePixel.frag | 4 +- plugins/csp-stars/shaders/starsOnePixel.vert | 6 +- plugins/csp-stars/shaders/starsSRPoint.comp | 6 +- plugins/csp-stars/src/Stars.cpp | 56 +++++++------------ 7 files changed, 69 insertions(+), 51 deletions(-) diff --git a/plugins/csp-stars/shaders/starSnippets.glsl b/plugins/csp-stars/shaders/starSnippets.glsl index 520050773..126db03f8 100644 --- a/plugins/csp-stars/shaders/starSnippets.glsl +++ b/plugins/csp-stars/shaders/starSnippets.glsl @@ -5,6 +5,42 @@ // SPDX-FileCopyrightText: German Aerospace Center (DLR) // SPDX-License-Identifier: MIT +// https://onlinelibrary.wiley.com/doi/10.1002/asna.202113868 +const float tMin = 2300; +const float tMax = 12000; +const int sSpectralColorsN = 73; +const vec3 sSpectralColors[73] = vec3[](vec3(1.0, 0.409, 0.078), vec3(1.0, 0.432, 0.093), + vec3(1.0, 0.455, 0.109), vec3(1.0, 0.476, 0.126), vec3(1.0, 0.497, 0.144), + vec3(1.0, 0.518, 0.163), vec3(1.0, 0.537, 0.182), vec3(1.0, 0.557, 0.202), + vec3(1.0, 0.575, 0.223), vec3(1.0, 0.593, 0.244), vec3(1.0, 0.611, 0.266), + vec3(1.0, 0.627, 0.289), vec3(1.0, 0.644, 0.311), vec3(1.0, 0.66, 0.335), + vec3(1.0, 0.675, 0.358), vec3(1.0, 0.69, 0.382), vec3(1.0, 0.704, 0.405), + vec3(1.0, 0.718, 0.429), vec3(1.0, 0.732, 0.454), vec3(1.0, 0.745, 0.478), + vec3(1.0, 0.758, 0.502), vec3(1.0, 0.77, 0.527), vec3(1.0, 0.782, 0.551), + vec3(1.0, 0.794, 0.575), vec3(1.0, 0.806, 0.599), vec3(1.0, 0.817, 0.624), + vec3(1.0, 0.827, 0.648), vec3(1.0, 0.838, 0.672), vec3(1.0, 0.848, 0.696), + vec3(1.0, 0.858, 0.719), vec3(1.0, 0.867, 0.743), vec3(1.0, 0.877, 0.766), + vec3(1.0, 0.886, 0.789), vec3(1.0, 0.894, 0.812), vec3(1.0, 0.903, 0.835), + vec3(1.0, 0.911, 0.858), vec3(1.0, 0.919, 0.88), vec3(1.0, 0.927, 0.902), + vec3(1.0, 0.935, 0.924), vec3(1.0, 0.942, 0.946), vec3(1.0, 0.95, 0.967), + vec3(1.0, 0.957, 0.989), vec3(0.991, 0.955, 1.0), vec3(0.971, 0.942, 1.0), + vec3(0.952, 0.93, 1.0), vec3(0.934, 0.918, 1.0), vec3(0.917, 0.907, 1.0), + vec3(0.901, 0.896, 1.0), vec3(0.87, 0.876, 1.0), vec3(0.843, 0.858, 1.0), + vec3(0.817, 0.841, 1.0), vec3(0.794, 0.825, 1.0), vec3(0.773, 0.81, 1.0), + vec3(0.753, 0.797, 1.0), vec3(0.735, 0.784, 1.0), vec3(0.718, 0.772, 1.0), + vec3(0.703, 0.761, 1.0), vec3(0.688, 0.75, 1.0), vec3(0.674, 0.741, 1.0), + vec3(0.662, 0.731, 1.0), vec3(0.65, 0.723, 1.0), vec3(0.639, 0.714, 1.0), + vec3(0.628, 0.706, 1.0), vec3(0.618, 0.699, 1.0), vec3(0.609, 0.692, 1.0), + vec3(0.6, 0.685, 1.0), vec3(0.592, 0.679, 1.0), vec3(0.584, 0.673, 1.0), + vec3(0.577, 0.667, 1.0), vec3(0.57, 0.662, 1.0), vec3(0.563, 0.657, 1.0), + vec3(0.557, 0.652, 1.0), vec3(0.55, 0.647, 1.0)); + +vec3 getStarColor(float temperature) { + float t = clamp((temperature - tMin) / (tMax - tMin), 0.0, 1.0); + int idx = int(t * (sSpectralColorsN - 1)); + return sSpectralColors[idx]; +} + float log10(float x) { return log(x) / log(10.0); } diff --git a/plugins/csp-stars/shaders/starsBillboard.geom b/plugins/csp-stars/shaders/starsBillboard.geom index d21cba435..b00ac73c8 100644 --- a/plugins/csp-stars/shaders/starsBillboard.geom +++ b/plugins/csp-stars/shaders/starsBillboard.geom @@ -9,7 +9,7 @@ layout(points) in; layout(triangle_strip, max_vertices = 4) out; // inputs -in vec3 vColor[]; +in float vTemperature[]; in float vMagnitude[]; // uniforms @@ -24,7 +24,7 @@ out float iMagnitude; out vec2 iTexcoords; void main() { - iColor = SRGBtoLINEAR(vColor[0]); + iColor = getStarColor(vTemperature[0]); iMagnitude = vMagnitude[0]; diff --git a/plugins/csp-stars/shaders/starsBillboard.vert b/plugins/csp-stars/shaders/starsBillboard.vert index fd928cb9a..7db065ca6 100644 --- a/plugins/csp-stars/shaders/starsBillboard.vert +++ b/plugins/csp-stars/shaders/starsBillboard.vert @@ -7,7 +7,7 @@ // inputs layout(location = 0) in vec3 inPos; -layout(location = 1) in vec3 inColor; +layout(location = 1) in float inTemperature; layout(location = 2) in float inAbsMagnitude; // uniforms @@ -15,15 +15,15 @@ uniform mat4 uMatMV; uniform mat4 uInvMV; // outputs -out vec3 vColor; +out float vTemperature; out float vMagnitude; void main() { const float parsecToMeter = 3.08567758e16; vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; - vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); - vColor = inColor; + vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); + vTemperature = inTemperature; gl_Position = uMatMV * vec4(inPos * parsecToMeter, 1); } \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsOnePixel.frag b/plugins/csp-stars/shaders/starsOnePixel.frag index d02c548ee..39f7cf902 100644 --- a/plugins/csp-stars/shaders/starsOnePixel.frag +++ b/plugins/csp-stars/shaders/starsOnePixel.frag @@ -6,7 +6,7 @@ // SPDX-License-Identifier: MIT // inputs -in vec3 vColor; +in float vTemperature; in vec4 vScreenSpacePos; in float vMagnitude; @@ -49,7 +49,7 @@ void main() { float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); float luminance = magnitudeToLuminance(vMagnitude, solidAngle); - oLuminance = vec4(vColor * luminance * uLuminanceMultiplicator, 1.0); + oLuminance = vec4(getStarColor(vTemperature) * luminance * uLuminanceMultiplicator, 1.0); #ifndef ENABLE_HDR oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); diff --git a/plugins/csp-stars/shaders/starsOnePixel.vert b/plugins/csp-stars/shaders/starsOnePixel.vert index 1acfb3df0..ad681c653 100644 --- a/plugins/csp-stars/shaders/starsOnePixel.vert +++ b/plugins/csp-stars/shaders/starsOnePixel.vert @@ -7,7 +7,7 @@ // inputs layout(location = 0) in vec3 inPos; -layout(location = 1) in vec3 inColor; +layout(location = 1) in float inTemperature; layout(location = 2) in float inAbsMagnitude; // uniforms @@ -16,7 +16,7 @@ uniform mat4 uMatP; uniform mat4 uInvMV; // outputs -out vec3 vColor; +out float vTemperature; out vec4 vScreenSpacePos; out float vMagnitude; @@ -26,7 +26,7 @@ void main() { vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); - vColor = SRGBtoLINEAR(inColor); + vTemperature = inTemperature; vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index ce6c1516e..730278f65 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -18,9 +18,7 @@ struct Star { float posX; float posY; float posZ; - float colorR; - float colorG; - float colorB; + float tEff; float absMagnitude; }; @@ -96,7 +94,7 @@ void main() { float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); float luminance = magnitudeToLuminance(vMagnitude, solidAngle); - vec3 vColor = SRGBtoLINEAR(vec3(star.colorR, star.colorG, star.colorB)); + vec3 vColor = getStarColor(star.tEff); vec4 oLuminance = vec4(vColor * luminance * uLuminanceMultiplicator, 1.0); #ifndef ENABLE_HDR diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index 20a7fa8aa..8e4fb38eb 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -50,23 +50,6 @@ bool fromString(std::string const& v, T& out) { return (iss.rdstate() & std::stringstream::failbit) == 0; } -// spectral colors from B-V index -0.4 to 2.0 in steps of 0.05 -// values from http://www.vendian.org/mncharity/dir3/starcolor/details.html -// NOLINTNEXTLINE(cert-err58-cpp) -const std::array sSpectralColors = {VistaColor(0x9bb2ff), VistaColor(0x9eb5ff), - VistaColor(0xa3b9ff), VistaColor(0xaabfff), VistaColor(0xb2c5ff), VistaColor(0xbbccff), - VistaColor(0xc4d2ff), VistaColor(0xccd8ff), VistaColor(0xd3ddff), VistaColor(0xdae2ff), - VistaColor(0xdfe5ff), VistaColor(0xe4e9ff), VistaColor(0xe9ecff), VistaColor(0xeeefff), - VistaColor(0xf3f2ff), VistaColor(0xf8f6ff), VistaColor(0xfef9ff), VistaColor(0xfff9fb), - VistaColor(0xfff7f5), VistaColor(0xfff5ef), VistaColor(0xfff3ea), VistaColor(0xfff1e5), - VistaColor(0xffefe0), VistaColor(0xffeddb), VistaColor(0xffebd6), VistaColor(0xffe8ce), - VistaColor(0xffe6ca), VistaColor(0xffe5c6), VistaColor(0xffe3c3), VistaColor(0xffe2bf), - VistaColor(0xffe0bb), VistaColor(0xffdfb8), VistaColor(0xffddb4), VistaColor(0xffdbb0), - VistaColor(0xffdaad), VistaColor(0xffd8a9), VistaColor(0xffd6a5), VistaColor(0xffd29c), - VistaColor(0xffd096), VistaColor(0xffcc8f), VistaColor(0xffc885), VistaColor(0xffc178), - VistaColor(0xffb765), VistaColor(0xffa94b), VistaColor(0xff9523), VistaColor(0xff7b00), - VistaColor(0xff5200)}; - //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace @@ -836,18 +819,21 @@ bool Stars::readStarCache(const std::string& sCacheFile) { //////////////////////////////////////////////////////////////////////////////////////////////////// void Stars::buildStarVAO() { - int c(0); - const int iElementCount(7); + int index(0); + const int iElementCount(5); std::vector data(iElementCount * mStars.size()); - for (auto it = mStars.begin(); it != mStars.end(); ++it, c += iElementCount) { + for (auto it = mStars.begin(); it != mStars.end(); ++it, index += iElementCount) { // use B and V magnitude to retrieve the according color - const float minIdx(-0.4F); - const float maxIdx(2.0F); - const float step(0.05F); - float bvIndex = std::min(maxIdx, std::max(minIdx, it->mBMagnitude - it->mVMagnitude)); - float normalizedIndex = (bvIndex - minIdx) / (maxIdx - minIdx) / step + 0.5F; - VistaColor color = sSpectralColors.at(static_cast(normalizedIndex)); + float bv = it->mBMagnitude - it->mVMagnitude; + + // https://arxiv.org/pdf/1201.1809 + // https://github.com/sczesla/PyAstronomy/blob/master/src/pyasl/asl/aslExt_1/ballesterosBV_T.py + const float t0 = 4600.F; + const float a = 0.92F; + const float b = 1.7F; + const float c = 0.62F; + float tEff = t0 * (1.0 / (a * bv + b) + 1.0 / (a * bv + c)); // distance in parsec --- some have parallax of zero; assume a // large distance in those cases @@ -861,13 +847,11 @@ void Stars::buildStarVAO() { glm::sin(it->mDeclination) * fDist, glm::cos(it->mDeclination) * glm::sin(it->mAscension) * fDist); - data[c] = starPos[0]; - data[c + 1] = starPos[1]; - data[c + 2] = starPos[2]; - data[c + 3] = color.GetRed(); - data[c + 4] = color.GetGreen(); - data[c + 5] = color.GetBlue(); - data[c + 6] = it->mVMagnitude - 5.F * std::log10(fDist / 10.F); + data[index] = starPos[0]; + data[index + 1] = starPos[1]; + data[index + 2] = starPos[2]; + data[index + 3] = tEff; + data[index + 4] = it->mVMagnitude - 5.F * std::log10(fDist / 10.F); } mStarVBO.Bind(GL_ARRAY_BUFFER); @@ -879,15 +863,15 @@ void Stars::buildStarVAO() { mStarVAO.SpecifyAttributeArrayFloat( 0, 3, GL_FLOAT, GL_FALSE, iElementCount * sizeof(float), 0, &mStarVBO); - // color + // temperature mStarVAO.EnableAttributeArray(1); mStarVAO.SpecifyAttributeArrayFloat( - 1, 3, GL_FLOAT, GL_FALSE, iElementCount * sizeof(float), 3 * sizeof(float), &mStarVBO); + 1, 1, GL_FLOAT, GL_FALSE, iElementCount * sizeof(float), 3 * sizeof(float), &mStarVBO); // magnitude mStarVAO.EnableAttributeArray(2); mStarVAO.SpecifyAttributeArrayFloat( - 2, 1, GL_FLOAT, GL_FALSE, iElementCount * sizeof(float), 6 * sizeof(float), &mStarVBO); + 2, 1, GL_FLOAT, GL_FALSE, iElementCount * sizeof(float), 4 * sizeof(float), &mStarVBO); } //////////////////////////////////////////////////////////////////////////////////////////////////// From 64a6c670a5f47c5682bd944e0b3e5232e3bfb232 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 19 Dec 2024 21:18:17 +0100 Subject: [PATCH 39/57] :wrench: Use single atomic uint32 texture for software star rasterizer --- plugins/csp-stars/shaders/starsSRBlit.frag | 23 ++++++++--- plugins/csp-stars/shaders/starsSRPoint.comp | 40 ++++++++++++------- plugins/csp-stars/src/Stars.cpp | 44 +++++---------------- plugins/csp-stars/src/Stars.hpp | 4 +- 4 files changed, 55 insertions(+), 56 deletions(-) diff --git a/plugins/csp-stars/shaders/starsSRBlit.frag b/plugins/csp-stars/shaders/starsSRBlit.frag index c2337f237..218ec73ff 100644 --- a/plugins/csp-stars/shaders/starsSRBlit.frag +++ b/plugins/csp-stars/shaders/starsSRBlit.frag @@ -7,12 +7,25 @@ in vec2 vTexcoords; -layout(binding = 0) uniform sampler2D uR; -layout(binding = 1) uniform sampler2D uG; -layout(binding = 2) uniform sampler2D uB; - +layout(binding = 0) uniform usampler2D uImage; layout(location = 0) out vec3 oColor; void main() { - oColor = vec3(texture(uR, vTexcoords).r, texture(uG, vTexcoords).r, texture(uB, vTexcoords).r); + + uint value = texture(uImage, vTexcoords).r; + + // tEff is stored in the first 8 bits of the red channel + float tEff = (value & 0xFF) * 100; + + // luminance is stored in the remaining 24 bits + float luminance = (value >> 8) / pow(2.0, 20.0); + + if (tEff <= 0.0) { + discard; + } + oColor = getStarColor(tEff) * luminance; + +#ifndef ENABLE_HDR + oColor = Uncharted2Tonemap(oColor.rgb * 1e3); +#endif } \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index 730278f65..552f0b71e 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -9,9 +9,7 @@ layout(local_size_x = 256) in; -layout(r32f, binding = 0) coherent uniform image2D uOutRed; -layout(r32f, binding = 1) coherent uniform image2D uOutGreen; -layout(r32f, binding = 2) coherent uniform image2D uOutBlue; +layout(r32ui, binding = 0) coherent uniform uimage2D uOutImage; // input SSBO struct Star { @@ -94,14 +92,9 @@ void main() { float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); float luminance = magnitudeToLuminance(vMagnitude, solidAngle); - vec3 vColor = getStarColor(star.tEff); - vec4 oLuminance = vec4(vColor * luminance * uLuminanceMultiplicator, 1.0); + vec3 vColor = getStarColor(star.tEff); -#ifndef ENABLE_HDR - oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); -#endif - - ivec2 outSize = imageSize(uOutRed); + ivec2 outSize = imageSize(uOutImage); vec2 float_star_coords = ((vScreenSpacePos.xy + 1) / 2) * vec2(outSize); @@ -122,10 +115,29 @@ void main() { // normalize weights and add for (int i = 0; i < 4; ++i) { ivec2 current_target_position = target_star_coords[i]; - vec3 oPixelWeightedLuminance = weights[i] * oLuminance.rgb; + float oPixelWeightedLuminance = weights[i] * luminance * uLuminanceMultiplicator; + + uint intLuminance = uint(pow(2, 20) * oPixelWeightedLuminance); + uint intTemp = uint(0.01 * star.tEff); + uint oldVal = 0; + uint newVal = 0; + + do { + oldVal = imageLoad(uOutImage, current_target_position).r; + + // tEff is stored in the first 8 bits of the red channel + uint tEff = oldVal & 0xFF; + + // luminance is stored in the remaining 24 bits + uint oldLuminance = oldVal >> 8; + + // luminance is added to the old value + uint newLuminance = oldLuminance + intLuminance; + + // tEff is weighted by the luminances + uint newTEff = uint((1.0 * tEff * oldLuminance + intTemp * intLuminance) / newLuminance); - imageAtomicAdd(uOutRed, current_target_position, oPixelWeightedLuminance.r); - imageAtomicAdd(uOutGreen, current_target_position, oPixelWeightedLuminance.g); - imageAtomicAdd(uOutBlue, current_target_position, oPixelWeightedLuminance.b); + newVal = (newTEff & 0xFF) | (newLuminance << 8); + } while (oldVal != imageAtomicCompSwap(uOutImage, current_target_position, oldVal, newVal)); } } \ No newline at end of file diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index 8e4fb38eb..e4eaa4770 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -330,7 +330,6 @@ bool Stars::Do() { if (mDrawMode == DrawMode::eSRPoint) { defines += "#version 430\n"; - defines += "#extension GL_NV_shader_atomic_float : enable\n"; } else { defines += "#version 330\n"; } @@ -528,42 +527,21 @@ bool Stars::Do() { data.mWidth = width; data.mHeight = height; - data.mR = std::make_unique(GL_TEXTURE_2D); - data.mR->Bind(); - data.mR->SetWrapS(GL_CLAMP_TO_EDGE); - data.mR->SetWrapT(GL_CLAMP_TO_EDGE); - data.mR->SetMinFilter(GL_NEAREST); - data.mR->SetMagFilter(GL_NEAREST); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); - - data.mG = std::make_unique(GL_TEXTURE_2D); - data.mG->Bind(); - data.mG->SetWrapS(GL_CLAMP_TO_EDGE); - data.mG->SetWrapT(GL_CLAMP_TO_EDGE); - data.mG->SetMinFilter(GL_NEAREST); - data.mG->SetMagFilter(GL_NEAREST); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); - - data.mB = std::make_unique(GL_TEXTURE_2D); - data.mB->Bind(); - data.mB->SetWrapS(GL_CLAMP_TO_EDGE); - data.mB->SetWrapT(GL_CLAMP_TO_EDGE); - data.mB->SetMinFilter(GL_NEAREST); - data.mB->SetMagFilter(GL_NEAREST); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); + data.mImage = std::make_unique(GL_TEXTURE_2D); + data.mImage->Bind(); + data.mImage->SetWrapS(GL_CLAMP_TO_EDGE); + data.mImage->SetWrapT(GL_CLAMP_TO_EDGE); + data.mImage->SetMinFilter(GL_NEAREST); + data.mImage->SetMagFilter(GL_NEAREST); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, width, height); } glUniform1i(mUniforms.starCount, static_cast(mStars.size())); { cs::utils::FrameStats::ScopedTimer timer("Software Rasterizer"); - glClearTexImage(data.mR->GetId(), 0, GL_RED, GL_FLOAT, nullptr); - glClearTexImage(data.mG->GetId(), 0, GL_RED, GL_FLOAT, nullptr); - glClearTexImage(data.mB->GetId(), 0, GL_RED, GL_FLOAT, nullptr); - - glBindImageTexture(0, data.mR->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); - glBindImageTexture(1, data.mG->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); - glBindImageTexture(2, data.mB->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + glClearTexImage(data.mImage->GetId(), 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr); + glBindImageTexture(0, data.mImage->GetId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI); glDispatchCompute(static_cast(std::ceil(1.0 * mStars.size() / 256)), 1, 1); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); @@ -572,9 +550,7 @@ bool Stars::Do() { { cs::utils::FrameStats::ScopedTimer timer("Blit Results"); mSRBlitShader.Bind(); - data.mR->Bind(GL_TEXTURE0); - data.mG->Bind(GL_TEXTURE1); - data.mB->Bind(GL_TEXTURE2); + data.mImage->Bind(GL_TEXTURE0); glDrawArrays(GL_TRIANGLES, 0, 3); } diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index 6ed0aa8fc..8cfccd27c 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -210,9 +210,7 @@ class Stars : public IVistaOpenGLDraw { } mUniforms; struct SoftwareRasterizerTargets { - std::unique_ptr mR; - std::unique_ptr mG; - std::unique_ptr mB; + std::unique_ptr mImage; int mWidth = 0; int mHeight = 0; }; From a5c44fcc3c989ac4caedb1b51f162aac8d6e1fc5 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 20 Dec 2024 14:12:47 +0100 Subject: [PATCH 40/57] :wrench: Pack star luminance and temperature into two half floats --- plugins/csp-stars/shaders/starsSRBlit.frag | 14 ++++--------- plugins/csp-stars/shaders/starsSRPoint.comp | 23 +++++++-------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/plugins/csp-stars/shaders/starsSRBlit.frag b/plugins/csp-stars/shaders/starsSRBlit.frag index 218ec73ff..15b75fc69 100644 --- a/plugins/csp-stars/shaders/starsSRBlit.frag +++ b/plugins/csp-stars/shaders/starsSRBlit.frag @@ -11,19 +11,13 @@ layout(binding = 0) uniform usampler2D uImage; layout(location = 0) out vec3 oColor; void main() { + vec2 temperatureLuminance = unpackHalf2x16(texture(uImage, vTexcoords).r); - uint value = texture(uImage, vTexcoords).r; - - // tEff is stored in the first 8 bits of the red channel - float tEff = (value & 0xFF) * 100; - - // luminance is stored in the remaining 24 bits - float luminance = (value >> 8) / pow(2.0, 20.0); - - if (tEff <= 0.0) { + if (any(lessThanEqual(temperatureLuminance, vec2(0.0)))) { discard; } - oColor = getStarColor(tEff) * luminance; + + oColor = getStarColor(temperatureLuminance.x) * temperatureLuminance.y; #ifndef ENABLE_HDR oColor = Uncharted2Tonemap(oColor.rgb * 1e3); diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index 552f0b71e..80304aa33 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -117,27 +117,20 @@ void main() { ivec2 current_target_position = target_star_coords[i]; float oPixelWeightedLuminance = weights[i] * luminance * uLuminanceMultiplicator; - uint intLuminance = uint(pow(2, 20) * oPixelWeightedLuminance); - uint intTemp = uint(0.01 * star.tEff); - uint oldVal = 0; - uint newVal = 0; + uint oldVal = 0; + uint newVal = 0; do { oldVal = imageLoad(uOutImage, current_target_position).r; - // tEff is stored in the first 8 bits of the red channel - uint tEff = oldVal & 0xFF; + vec2 temperatureLuminance = unpackHalf2x16(oldVal); - // luminance is stored in the remaining 24 bits - uint oldLuminance = oldVal >> 8; + float newLuminance = temperatureLuminance.y + oPixelWeightedLuminance; + float newTEff = + (temperatureLuminance.x * temperatureLuminance.y + star.tEff * oPixelWeightedLuminance) / + newLuminance; - // luminance is added to the old value - uint newLuminance = oldLuminance + intLuminance; - - // tEff is weighted by the luminances - uint newTEff = uint((1.0 * tEff * oldLuminance + intTemp * intLuminance) / newLuminance); - - newVal = (newTEff & 0xFF) | (newLuminance << 8); + newVal = packHalf2x16(vec2(newTEff, newLuminance)); } while (oldVal != imageAtomicCompSwap(uOutImage, current_target_position, oldVal, newVal)); } } \ No newline at end of file From e1a419f83c6d03c3ca25ce107f4b7d9d0b88f1fa Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 20 Dec 2024 14:21:03 +0100 Subject: [PATCH 41/57] :wrench: Apply star luminance multiplicator in final blit pass --- plugins/csp-stars/shaders/starsSRBlit.frag | 6 ++++-- plugins/csp-stars/shaders/starsSRPoint.comp | 3 +-- plugins/csp-stars/src/Stars.cpp | 13 +++++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/csp-stars/shaders/starsSRBlit.frag b/plugins/csp-stars/shaders/starsSRBlit.frag index 15b75fc69..291f31247 100644 --- a/plugins/csp-stars/shaders/starsSRBlit.frag +++ b/plugins/csp-stars/shaders/starsSRBlit.frag @@ -10,6 +10,8 @@ in vec2 vTexcoords; layout(binding = 0) uniform usampler2D uImage; layout(location = 0) out vec3 oColor; +uniform float uLuminanceMultiplicator; + void main() { vec2 temperatureLuminance = unpackHalf2x16(texture(uImage, vTexcoords).r); @@ -17,9 +19,9 @@ void main() { discard; } - oColor = getStarColor(temperatureLuminance.x) * temperatureLuminance.y; + oColor = getStarColor(temperatureLuminance.x) * temperatureLuminance.y * uLuminanceMultiplicator; #ifndef ENABLE_HDR - oColor = Uncharted2Tonemap(oColor.rgb * 1e3); + oColor = Uncharted2Tonemap(oColor.rgb * 4e3); #endif } \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index 80304aa33..291a110b8 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -30,7 +30,6 @@ uniform mat4 uMatMV; uniform mat4 uMatP; uniform mat4 uInvMV; -uniform float uLuminanceMultiplicator; uniform mat4 uInvP; uniform vec2 uResolution; uniform float uMinMagnitude; @@ -115,7 +114,7 @@ void main() { // normalize weights and add for (int i = 0; i < 4; ++i) { ivec2 current_target_position = target_star_coords[i]; - float oPixelWeightedLuminance = weights[i] * luminance * uLuminanceMultiplicator; + float oPixelWeightedLuminance = weights[i] * luminance; uint oldVal = 0; uint newVal = 0; diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index e4eaa4770..e9bd309c8 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -412,7 +412,12 @@ bool Stars::Do() { mUniforms.starMinMagnitude = mStarShader.GetUniformLocation("uMinMagnitude"); mUniforms.starMaxMagnitude = mStarShader.GetUniformLocation("uMaxMagnitude"); mUniforms.starSolidAngle = mStarShader.GetUniformLocation("uSolidAngle"); - mUniforms.starLuminanceMul = mStarShader.GetUniformLocation("uLuminanceMultiplicator"); + + if (mDrawMode == DrawMode::eSRPoint) { + mUniforms.starLuminanceMul = mSRBlitShader.GetUniformLocation("uLuminanceMultiplicator"); + } else { + mUniforms.starLuminanceMul = mStarShader.GetUniformLocation("uLuminanceMultiplicator"); + } mUniforms.starMVMatrix = mStarShader.GetUniformLocation("uMatMV"); mUniforms.starPMatrix = mStarShader.GetUniformLocation("uMatP"); @@ -505,7 +510,9 @@ bool Stars::Do() { mStarShader.SetUniform(mUniforms.starSolidAngle, mSolidAngle); float fadeOut = mEnableHDR ? 1.F : sceneBrightness; - mStarShader.SetUniform(mUniforms.starLuminanceMul, mLuminanceMultiplicator * fadeOut); + if (mDrawMode != DrawMode::eSRPoint) { + mStarShader.SetUniform(mUniforms.starLuminanceMul, mLuminanceMultiplicator * fadeOut); + } VistaTransformMatrix matInverseMV(matModelView.GetInverted()); VistaTransformMatrix matInverseP(matProjection.GetInverted()); @@ -550,6 +557,8 @@ bool Stars::Do() { { cs::utils::FrameStats::ScopedTimer timer("Blit Results"); mSRBlitShader.Bind(); + mSRBlitShader.SetUniform(mUniforms.starLuminanceMul, mLuminanceMultiplicator * fadeOut); + data.mImage->Bind(GL_TEXTURE0); glDrawArrays(GL_TRIANGLES, 0, 3); From 78b955ac47686887cfbef2e43334013a39594e05 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 20 Dec 2024 15:42:48 +0100 Subject: [PATCH 42/57] :tada: Add initial support for Gaia stars --- plugins/csp-stars/src/Plugin.cpp | 6 ++ plugins/csp-stars/src/Plugin.hpp | 1 + plugins/csp-stars/src/Stars.cpp | 106 +++++++++++++++++++------------ plugins/csp-stars/src/Stars.hpp | 17 +++-- 4 files changed, 80 insertions(+), 50 deletions(-) diff --git a/plugins/csp-stars/src/Plugin.cpp b/plugins/csp-stars/src/Plugin.cpp index dda0e76bd..112af5455 100644 --- a/plugins/csp-stars/src/Plugin.cpp +++ b/plugins/csp-stars/src/Plugin.cpp @@ -47,6 +47,7 @@ void from_json(nlohmann::json const& j, Plugin::Settings& o) { cs::core::Settings::deserialize(j, "hipparcosCatalog", o.mHipparcosCatalog); cs::core::Settings::deserialize(j, "tychoCatalog", o.mTychoCatalog); cs::core::Settings::deserialize(j, "tycho2Catalog", o.mTycho2Catalog); + cs::core::Settings::deserialize(j, "gaiaCatalog", o.mGaiaCatalog); cs::core::Settings::deserialize(j, "enabled", o.mEnabled); cs::core::Settings::deserialize(j, "enableCelestialGrid", o.mEnableCelestialGrid); cs::core::Settings::deserialize(j, "enableStarFigures", o.mEnableStarFigures); @@ -66,6 +67,7 @@ void to_json(nlohmann::json& j, Plugin::Settings const& o) { cs::core::Settings::serialize(j, "hipparcosCatalog", o.mHipparcosCatalog); cs::core::Settings::serialize(j, "tychoCatalog", o.mTychoCatalog); cs::core::Settings::serialize(j, "tycho2Catalog", o.mTycho2Catalog); + cs::core::Settings::serialize(j, "gaiaCatalog", o.mGaiaCatalog); cs::core::Settings::serialize(j, "enabled", o.mEnabled); cs::core::Settings::serialize(j, "enableCelestialGrid", o.mEnableCelestialGrid); cs::core::Settings::serialize(j, "enableStarFigures", o.mEnableStarFigures); @@ -290,6 +292,10 @@ void Plugin::onLoad() { catalogs[Stars::CatalogType::eTycho2] = *mPluginSettings.mTycho2Catalog; } + if (mPluginSettings.mGaiaCatalog) { + catalogs[Stars::CatalogType::eGaia] = *mPluginSettings.mGaiaCatalog; + } + mStars->setCatalogs(catalogs); } diff --git a/plugins/csp-stars/src/Plugin.hpp b/plugins/csp-stars/src/Plugin.hpp index aa3b4a84a..f515c9719 100644 --- a/plugins/csp-stars/src/Plugin.hpp +++ b/plugins/csp-stars/src/Plugin.hpp @@ -33,6 +33,7 @@ class Plugin : public cs::core::PluginBase { std::optional mHipparcosCatalog; std::optional mTychoCatalog; std::optional mTycho2Catalog; + std::optional mGaiaCatalog; cs::utils::DefaultProperty mEnabled{true}; cs::utils::DefaultProperty mEnableCelestialGrid{false}; cs::utils::DefaultProperty mEnableStarFigures{false}; diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index e9bd309c8..5a699bb41 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -57,16 +57,17 @@ bool fromString(std::string const& v, T& out) { //////////////////////////////////////////////////////////////////////////////////////////////////// const std::array, Stars::NUM_CATALOGS> Stars::cColumnMapping{ - std::array{34, 32, 11, 8, 9, 31}, // CatalogType::eHipparcos - std::array{34, 32, 11, 8, 9, 31}, // CatalogType::eTycho - std::array{19, 17, -1, 2, 3, 23} // CatalogType::eTycho2 + std::array{34, 11, 8, 9, 31}, // CatalogType::eHipparcos + std::array{34, 11, 8, 9, 31}, // CatalogType::eTycho + std::array{19, -1, 2, 3, 23}, // CatalogType::eTycho2 + std::array{5, 4, 2, 3, 1} // CatalogType::eGaia }; //////////////////////////////////////////////////////////////////////////////////////////////////// // Increase this if the cache format changed and is incompatible now. This will // force a reload. -const int Stars::cCacheVersion = 3; +const int Stars::cCacheVersion = 4; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -102,7 +103,7 @@ void Stars::setCatalogs(std::map catalogs) { it = mCatalogs.find(CatalogType::eTycho2); if (it != mCatalogs.end()) { - // do not load tycho and tycho 2 + // Do not load tycho and tycho 2. if (mCatalogs.find(CatalogType::eTycho) == mCatalogs.end()) { readStarsFromCatalog(it->first, it->second); } else { @@ -110,6 +111,17 @@ void Stars::setCatalogs(std::map catalogs) { } } + it = mCatalogs.find(CatalogType::eGaia); + if (it != mCatalogs.end()) { + // Do not load together with tycho and tycho 2. + if (mCatalogs.find(CatalogType::eTycho) == mCatalogs.end() && + mCatalogs.find(CatalogType::eTycho2) == mCatalogs.end()) { + readStarsFromCatalog(it->first, it->second); + } else { + logger().warn("Failed to load Gaia catalog: Tycho already loaded!"); + } + } + if (!mStars.empty()) { writeStarCache(mCacheFile); } else { @@ -622,29 +634,26 @@ bool Stars::readStarsFromCatalog(CatalogType type, std::string const& filename) std::vector items = cs::utils::splitString(line, '|'); // convert value strings to int/double/float and save in star data structure - // expecting Hipparcos or Tycho-1 catalog and more than 12 columns - if (items.size() > 12) { + // expecting Hipparcos or Tycho-1 catalog and more than 5 columns + if (items.size() > 5) { // skip if part of hipparcos catalogue - int tmp{}; - if (type != CatalogType::eHipparcos && loadHipparcos && - fromString(items[cColumnMapping.at(cs::utils::enumCast(type)) - .at(cs::utils::enumCast(CatalogColumn::eHipp))], - tmp)) { - continue; + if (type != CatalogType::eHipparcos && loadHipparcos) { + int hippID{}; + bool read = fromString(items[cColumnMapping.at(cs::utils::enumCast(type)) + .at(cs::utils::enumCast(CatalogColumn::eHipp))], + hippID); + if (read && hippID >= 0) { + continue; + } } // store star data bool successStoreData(true); Star star{}; - successStoreData &= fromString( - items[cColumnMapping.at( - cs::utils::enumCast(type))[cs::utils::enumCast(CatalogColumn::eVmag)]], - star.mVMagnitude); - successStoreData &= fromString( - items[cColumnMapping.at( - cs::utils::enumCast(type))[cs::utils::enumCast(CatalogColumn::eBmag)]], - star.mBMagnitude); + successStoreData &= fromString(items[cColumnMapping.at(cs::utils::enumCast( + type))[cs::utils::enumCast(CatalogColumn::eMag)]], + star.mMagnitude); successStoreData &= fromString( items[cColumnMapping.at( cs::utils::enumCast(type))[cs::utils::enumCast(CatalogColumn::eRect)]], @@ -665,6 +674,33 @@ bool Stars::readStarsFromCatalog(CatalogType type, std::string const& filename) star.mParallax = 0; } + if (type == CatalogType::eGaia) { + int GbpMinusGrpColumn = 6; + float GbpMinusGrp = 0; + successStoreData &= fromString(items[GbpMinusGrpColumn], GbpMinusGrp); + + // https://doi.org/10.1051/0004-6361/201015441 + float logTeff = 3.999 - 0.654 * GbpMinusGrp + 0.709 * std::pow(GbpMinusGrp, 2) - + 0.316 * std::pow(GbpMinusGrp, 3); + star.mTEff = std::pow(10, logTeff); + + } else { + int bMagColumn = type == CatalogType::eTycho2 ? 17 : 32; + float bMag = 0; + successStoreData &= fromString(items[bMagColumn], bMag); + + // use B and V magnitude to retrieve the according color + float bv = bMag - star.mMagnitude; + + // https://arxiv.org/pdf/1201.1809 + // https://github.com/sczesla/PyAstronomy/blob/master/src/pyasl/asl/aslExt_1/ballesterosBV_T.py + const float t0 = 4600.F; + const float a = 0.92F; + const float b = 1.7F; + const float c = 0.62F; + star.mTEff = t0 * (1.0 / (a * bv + b) + 1.0 / (a * bv + c)); + } + if (successStoreData) { star.mAscension = (360.F + 90.F - star.mAscension) / 180.F * Vista::Pi; star.mDeclination = star.mDeclination / 180.F * Vista::Pi; @@ -673,7 +709,7 @@ bool Stars::readStarsFromCatalog(CatalogType type, std::string const& filename) } } - // print progress status + // Print progress status every 10000 stars. if (mStars.size() % 10000 == 0) { logger().info("Read {} stars so far...", mStars.size()); } @@ -706,8 +742,8 @@ void Stars::writeStarCache(const std::string& sCacheFile) const { for (const auto& mStar : mStars) { // serialize star data into byte stream - serializer.WriteFloat32(mStar.mVMagnitude); - serializer.WriteFloat32(mStar.mBMagnitude); + serializer.WriteFloat32(mStar.mMagnitude); + serializer.WriteFloat32(mStar.mTEff); serializer.WriteFloat32(mStar.mAscension); serializer.WriteFloat32(mStar.mDeclination); serializer.WriteFloat32(mStar.mParallax); @@ -779,8 +815,8 @@ bool Stars::readStarCache(const std::string& sCacheFile) { for (unsigned int num = 0; num < numStars; ++num) { Star star{}; - deserializer.ReadFloat32(star.mVMagnitude); - deserializer.ReadFloat32(star.mBMagnitude); + deserializer.ReadFloat32(star.mMagnitude); + deserializer.ReadFloat32(star.mTEff); deserializer.ReadFloat32(star.mAscension); deserializer.ReadFloat32(star.mDeclination); deserializer.ReadFloat32(star.mParallax); @@ -809,19 +845,7 @@ void Stars::buildStarVAO() { std::vector data(iElementCount * mStars.size()); for (auto it = mStars.begin(); it != mStars.end(); ++it, index += iElementCount) { - // use B and V magnitude to retrieve the according color - float bv = it->mBMagnitude - it->mVMagnitude; - - // https://arxiv.org/pdf/1201.1809 - // https://github.com/sczesla/PyAstronomy/blob/master/src/pyasl/asl/aslExt_1/ballesterosBV_T.py - const float t0 = 4600.F; - const float a = 0.92F; - const float b = 1.7F; - const float c = 0.62F; - float tEff = t0 * (1.0 / (a * bv + b) + 1.0 / (a * bv + c)); - - // distance in parsec --- some have parallax of zero; assume a - // large distance in those cases + // Distance in parsec --- some have parallax of zero; assume a large distance in those cases. float fDist = 1000.F; if (it->mParallax > 0.F) { @@ -835,8 +859,8 @@ void Stars::buildStarVAO() { data[index] = starPos[0]; data[index + 1] = starPos[1]; data[index + 2] = starPos[2]; - data[index + 3] = tEff; - data[index + 4] = it->mVMagnitude - 5.F * std::log10(fDist / 10.F); + data[index + 3] = it->mTEff; + data[index + 4] = it->mMagnitude - 5.F * std::log10(fDist / 10.F); } mStarVBO.Bind(GL_ARRAY_BUFFER); diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index 8cfccd27c..ba29709ca 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -35,17 +35,16 @@ class Stars : public IVistaOpenGLDraw { /// http://cdsarc.u-strasbg.fr/viz-bin/Cat?cat=I%2F239 /// Tycho2 can be obtained from: /// http://cdsarc.u-strasbg.fr/cgi-bin/myqcat3?I/259/ - enum class CatalogType { eHipparcos = 0, eTycho, eTycho2, eCount }; + enum class CatalogType { eHipparcos = 0, eTycho, eTycho2, eGaia, eCount }; /// The required columns of each catalog. The position of each column in each catalog is /// configured with the static member COLUMN_MAPPING at the bottom of this file. enum class CatalogColumn { - eVmag = 0, ///< visual magnitude - eBmag, ///< blue magnitude - ePara, ///< trigonometric parallax - eRect, ///< rectascension - eDecl, ///< declination - eHipp, ///< hipparcos number + eMag = 0, ///< visual magnitude + ePara, ///< trigonometric parallax + eRect, ///< rectascension + eDecl, ///< declination + eHipp, ///< hipparcos number eCount }; @@ -134,8 +133,8 @@ class Stars : public IVistaOpenGLDraw { private: /// Data structure of one record from star catalog. struct Star { - float mVMagnitude; - float mBMagnitude; + float mMagnitude; + float mTEff; float mAscension; float mDeclination; float mParallax; From e9890f0486f8b1fd12be7eca305325a983e431e8 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 07:49:51 +0100 Subject: [PATCH 43/57] :wrench: Decrease default magnitude limit --- plugins/csp-stars/gui/js/csp-stars.js | 2 +- plugins/csp-stars/gui/stars_settings.html | 2 +- plugins/csp-stars/src/Plugin.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/csp-stars/gui/js/csp-stars.js b/plugins/csp-stars/gui/js/csp-stars.js index 3556ef7c4..3fa70f05e 100644 --- a/plugins/csp-stars/gui/js/csp-stars.js +++ b/plugins/csp-stars/gui/js/csp-stars.js @@ -19,7 +19,7 @@ * @inheritDoc */ init() { - CosmoScout.gui.initSlider("stars.setMagnitude", -10.0, 20.0, 0.1, [-5, 15]); + CosmoScout.gui.initSlider("stars.setMagnitude", -10.0, 20.0, 0.1, [-5, 13]); CosmoScout.gui.initSlider("stars.setSize", 0.01, 1, 0.01, [0.05]); CosmoScout.gui.initSlider("stars.setLuminanceBoost", 0.0, 20.0, 0.1, [0]); } diff --git a/plugins/csp-stars/gui/stars_settings.html b/plugins/csp-stars/gui/stars_settings.html index fc1bae3c4..3b07b9aa6 100644 --- a/plugins/csp-stars/gui/stars_settings.html +++ b/plugins/csp-stars/gui/stars_settings.html @@ -77,6 +77,7 @@ name="star_draw_mode" type="radio" data-callback="stars.setDrawMode7" + checked /> SR-Points @@ -97,7 +98,6 @@ name="star_draw_mode" type="radio" data-callback="stars.setDrawMode5" - checked /> Glare Disc diff --git a/plugins/csp-stars/src/Plugin.hpp b/plugins/csp-stars/src/Plugin.hpp index f515c9719..0b87224e8 100644 --- a/plugins/csp-stars/src/Plugin.hpp +++ b/plugins/csp-stars/src/Plugin.hpp @@ -40,7 +40,7 @@ class Plugin : public cs::core::PluginBase { cs::utils::DefaultProperty mLuminanceMultiplicator{0.F}; cs::utils::DefaultProperty mDrawMode{Stars::DrawMode::eGlareDisc}; cs::utils::DefaultProperty mSize{0.05F}; - cs::utils::DefaultProperty mMagnitudeRange{glm::vec2(-5.F, 15.F)}; + cs::utils::DefaultProperty mMagnitudeRange{glm::vec2(-5.F, 13.F)}; }; void init() override; From a60de8b72d6a5df9dd0e39a89403d9d56857337e Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 07:50:21 +0100 Subject: [PATCH 44/57] :wrench: Use SR stars per default --- plugins/csp-stars/src/Plugin.hpp | 2 +- plugins/csp-stars/src/Stars.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/csp-stars/src/Plugin.hpp b/plugins/csp-stars/src/Plugin.hpp index 0b87224e8..193f2c8cf 100644 --- a/plugins/csp-stars/src/Plugin.hpp +++ b/plugins/csp-stars/src/Plugin.hpp @@ -38,7 +38,7 @@ class Plugin : public cs::core::PluginBase { cs::utils::DefaultProperty mEnableCelestialGrid{false}; cs::utils::DefaultProperty mEnableStarFigures{false}; cs::utils::DefaultProperty mLuminanceMultiplicator{0.F}; - cs::utils::DefaultProperty mDrawMode{Stars::DrawMode::eGlareDisc}; + cs::utils::DefaultProperty mDrawMode{Stars::DrawMode::eSRPoint}; cs::utils::DefaultProperty mSize{0.05F}; cs::utils::DefaultProperty mMagnitudeRange{glm::vec2(-5.F, 13.F)}; }; diff --git a/plugins/csp-stars/src/Stars.hpp b/plugins/csp-stars/src/Stars.hpp index ba29709ca..c21c2f108 100644 --- a/plugins/csp-stars/src/Stars.hpp +++ b/plugins/csp-stars/src/Stars.hpp @@ -177,7 +177,7 @@ class Stars : public IVistaOpenGLDraw { std::vector mStars; std::map mCatalogs; - DrawMode mDrawMode = DrawMode::eGlareDisc; + DrawMode mDrawMode = DrawMode::eSRPoint; bool mShaderDirty = true; bool mEnableHDR = true; From 555889e7a47a248fed52513f67164365b9319268 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 07:50:49 +0100 Subject: [PATCH 45/57] :wrench: Increase default glare amount --- resources/gui/cosmoscout.html | 2 +- src/cs-core/Settings.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/gui/cosmoscout.html b/resources/gui/cosmoscout.html index 97fff3f72..aadfbcbaa 100644 --- a/resources/gui/cosmoscout.html +++ b/resources/gui/cosmoscout.html @@ -934,7 +934,7 @@ CosmoScout.gui.initSlider("graphics.setAmbientLight", 0.0, 1.0, 0.001, [0.0]); CosmoScout.gui.initSlider("graphics.setAmbientOcclusion", 0.0, 1.0, 0.001, [0.5]); CosmoScout.gui.initSlider("graphics.setExposureRange", -30.0, 30, 0.1, [-12, 9]); - CosmoScout.gui.initSlider("graphics.setGlareIntensity", 0.0, 1, 0.01, [0.2]); + CosmoScout.gui.initSlider("graphics.setGlareIntensity", 0.0, 1, 0.01, [0.3]); CosmoScout.gui.initSlider("graphics.setGlareQuality", 0, 5, 1, [1]); $(document).on('click', '.item-create-button', function () { diff --git a/src/cs-core/Settings.hpp b/src/cs-core/Settings.hpp index 2a657413b..b5a408013 100644 --- a/src/cs-core/Settings.hpp +++ b/src/cs-core/Settings.hpp @@ -443,7 +443,7 @@ class CS_CORE_EXPORT Settings { /// The amount of artifical glare. Has no effect if HDR rendering is disabled. This should be in /// the range 0-1. A value of zero disables the glare. - utils::DefaultProperty pGlareIntensity{0.2F}; + utils::DefaultProperty pGlareIntensity{0.3F}; /// Higher values produce a smoother glare. utils::DefaultProperty pGlareQuality{1}; From 7c7ec0aabf9e7797eedc7d1e8c1a6ad5c538a2ae Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 14:14:53 +0100 Subject: [PATCH 46/57] :sparkles: Apply clang-format --- src/cs-graphics/GlareMipMap.hpp | 4 ++-- src/cs-graphics/HDRBuffer.hpp | 7 ++++--- src/cs-graphics/ToneMappingNode.hpp | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index f1bc95000..d5ea8a747 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -24,10 +24,10 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { ~GlareMipMap() override; GlareMipMap(GlareMipMap const& other) = delete; - GlareMipMap(GlareMipMap&& other) = delete; + GlareMipMap(GlareMipMap&& other) = delete; GlareMipMap& operator=(GlareMipMap const& other) = delete; - GlareMipMap& operator=(GlareMipMap&& other) = delete; + GlareMipMap& operator=(GlareMipMap&& other) = delete; /// Perform the glare calculation by parallel reduction of the HDR values. This is a costly /// operation and should only be called once a frame. diff --git a/src/cs-graphics/HDRBuffer.hpp b/src/cs-graphics/HDRBuffer.hpp index 2a848a010..0b1aa331b 100644 --- a/src/cs-graphics/HDRBuffer.hpp +++ b/src/cs-graphics/HDRBuffer.hpp @@ -41,10 +41,10 @@ class CS_GRAPHICS_EXPORT HDRBuffer { explicit HDRBuffer(uint32_t multiSamples, bool highPrecision = true); HDRBuffer(HDRBuffer const& other) = delete; - HDRBuffer(HDRBuffer&& other) = delete; + HDRBuffer(HDRBuffer&& other) = delete; HDRBuffer& operator=(HDRBuffer const& other) = delete; - HDRBuffer& operator=(HDRBuffer&& other) = delete; + HDRBuffer& operator=(HDRBuffer&& other) = delete; virtual ~HDRBuffer(); @@ -76,7 +76,8 @@ class CS_GRAPHICS_EXPORT HDRBuffer { float getTotalLuminance() const; float getMaximumLuminance() const; - /// Update and access the GlareMipMap. + /// Update and access the GlareMipMap. The maximum luminance of the current frame is required to + /// normalize the glare. void updateGlareMipMap(float maxLuminance); VistaTexture* getGlareMipMap() const; diff --git a/src/cs-graphics/ToneMappingNode.hpp b/src/cs-graphics/ToneMappingNode.hpp index 8ff5f1c48..48824f7bd 100644 --- a/src/cs-graphics/ToneMappingNode.hpp +++ b/src/cs-graphics/ToneMappingNode.hpp @@ -36,10 +36,10 @@ class CS_GRAPHICS_EXPORT ToneMappingNode : public IVistaOpenGLDraw, public Vista explicit ToneMappingNode(std::shared_ptr hdrBuffer); ToneMappingNode(ToneMappingNode const& other) = delete; - ToneMappingNode(ToneMappingNode&& other) = delete; + ToneMappingNode(ToneMappingNode&& other) = delete; ToneMappingNode& operator=(ToneMappingNode const& other) = delete; - ToneMappingNode& operator=(ToneMappingNode&& other) = delete; + ToneMappingNode& operator=(ToneMappingNode&& other) = delete; ~ToneMappingNode() override; From f45fe2ddce345d012396930ce90171a02cfd34c8 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 14:16:33 +0100 Subject: [PATCH 47/57] :wrench: Remove implicit conversion --- plugins/csp-stars/src/Stars.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index 5a699bb41..a9a694578 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -680,8 +680,8 @@ bool Stars::readStarsFromCatalog(CatalogType type, std::string const& filename) successStoreData &= fromString(items[GbpMinusGrpColumn], GbpMinusGrp); // https://doi.org/10.1051/0004-6361/201015441 - float logTeff = 3.999 - 0.654 * GbpMinusGrp + 0.709 * std::pow(GbpMinusGrp, 2) - - 0.316 * std::pow(GbpMinusGrp, 3); + float logTeff = 3.999F - 0.654F * GbpMinusGrp + 0.709F * std::pow(GbpMinusGrp, 2.F) - + 0.316F * std::pow(GbpMinusGrp, 3.F); star.mTEff = std::pow(10, logTeff); } else { @@ -698,7 +698,7 @@ bool Stars::readStarsFromCatalog(CatalogType type, std::string const& filename) const float a = 0.92F; const float b = 1.7F; const float c = 0.62F; - star.mTEff = t0 * (1.0 / (a * bv + b) + 1.0 / (a * bv + c)); + star.mTEff = t0 * (1.0F / (a * bv + b) + 1.0F / (a * bv + c)); } if (successStoreData) { From 1e49719f45327bd79e1cee1615aab5f1f9bee162 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 14:23:00 +0100 Subject: [PATCH 48/57] :sparkles: Apply clang-format --- src/cs-graphics/GlareMipMap.hpp | 2 +- src/cs-graphics/HDRBuffer.hpp | 2 +- src/cs-graphics/ToneMappingNode.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cs-graphics/GlareMipMap.hpp b/src/cs-graphics/GlareMipMap.hpp index d5ea8a747..c9fdc143e 100644 --- a/src/cs-graphics/GlareMipMap.hpp +++ b/src/cs-graphics/GlareMipMap.hpp @@ -24,7 +24,7 @@ class CS_GRAPHICS_EXPORT GlareMipMap : public VistaTexture { ~GlareMipMap() override; GlareMipMap(GlareMipMap const& other) = delete; - GlareMipMap(GlareMipMap&& other) = delete; + GlareMipMap(GlareMipMap&& other) = delete; GlareMipMap& operator=(GlareMipMap const& other) = delete; GlareMipMap& operator=(GlareMipMap&& other) = delete; diff --git a/src/cs-graphics/HDRBuffer.hpp b/src/cs-graphics/HDRBuffer.hpp index 0b1aa331b..962046c36 100644 --- a/src/cs-graphics/HDRBuffer.hpp +++ b/src/cs-graphics/HDRBuffer.hpp @@ -41,7 +41,7 @@ class CS_GRAPHICS_EXPORT HDRBuffer { explicit HDRBuffer(uint32_t multiSamples, bool highPrecision = true); HDRBuffer(HDRBuffer const& other) = delete; - HDRBuffer(HDRBuffer&& other) = delete; + HDRBuffer(HDRBuffer&& other) = delete; HDRBuffer& operator=(HDRBuffer const& other) = delete; HDRBuffer& operator=(HDRBuffer&& other) = delete; diff --git a/src/cs-graphics/ToneMappingNode.hpp b/src/cs-graphics/ToneMappingNode.hpp index 48824f7bd..c2a9d33c5 100644 --- a/src/cs-graphics/ToneMappingNode.hpp +++ b/src/cs-graphics/ToneMappingNode.hpp @@ -36,7 +36,7 @@ class CS_GRAPHICS_EXPORT ToneMappingNode : public IVistaOpenGLDraw, public Vista explicit ToneMappingNode(std::shared_ptr hdrBuffer); ToneMappingNode(ToneMappingNode const& other) = delete; - ToneMappingNode(ToneMappingNode&& other) = delete; + ToneMappingNode(ToneMappingNode&& other) = delete; ToneMappingNode& operator=(ToneMappingNode const& other) = delete; ToneMappingNode& operator=(ToneMappingNode&& other) = delete; From 24e65837f0767e5fe40fd3eb13b7307937a589a3 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 15:07:03 +0100 Subject: [PATCH 49/57] :memo: Add some comments --- plugins/csp-stars/shaders/starSnippets.glsl | 110 ++++++++++++------ plugins/csp-stars/shaders/starsBillboard.frag | 3 +- plugins/csp-stars/shaders/starsBillboard.vert | 10 +- plugins/csp-stars/shaders/starsOnePixel.frag | 23 +--- plugins/csp-stars/shaders/starsOnePixel.vert | 8 +- plugins/csp-stars/shaders/starsSRBlit.frag | 4 + plugins/csp-stars/shaders/starsSRBlit.vert | 1 + plugins/csp-stars/shaders/starsSRPoint.comp | 99 +++++++--------- plugins/csp-stars/src/Stars.cpp | 29 +++-- 9 files changed, 151 insertions(+), 136 deletions(-) diff --git a/plugins/csp-stars/shaders/starSnippets.glsl b/plugins/csp-stars/shaders/starSnippets.glsl index 126db03f8..5fecba482 100644 --- a/plugins/csp-stars/shaders/starSnippets.glsl +++ b/plugins/csp-stars/shaders/starSnippets.glsl @@ -5,66 +5,95 @@ // SPDX-FileCopyrightText: German Aerospace Center (DLR) // SPDX-License-Identifier: MIT -// https://onlinelibrary.wiley.com/doi/10.1002/asna.202113868 -const float tMin = 2300; -const float tMax = 12000; -const int sSpectralColorsN = 73; -const vec3 sSpectralColors[73] = vec3[](vec3(1.0, 0.409, 0.078), vec3(1.0, 0.432, 0.093), - vec3(1.0, 0.455, 0.109), vec3(1.0, 0.476, 0.126), vec3(1.0, 0.497, 0.144), - vec3(1.0, 0.518, 0.163), vec3(1.0, 0.537, 0.182), vec3(1.0, 0.557, 0.202), - vec3(1.0, 0.575, 0.223), vec3(1.0, 0.593, 0.244), vec3(1.0, 0.611, 0.266), - vec3(1.0, 0.627, 0.289), vec3(1.0, 0.644, 0.311), vec3(1.0, 0.66, 0.335), - vec3(1.0, 0.675, 0.358), vec3(1.0, 0.69, 0.382), vec3(1.0, 0.704, 0.405), - vec3(1.0, 0.718, 0.429), vec3(1.0, 0.732, 0.454), vec3(1.0, 0.745, 0.478), - vec3(1.0, 0.758, 0.502), vec3(1.0, 0.77, 0.527), vec3(1.0, 0.782, 0.551), - vec3(1.0, 0.794, 0.575), vec3(1.0, 0.806, 0.599), vec3(1.0, 0.817, 0.624), - vec3(1.0, 0.827, 0.648), vec3(1.0, 0.838, 0.672), vec3(1.0, 0.848, 0.696), - vec3(1.0, 0.858, 0.719), vec3(1.0, 0.867, 0.743), vec3(1.0, 0.877, 0.766), - vec3(1.0, 0.886, 0.789), vec3(1.0, 0.894, 0.812), vec3(1.0, 0.903, 0.835), - vec3(1.0, 0.911, 0.858), vec3(1.0, 0.919, 0.88), vec3(1.0, 0.927, 0.902), - vec3(1.0, 0.935, 0.924), vec3(1.0, 0.942, 0.946), vec3(1.0, 0.95, 0.967), - vec3(1.0, 0.957, 0.989), vec3(0.991, 0.955, 1.0), vec3(0.971, 0.942, 1.0), - vec3(0.952, 0.93, 1.0), vec3(0.934, 0.918, 1.0), vec3(0.917, 0.907, 1.0), - vec3(0.901, 0.896, 1.0), vec3(0.87, 0.876, 1.0), vec3(0.843, 0.858, 1.0), - vec3(0.817, 0.841, 1.0), vec3(0.794, 0.825, 1.0), vec3(0.773, 0.81, 1.0), - vec3(0.753, 0.797, 1.0), vec3(0.735, 0.784, 1.0), vec3(0.718, 0.772, 1.0), - vec3(0.703, 0.761, 1.0), vec3(0.688, 0.75, 1.0), vec3(0.674, 0.741, 1.0), - vec3(0.662, 0.731, 1.0), vec3(0.65, 0.723, 1.0), vec3(0.639, 0.714, 1.0), - vec3(0.628, 0.706, 1.0), vec3(0.618, 0.699, 1.0), vec3(0.609, 0.692, 1.0), - vec3(0.6, 0.685, 1.0), vec3(0.592, 0.679, 1.0), vec3(0.584, 0.673, 1.0), - vec3(0.577, 0.667, 1.0), vec3(0.57, 0.662, 1.0), vec3(0.563, 0.657, 1.0), - vec3(0.557, 0.652, 1.0), vec3(0.55, 0.647, 1.0)); +const float cParsecToMeter = 3.08567758e16; +// Returns one value from the spectral color table based on the given temperature. +// https://onlinelibrary.wiley.com/doi/10.1002/asna.202113868 vec3 getStarColor(float temperature) { + const float tMin = 2300; + const float tMax = 12000; + + const int cSpectralColorsN = 73; + const vec3 cSpectralColors[73] = vec3[](vec3(1.0, 0.409, 0.078), vec3(1.0, 0.432, 0.093), + vec3(1.0, 0.455, 0.109), vec3(1.0, 0.476, 0.126), vec3(1.0, 0.497, 0.144), + vec3(1.0, 0.518, 0.163), vec3(1.0, 0.537, 0.182), vec3(1.0, 0.557, 0.202), + vec3(1.0, 0.575, 0.223), vec3(1.0, 0.593, 0.244), vec3(1.0, 0.611, 0.266), + vec3(1.0, 0.627, 0.289), vec3(1.0, 0.644, 0.311), vec3(1.0, 0.66, 0.335), + vec3(1.0, 0.675, 0.358), vec3(1.0, 0.69, 0.382), vec3(1.0, 0.704, 0.405), + vec3(1.0, 0.718, 0.429), vec3(1.0, 0.732, 0.454), vec3(1.0, 0.745, 0.478), + vec3(1.0, 0.758, 0.502), vec3(1.0, 0.77, 0.527), vec3(1.0, 0.782, 0.551), + vec3(1.0, 0.794, 0.575), vec3(1.0, 0.806, 0.599), vec3(1.0, 0.817, 0.624), + vec3(1.0, 0.827, 0.648), vec3(1.0, 0.838, 0.672), vec3(1.0, 0.848, 0.696), + vec3(1.0, 0.858, 0.719), vec3(1.0, 0.867, 0.743), vec3(1.0, 0.877, 0.766), + vec3(1.0, 0.886, 0.789), vec3(1.0, 0.894, 0.812), vec3(1.0, 0.903, 0.835), + vec3(1.0, 0.911, 0.858), vec3(1.0, 0.919, 0.88), vec3(1.0, 0.927, 0.902), + vec3(1.0, 0.935, 0.924), vec3(1.0, 0.942, 0.946), vec3(1.0, 0.95, 0.967), + vec3(1.0, 0.957, 0.989), vec3(0.991, 0.955, 1.0), vec3(0.971, 0.942, 1.0), + vec3(0.952, 0.93, 1.0), vec3(0.934, 0.918, 1.0), vec3(0.917, 0.907, 1.0), + vec3(0.901, 0.896, 1.0), vec3(0.87, 0.876, 1.0), vec3(0.843, 0.858, 1.0), + vec3(0.817, 0.841, 1.0), vec3(0.794, 0.825, 1.0), vec3(0.773, 0.81, 1.0), + vec3(0.753, 0.797, 1.0), vec3(0.735, 0.784, 1.0), vec3(0.718, 0.772, 1.0), + vec3(0.703, 0.761, 1.0), vec3(0.688, 0.75, 1.0), vec3(0.674, 0.741, 1.0), + vec3(0.662, 0.731, 1.0), vec3(0.65, 0.723, 1.0), vec3(0.639, 0.714, 1.0), + vec3(0.628, 0.706, 1.0), vec3(0.618, 0.699, 1.0), vec3(0.609, 0.692, 1.0), + vec3(0.6, 0.685, 1.0), vec3(0.592, 0.679, 1.0), vec3(0.584, 0.673, 1.0), + vec3(0.577, 0.667, 1.0), vec3(0.57, 0.662, 1.0), vec3(0.563, 0.657, 1.0), + vec3(0.557, 0.652, 1.0), vec3(0.55, 0.647, 1.0)); + float t = clamp((temperature - tMin) / (tMax - tMin), 0.0, 1.0); - int idx = int(t * (sSpectralColorsN - 1)); - return sSpectralColors[idx]; + int idx = int(t * (cSpectralColorsN - 1)); + return cSpectralColors[idx]; } +// Returns the logarithm to the base 10. float log10(float x) { return log(x) / log(10.0); } +// Returns the apparent magnitude of a star based on its absolute magnitude and distance in parsecs. float getApparentMagnitude(float absMagnitude, float distInParsce) { return absMagnitude + 5.0 * log10(distInParsce / 10.0); } -// formula from https://en.wikipedia.org/wiki/Surface_brightness +// Formula from https://en.wikipedia.org/wiki/Surface_brightness. float magnitudeToLuminance(float apparentMagnitude, float solidAngle) { const float steradiansToSquareArcSecs = 4.25e10; float surfaceBrightness = apparentMagnitude + 2.5 * log10(solidAngle * steradiansToSquareArcSecs); return 10.8e4 * pow(10, -0.4 * surfaceBrightness); } -vec3 SRGBtoLINEAR(vec3 srgbIn) { - vec3 bLess = step(vec3(0.04045), srgbIn); - return mix(srgbIn / vec3(12.92), pow((srgbIn + vec3(0.055)) / vec3(1.055), vec3(2.4)), bLess); -} - +// Maps a value from one range to another. float mapRange(float value, float oldMin, float oldMax, float newMin, float newMax) { return newMin + (clamp(value, oldMin, oldMax) - oldMin) * (newMax - newMin) / (oldMax - oldMin); } +// Returns the solid angle of the triangle defined by the tips of the vectors a, b and c. +float getSolidAngle(vec3 a, vec3 b, vec3 c) { + return 2 * atan(abs(dot(a, cross(b, c))) / (1 + dot(a, b) + dot(a, c) + dot(b, c))); +} + +// Returns the solid angle of the pixel at screenSpacePosition. +float getSolidAngleOfPixel(vec4 screenSpacePosition, vec2 resolution, mat4 invProjection) { + vec2 pixel = vec2(1.0) / resolution; + vec4 pixelCorners[4] = vec4[4](screenSpacePosition + vec4(-pixel.x, -pixel.y, 0, 0), + screenSpacePosition + vec4(+pixel.x, -pixel.y, 0, 0), + screenSpacePosition + vec4(+pixel.x, +pixel.y, 0, 0), + screenSpacePosition + vec4(-pixel.x, +pixel.y, 0, 0)); + + for (int i = 0; i < 4; ++i) { + pixelCorners[i] = invProjection * pixelCorners[i]; + pixelCorners[i].xyz = normalize(pixelCorners[i].xyz); + } + + return getSolidAngle(pixelCorners[0].xyz, pixelCorners[1].xyz, pixelCorners[2].xyz) + + getSolidAngle(pixelCorners[0].xyz, pixelCorners[2].xyz, pixelCorners[3].xyz); +} + +// Returns the observer position in parsecs based on the inverse modelview matrix. +vec3 getObserverPosition(mat4 invMV) { + return (invMV * vec4(0, 0, 0, 1) / cParsecToMeter).xyz; +} + // http://filmicworlds.com/blog/filmic-tonemapping-operators/ float A = 0.15; float B = 0.50; @@ -76,4 +105,9 @@ float W = 11.2; vec3 Uncharted2Tonemap(vec3 x) { return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; -} \ No newline at end of file +} + +vec3 SRGBtoLINEAR(vec3 srgbIn) { + vec3 bLess = step(vec3(0.04045), srgbIn); + return mix(srgbIn / vec3(12.92), pow((srgbIn + vec3(0.055)) / vec3(1.055), vec3(2.4)), bLess); +} diff --git a/plugins/csp-stars/shaders/starsBillboard.frag b/plugins/csp-stars/shaders/starsBillboard.frag index a9adda9f1..6edec9ab0 100644 --- a/plugins/csp-stars/shaders/starsBillboard.frag +++ b/plugins/csp-stars/shaders/starsBillboard.frag @@ -75,6 +75,7 @@ void main() { oLuminance = vec4(vColor, 1.0); #ifndef ENABLE_HDR - oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); + // Random exposure adjustment to make the stars look good in non-HDR mode. + oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 4e8); #endif } \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsBillboard.vert b/plugins/csp-stars/shaders/starsBillboard.vert index 7db065ca6..64532b69a 100644 --- a/plugins/csp-stars/shaders/starsBillboard.vert +++ b/plugins/csp-stars/shaders/starsBillboard.vert @@ -19,11 +19,9 @@ out float vTemperature; out float vMagnitude; void main() { - const float parsecToMeter = 3.08567758e16; - vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; + vec3 observerPos = getObserverPosition(uInvMV); + vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); + vTemperature = inTemperature; - vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); - vTemperature = inTemperature; - - gl_Position = uMatMV * vec4(inPos * parsecToMeter, 1); + gl_Position = uMatMV * vec4(inPos * cParsecToMeter, 1); } \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsOnePixel.frag b/plugins/csp-stars/shaders/starsOnePixel.frag index 39f7cf902..621552627 100644 --- a/plugins/csp-stars/shaders/starsOnePixel.frag +++ b/plugins/csp-stars/shaders/starsOnePixel.frag @@ -21,26 +21,6 @@ uniform float uSolidAngle; // outputs out vec4 oLuminance; -float getSolidAngle(vec3 a, vec3 b, vec3 c) { - return 2 * atan(abs(dot(a, cross(b, c))) / (1 + dot(a, b) + dot(a, c) + dot(b, c))); -} - -float getSolidAngleOfPixel(vec4 screenSpacePosition, vec2 resolution, mat4 invProjection) { - vec2 pixel = vec2(1.0) / resolution; - vec4 pixelCorners[4] = vec4[4](screenSpacePosition + vec4(-pixel.x, -pixel.y, 0, 0), - screenSpacePosition + vec4(+pixel.x, -pixel.y, 0, 0), - screenSpacePosition + vec4(+pixel.x, +pixel.y, 0, 0), - screenSpacePosition + vec4(-pixel.x, +pixel.y, 0, 0)); - - for (int i = 0; i < 4; ++i) { - pixelCorners[i] = invProjection * pixelCorners[i]; - pixelCorners[i].xyz = normalize(pixelCorners[i].xyz); - } - - return getSolidAngle(pixelCorners[0].xyz, pixelCorners[1].xyz, pixelCorners[2].xyz) + - getSolidAngle(pixelCorners[0].xyz, pixelCorners[2].xyz, pixelCorners[3].xyz); -} - void main() { if (vMagnitude > uMaxMagnitude || vMagnitude < uMinMagnitude) { discard; @@ -52,6 +32,7 @@ void main() { oLuminance = vec4(getStarColor(vTemperature) * luminance * uLuminanceMultiplicator, 1.0); #ifndef ENABLE_HDR - oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 5e8); + // Random exposure adjustment to make the stars look good in non-HDR mode. + oLuminance.rgb = Uncharted2Tonemap(oLuminance.rgb * uSolidAngle * 4e8); #endif } \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsOnePixel.vert b/plugins/csp-stars/shaders/starsOnePixel.vert index ad681c653..387bcbd34 100644 --- a/plugins/csp-stars/shaders/starsOnePixel.vert +++ b/plugins/csp-stars/shaders/starsOnePixel.vert @@ -21,14 +21,12 @@ out vec4 vScreenSpacePos; out float vMagnitude; void main() { - const float parsecToMeter = 3.08567758e16; - vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; - - vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); + vec3 observerPos = getObserverPosition(uInvMV); + vMagnitude = getApparentMagnitude(inAbsMagnitude, length(inPos - observerPos)); vTemperature = inTemperature; - vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); + vScreenSpacePos = uMatP * uMatMV * vec4(inPos * cParsecToMeter, 1); gl_Position = vScreenSpacePos; diff --git a/plugins/csp-stars/shaders/starsSRBlit.frag b/plugins/csp-stars/shaders/starsSRBlit.frag index 291f31247..805cd4bf6 100644 --- a/plugins/csp-stars/shaders/starsSRBlit.frag +++ b/plugins/csp-stars/shaders/starsSRBlit.frag @@ -13,15 +13,19 @@ layout(location = 0) out vec3 oColor; uniform float uLuminanceMultiplicator; void main() { + // Extract the combined star temperature and luminance from the texture. vec2 temperatureLuminance = unpackHalf2x16(texture(uImage, vTexcoords).r); + // Discard any pixels which have not been written to. if (any(lessThanEqual(temperatureLuminance, vec2(0.0)))) { discard; } + // Calculate the color of the stars which contributed to this pixel. oColor = getStarColor(temperatureLuminance.x) * temperatureLuminance.y * uLuminanceMultiplicator; #ifndef ENABLE_HDR + // Random exposure adjustment to make the stars look good in non-HDR mode. oColor = Uncharted2Tonemap(oColor.rgb * 4e3); #endif } \ No newline at end of file diff --git a/plugins/csp-stars/shaders/starsSRBlit.vert b/plugins/csp-stars/shaders/starsSRBlit.vert index f35a53e8f..34063ebcd 100644 --- a/plugins/csp-stars/shaders/starsSRBlit.vert +++ b/plugins/csp-stars/shaders/starsSRBlit.vert @@ -7,6 +7,7 @@ out vec2 vTexcoords; +// This vertex shader is used to draw a fullscreen quad. void main() { vTexcoords = vec2(gl_VertexID & 2, (gl_VertexID << 1) & 2); gl_Position = vec4(vTexcoords * 2.0 - 1.0, 0.0, 1.0); diff --git a/plugins/csp-stars/shaders/starsSRPoint.comp b/plugins/csp-stars/shaders/starsSRPoint.comp index 291a110b8..3f7701b5e 100644 --- a/plugins/csp-stars/shaders/starsSRPoint.comp +++ b/plugins/csp-stars/shaders/starsSRPoint.comp @@ -5,8 +5,6 @@ // SPDX-FileCopyrightText: German Aerospace Center (DLR) // SPDX-License-Identifier: MIT -#line 9 - layout(local_size_x = 256) in; layout(r32ui, binding = 0) coherent uniform uimage2D uOutImage; @@ -20,70 +18,51 @@ struct Star { float absMagnitude; }; +// This SSBO contains all stars. In the other modes, this is bound as VBO. layout(std430, binding = 0) buffer StarSSBO { Star stars[]; }; // uniforms -uniform int uStarCount; -uniform mat4 uMatMV; -uniform mat4 uMatP; -uniform mat4 uInvMV; - +uniform int uStarCount; +uniform mat4 uMatMV; +uniform mat4 uMatP; +uniform mat4 uInvMV; uniform mat4 uInvP; uniform vec2 uResolution; uniform float uMinMagnitude; uniform float uMaxMagnitude; uniform float uSolidAngle; -float getSolidAngle(vec3 a, vec3 b, vec3 c) { - return 2 * atan(abs(dot(a, cross(b, c))) / (1 + dot(a, b) + dot(a, c) + dot(b, c))); -} - -float getSolidAngleOfPixel(vec4 screenSpacePosition, vec2 resolution, mat4 invProjection) { - vec2 pixel = vec2(1.0) / resolution; - vec4 pixelCorners[4] = vec4[4](screenSpacePosition + vec4(-pixel.x, -pixel.y, 0, 0), - screenSpacePosition + vec4(+pixel.x, -pixel.y, 0, 0), - screenSpacePosition + vec4(+pixel.x, +pixel.y, 0, 0), - screenSpacePosition + vec4(-pixel.x, +pixel.y, 0, 0)); - - for (int i = 0; i < 4; ++i) { - pixelCorners[i] = invProjection * pixelCorners[i]; - pixelCorners[i].xyz = normalize(pixelCorners[i].xyz); - } - - return getSolidAngle(pixelCorners[0].xyz, pixelCorners[1].xyz, pixelCorners[2].xyz) + - getSolidAngle(pixelCorners[0].xyz, pixelCorners[2].xyz, pixelCorners[3].xyz); -} - void main() { int index = int(gl_GlobalInvocationID.x); - // Discard any threads outside the output layer. + // Discard any threads outside our star data. if (index >= uStarCount) { return; } - const float parsecToMeter = 3.08567758e16; - vec3 observerPos = (uInvMV * vec4(0, 0, 0, 1) / parsecToMeter).xyz; - - Star star = stars[index]; - vec3 inPos = vec3(star.posX, star.posY, star.posZ); - - vec4 vScreenSpacePos = uMatP * uMatMV * vec4(inPos * parsecToMeter, 1); + Star star = stars[index]; + vec3 inPos = vec3(star.posX, star.posY, star.posZ); + vec4 vScreenSpacePos = uMatP * uMatMV * vec4(inPos * cParsecToMeter, 1); + // Discard stars behind the camera. if (vScreenSpacePos.w <= 0) { return; } vScreenSpacePos /= vScreenSpacePos.w; + // Discard stars outside the frustum. if (vScreenSpacePos.x < -1 || vScreenSpacePos.x > 1 || vScreenSpacePos.y < -1 || vScreenSpacePos.y > 1) { return; } - float vMagnitude = getApparentMagnitude(star.absMagnitude, length(inPos - observerPos)); + vec3 observerPos = getObserverPosition(uInvMV); + float vMagnitude = getApparentMagnitude(star.absMagnitude, length(inPos - observerPos)); + + // Discard stars outside the magnitude range. if (vMagnitude > uMaxMagnitude || vMagnitude < uMinMagnitude) { return; } @@ -91,45 +70,53 @@ void main() { float solidAngle = getSolidAngleOfPixel(vScreenSpacePos, uResolution, uInvP); float luminance = magnitudeToLuminance(vMagnitude, solidAngle); - vec3 vColor = getStarColor(star.tEff); - - ivec2 outSize = imageSize(uOutImage); + // Compute the star coordinates in fractional pixel coordinates. + // Shift the exact projected star coordinates by -.5 such that one refers to the four nearest + // pixels in the following. + ivec2 outSize = imageSize(uOutImage); + vec2 starCoords = ((vScreenSpacePos.xy + 1) / 2) * vec2(outSize) - 0.5; + ivec2 integerStarCoords = ivec2(floor(starCoords)); - vec2 float_star_coords = ((vScreenSpacePos.xy + 1) / 2) * vec2(outSize); + // Compute the fractional part of the star coordinates. + vec2 starWeights = fract(starCoords); - // shift the exact projected star coordinates by -.5 such that one refers to the four nearest - // pixels in the following - vec2 ref_weights = float_star_coords - 0.5; - ivec2 integer_ref_star_coords = ivec2(floor(ref_weights)); - ref_weights = fract(ref_weights); + // Four nearest pixels. + ivec2 targetStarCoords[4] = {integerStarCoords, integerStarCoords + ivec2(1, 0), + integerStarCoords + ivec2(0, 1), integerStarCoords + ivec2(1, 1)}; - // four nearest pixels - ivec2 target_star_coords[4] = {integer_ref_star_coords, integer_ref_star_coords + ivec2(1, 0), - integer_ref_star_coords + ivec2(0, 1), integer_ref_star_coords + ivec2(1, 1)}; + // Linear interpolation weights for the four nearest pixels. + float weights[4] = {(1 - starWeights.x) * (1 - starWeights.y), + starWeights.x * (1 - starWeights.y), (1 - starWeights.x) * starWeights.y, + starWeights.x * starWeights.y}; - float weights[4] = {(1 - ref_weights.x) * (1 - ref_weights.y), - ref_weights.x * (1 - ref_weights.y), (1 - ref_weights.x) * ref_weights.y, - ref_weights.x * ref_weights.y}; - - // normalize weights and add + // Write the contribution of the star to the four nearest pixels. for (int i = 0; i < 4; ++i) { - ivec2 current_target_position = target_star_coords[i]; float oPixelWeightedLuminance = weights[i] * luminance; + // We use an atomic operation to write the new luminance and color temperature to the image. The + // process is as follows: We read the old value from the image, compute the new value, and write + // the result only if the image still contains the old value. Else another thread has already + // written a new value, and we have to try again. uint oldVal = 0; uint newVal = 0; do { - oldVal = imageLoad(uOutImage, current_target_position).r; + oldVal = imageLoad(uOutImage, targetStarCoords[i]).r; + // Unpack the old value. vec2 temperatureLuminance = unpackHalf2x16(oldVal); + // Luminance is additive. float newLuminance = temperatureLuminance.y + oPixelWeightedLuminance; + + // Weighted average of color temperature. float newTEff = (temperatureLuminance.x * temperatureLuminance.y + star.tEff * oPixelWeightedLuminance) / newLuminance; + // Pack the new value. newVal = packHalf2x16(vec2(newTEff, newLuminance)); - } while (oldVal != imageAtomicCompSwap(uOutImage, current_target_position, oldVal, newVal)); + + } while (oldVal != imageAtomicCompSwap(uOutImage, targetStarCoords[i], oldVal, newVal)); } } \ No newline at end of file diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index a9a694578..166a21f90 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -113,7 +113,7 @@ void Stars::setCatalogs(std::map catalogs) { it = mCatalogs.find(CatalogType::eGaia); if (it != mCatalogs.end()) { - // Do not load together with tycho and tycho 2. + // Do not load gaia together with tycho or tycho 2. if (mCatalogs.find(CatalogType::eTycho) == mCatalogs.end() && mCatalogs.find(CatalogType::eTycho2) == mCatalogs.end()) { readStarsFromCatalog(it->first, it->second); @@ -313,7 +313,7 @@ bool Stars::Do() { // mLuminanceMultiplicator even if we are in full daylight. float sceneBrightness = (1.F - mApproximateSceneBrightness) + 0.001F; - // Start are not visible with a low luminance multiplicator + // Stars are not visible with a low luminance multiplicator if (mLuminanceMultiplicator * sceneBrightness < 0.005F) { return true; } @@ -322,14 +322,14 @@ bool Stars::Do() { cs::utils::FrameStats::ScopedSamplesCounter samplesCounter("Render Stars"); cs::utils::FrameStats::ScopedPrimitivesCounter primitivesCounter("Render Stars"); - // save current state of the OpenGL state machine + // Save current state of the OpenGL state machine. glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT); glDepthMask(GL_FALSE); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); - // get matrices + // Get matrices. std::array glMat{}; glGetFloatv(GL_MODELVIEW_MATRIX, glMat.data()); VistaTransformMatrix matModelView(glMat.data(), true); @@ -340,6 +340,7 @@ bool Stars::Do() { if (mShaderDirty) { std::string defines; + // The compute shader needs a higher version for gl_GlobalInvocationID. if (mDrawMode == DrawMode::eSRPoint) { defines += "#version 430\n"; } else { @@ -443,7 +444,7 @@ bool Stars::Do() { mShaderDirty = false; } - // draw background + // Draw background images. if ((mCelestialGridTexture && mBackgroundColor1[3] != 0.F) || (mStarFiguresTexture && mBackgroundColor2[3] != 0.F)) { mBackgroundVAO.Bind(); @@ -455,7 +456,7 @@ bool Stars::Do() { VistaTransformMatrix matMVNoTranslation = matModelView; - // reduce jitter + // Reduce jitter. matMVNoTranslation[0][3] = 0.F; matMVNoTranslation[1][3] = 0.F; matMVNoTranslation[2][3] = 0.F; @@ -514,8 +515,10 @@ bool Stars::Do() { mStarShader.SetUniform(mUniforms.starResolution, static_cast(viewport.at(2)), static_cast(viewport.at(3))); - mStarTexture->Bind(GL_TEXTURE0); - mStarShader.SetUniform(mUniforms.starTexture, 0); + if (mDrawMode == DrawMode::eSprite) { + mStarTexture->Bind(GL_TEXTURE0); + mStarShader.SetUniform(mUniforms.starTexture, 0); + } mStarShader.SetUniform(mUniforms.starMinMagnitude, mMinMagnitude); mStarShader.SetUniform(mUniforms.starMaxMagnitude, mMaxMagnitude); @@ -534,6 +537,10 @@ bool Stars::Do() { glUniformMatrix4fv(mUniforms.starInverseMVMatrix, 1, GL_FALSE, matInverseMV.GetData()); glUniformMatrix4fv(mUniforms.starInversePMatrix, 1, GL_FALSE, matInverseP.GetData()); + // The software rasterization mode is implemented as a compute shader and a blit shader. + // The compute pass accumulates the star luminance and color temperatures in a 2D texture. The + // blit shader then reads the texture, computes the final color for each pixel and writes it to + // the framebuffer. if (mDrawMode == DrawMode::eSRPoint) { // Recreate the render targets if the viewport size changed. auto* viewport = GetVistaSystem()->GetDisplayManager()->GetCurrentRenderInfo()->m_pViewport; @@ -577,11 +584,15 @@ bool Stars::Do() { } } else { + // The other draw modes are very simple. They are either using point primitives or a geometry + // shader to create the star billboards. glDrawArrays(GL_POINTS, 0, static_cast(mStars.size())); mStarVAO.Release(); } - mStarTexture->Unbind(GL_TEXTURE0); + if (mDrawMode == DrawMode::eSprite) { + mStarTexture->Unbind(GL_TEXTURE0); + } mStarShader.Release(); From db996f97d1fdb6f02321b22a7827dd4973a73db8 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 15:08:43 +0100 Subject: [PATCH 50/57] :wrench: Remove implicit conversion --- plugins/csp-stars/src/Stars.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/csp-stars/src/Stars.cpp b/plugins/csp-stars/src/Stars.cpp index 166a21f90..1f780279c 100644 --- a/plugins/csp-stars/src/Stars.cpp +++ b/plugins/csp-stars/src/Stars.cpp @@ -693,7 +693,7 @@ bool Stars::readStarsFromCatalog(CatalogType type, std::string const& filename) // https://doi.org/10.1051/0004-6361/201015441 float logTeff = 3.999F - 0.654F * GbpMinusGrp + 0.709F * std::pow(GbpMinusGrp, 2.F) - 0.316F * std::pow(GbpMinusGrp, 3.F); - star.mTEff = std::pow(10, logTeff); + star.mTEff = std::pow(10.F, logTeff); } else { int bMagColumn = type == CatalogType::eTycho2 ? 17 : 32; From d5e30f4ffa1df0a8db8ab4fd293e7953ff378c9d Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sun, 5 Jan 2025 15:21:51 +0100 Subject: [PATCH 51/57] :memo: Add more comments --- resources/shaders/glare.comp | 22 ++-- resources/shaders/glareComposite.comp | 152 +------------------------- resources/shaders/tonemap.frag | 3 +- 3 files changed, 17 insertions(+), 160 deletions(-) diff --git a/resources/shaders/glare.comp b/resources/shaders/glare.comp index 51df5badd..24783e591 100644 --- a/resources/shaders/glare.comp +++ b/resources/shaders/glare.comp @@ -32,8 +32,10 @@ uniform mat4 uMatInvP; const float PI = 3.14159265359; // Makes four texture look-ups in the input HDRBuffer at the four pixels corresponding to -// the pixel position in the base layer of the output mipmap pyramid. -// For performance reasons, we only use one sample for multisample inputs. +// the pixel position in the base layer of the output mipmap pyramid. For performance +// reasons, we only use one sample for multisample inputs. The luminance is scaled so that +// the maximum luminance in the scene is mapped to 65500, the maximum value representable +// by a half float. vec3 sampleHDRBuffer(ivec2 pos) { ivec2 posTimesTwo = pos << 1; @@ -63,8 +65,9 @@ vec3 sampleHDRBuffer(vec2 pos) { return mix(tA, tB, f.y); } -// Makes four texture look-ups in the input layer of the glare mipmap at the four pixels -// corresponding to the pixel position in the current layer of the mipmap pyramid. +// Makes a bilinearly interpolated texture look-up in the input layer of the glare mipmap +// exactly in the center of the four pixels corresponding to the pixel position in the +// current layer of the mipmap pyramid. vec3 sampleHigherLevel(ivec2 pos) { ivec2 posTimesTwo = pos << 1; int inputLevel = uPass == 0 ? max(0, uLevel - 1) : uLevel; @@ -73,7 +76,7 @@ vec3 sampleHigherLevel(ivec2 pos) { } // Calls the method above four times in order to allow for bilinear interpolation. This is -// required for floating point positions and results in sixteen texture look-ups. +// required for floating point positions and results in four texture look-ups. vec3 sampleHigherLevel(vec2 pos) { ivec2 ipos = ivec2(pos); vec3 tl = sampleHigherLevel(ipos); @@ -86,14 +89,13 @@ vec3 sampleHigherLevel(vec2 pos) { return mix(tA, tB, f.y); } -// Makes just one texture look-ups in the input layer of the glare mipmap at the given +// Makes just one texture look-up in the input layer of the glare mipmap at the given // pixel position. vec3 sampleSameLevel(ivec2 pos) { return texelFetch(uInGlareMipMap, pos, uLevel).rgb; } -// Calls the method above four times in order to allow for bilinear interpolation. This is -// required for floating point positions and results in four texture look-ups. +// Same as above but for floating point positions. This uses bilinear interpolation. vec3 sampleSameLevel(vec2 pos) { ivec2 size = textureSize(uInGlareMipMap, uLevel); return textureLod(uInGlareMipMap, (pos + 0.5) / vec2(size), uLevel).rgb; @@ -160,7 +162,7 @@ void main() { // Primary Component Secondary (orthogonal) Component // // \ | / / --- \ - // -- X -- | O | + // -- X -- | O | // / | \ \ --- / // radial circular // @@ -168,7 +170,7 @@ void main() { // Therefore the implementation below uses two components like the following: // // \ | / / -- \ - // | | | -- -- -- + // | | | -- -- -- // / | \ \ -- / // vertical / radial horizontal / circular diff --git a/resources/shaders/glareComposite.comp b/resources/shaders/glareComposite.comp index d409bfe3e..186f4b633 100644 --- a/resources/shaders/glareComposite.comp +++ b/resources/shaders/glareComposite.comp @@ -14,9 +14,6 @@ layout(rgba32f, binding = 2) writeonly uniform image2D uOutGlare; // See GPU Gems 2: "Fast Third-Order Texture Filtering", Sigg & Hadwiger: // http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html - - - // w0, w1, w2, and w3 are the four cubic B-spline basis functions float w0(float a) { return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); @@ -52,134 +49,7 @@ float h1(float a) { return 1.0 + w3(a) / (w2(a) + w3(a)); } -vec3 cubic_filter(float x, vec3 c0, vec3 c1, vec3 c2, vec3 c3) { - vec3 result = c0 * w0(x); - result += c1 * w1(x); - result += c2 * w2(x); - result += c3 * w3(x); - - return result; -} - - -const int NUM_PADDING_CELLS_OFFSET = 1; -const int NUM_PADDING_CELLS_IN_TOTAL = 3; -const int NUM_SHARED_MEMORY_CELLS_ONE_D = 16+NUM_PADDING_CELLS_IN_TOTAL; -shared vec3 sharedGlareSamples[NUM_SHARED_MEMORY_CELLS_ONE_D][NUM_SHARED_MEMORY_CELLS_ONE_D]; -const int totalNumSharedSamples = NUM_SHARED_MEMORY_CELLS_ONE_D*NUM_SHARED_MEMORY_CELLS_ONE_D; -const int totalNumThreadsInWorkgroup = 16 * 16; - - -vec3 texture2D_exact_bicubic_with_shared_memory(sampler2D tex, vec2 uv, int p_lod) { - float lod = float(p_lod); - vec2 tex_size = textureSize(uGlareMipMap, p_lod); - uv = uv * tex_size + 0.5; - vec2 iuv = floor(uv); - ivec2 iuv_as_int = ivec2(uv - 1.0); - vec2 fuv = fract(uv); - - - - ivec2 globalWorkgroupStartOffset = ivec2(gl_WorkGroupID.xy * (gl_WorkGroupSize.xy) / (1 << p_lod) ) ; - - - - barrier(); - - int total_num_cells_to_sample_per_level = (( int(gl_WorkGroupSize.x) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL) * - (( int(gl_WorkGroupSize.y) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL); - - for(int flatGlareSampleWriteIndex = int(gl_LocalInvocationIndex); - flatGlareSampleWriteIndex < total_num_cells_to_sample_per_level; - flatGlareSampleWriteIndex += totalNumThreadsInWorkgroup) { - - ivec2 unflattenedGlareSampleWriteIndex = - ivec2(flatGlareSampleWriteIndex % (( int(gl_WorkGroupSize.x) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL), - flatGlareSampleWriteIndex / (( int(gl_WorkGroupSize.x) / (1 << p_lod)) + NUM_PADDING_CELLS_IN_TOTAL)); - - //calculate the offset of the shared sample array with respect to the threads local position - ivec2 globalCellID = max(ivec2(0),(unflattenedGlareSampleWriteIndex + globalWorkgroupStartOffset) - NUM_PADDING_CELLS_OFFSET); - - vec2 vTexcoords = (globalCellID + vec2(0.5)) / textureSize(uGlareMipMap, p_lod); - - vTexcoords = vTexcoords * tex_size + 0.5; - ivec2 iuv_as_int = ivec2(vTexcoords - 1.0); - sharedGlareSamples[unflattenedGlareSampleWriteIndex.x][unflattenedGlareSampleWriteIndex.y] - = texelFetch(uGlareMipMap, iuv_as_int, p_lod).rgb; - - - } - - barrier(); - - ivec2 shared_memory_sampling_base_offset = (ivec2(gl_LocalInvocationID.xy / (1 << p_lod) )) + NUM_PADDING_CELLS_OFFSET; - - return cubic_filter( - fuv.y, cubic_filter( - fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y - 1], - sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y - 1], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y - 1], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y - 1] - ), - cubic_filter( - fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y ], - sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y ], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y ], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y ] - ), - cubic_filter( - fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y + 1], - sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y + 1], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y + 1], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y + 1] - ), - cubic_filter( - fuv.x, sharedGlareSamples[shared_memory_sampling_base_offset.x - 1][shared_memory_sampling_base_offset.y + 2], - sharedGlareSamples[shared_memory_sampling_base_offset.x ][shared_memory_sampling_base_offset.y + 2], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 1][shared_memory_sampling_base_offset.y + 2], - sharedGlareSamples[shared_memory_sampling_base_offset.x + 2][shared_memory_sampling_base_offset.y + 2] - ) - ); -} - -vec3 texture2D_exact_bicubic(sampler2D tex, vec2 uv, int p_lod) { - float lod = float(p_lod); - vec2 tex_size = textureSize(uGlareMipMap, p_lod); - uv = uv * tex_size - 0.5; - vec2 iuv = floor(uv); - ivec2 iuv_as_int = ivec2(uv); - vec2 fuv = fract(uv); - - return cubic_filter( - fuv.y, cubic_filter( - fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, -1), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, -1), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, -1), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, -1), p_lod).rgb - ), - cubic_filter( - fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, 0), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, 0), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, 0), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, 0), p_lod).rgb - ), - cubic_filter( - fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, 1), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, 1), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, 1), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, 1), p_lod).rgb - ), - cubic_filter( - fuv.x, texelFetch(uGlareMipMap, iuv_as_int + ivec2(-1, 2), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 0, 2), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 1, 2), p_lod).rgb, - texelFetch(uGlareMipMap, iuv_as_int + ivec2( 2, 2), p_lod).rgb - ) - - ); -} - -vec3 texture2D_fast_bicubic(sampler2D tex, vec2 uv, int p_lod) { +vec3 texture2DBicubic(sampler2D tex, vec2 uv, int p_lod) { float lod = float(p_lod); vec2 tex_size = textureSize(uGlareMipMap, p_lod); vec2 pixel_size = 1.0 / tex_size; @@ -203,6 +73,7 @@ vec3 texture2D_fast_bicubic(sampler2D tex, vec2 uv, int p_lod) { (g1(fuv.y) * (g0x * textureLod(tex, p2, lod).rgb + g1x * textureLod(tex, p3, lod).rgb)); } +// This pass accumulates the glare values from the glare mipmap into the output layer. void main() { ivec2 pixelPos = ivec2(gl_GlobalInvocationID.xy); @@ -225,23 +96,7 @@ void main() { for (int i = 0; i < maxLevels; ++i) { float weight = 1.0 / (1 << i); #ifdef BICUBIC_GLARE_FILTER - - //#define USE_EXACT_PATH_WITHOUT_SHARED_MEMORY - //#define USE_EXACT_PATH_WITH_SHARED_MEMORY - #define USE_APPROXIMATE_FAST_PATH - - #ifdef USE_APPROXIMATE_FAST_PATH - glare += texture2D_fast_bicubic(uGlareMipMap, vTexcoords, i) * weight; - #else - - #ifdef USE_EXACT_PATH_WITHOUT_SHARED_MEMORY - glare += texture2D_exact_bicubic(uGlareMipMap, vTexcoords, i) * weight; - #else - glare += texture2D_exact_bicubic_with_shared_memory(uGlareMipMap, vTexcoords, i) * weight; - #endif - - #endif - + glare += texture2DBicubic(uGlareMipMap, vTexcoords, i) * weight; #else glare += textureLod(uGlareMipMap, vTexcoords, float(i)).rgb * weight; #endif @@ -250,7 +105,6 @@ void main() { vec3 final_glare_value = glare / totalWeight; - // vec3 sampled_glare_base_level_values = texelFetch(uGlareMipMap, pixelPos, 0).rgb; // Finally store the glare value in the output layer of the glare mipmap. imageStore(uOutGlare, pixelPos, vec4(final_glare_value, 0.0)); } \ No newline at end of file diff --git a/resources/shaders/tonemap.frag b/resources/shaders/tonemap.frag index d688d1ed4..7d25997b7 100644 --- a/resources/shaders/tonemap.frag +++ b/resources/shaders/tonemap.frag @@ -67,7 +67,8 @@ void main() { gl_FragDepth = texelFetch(uDepth, ivec2(vTexcoords * textureSize(uDepth, 0)), 0).r; #endif - // Glare is scaled by max luminance. + // Glare is scaled so that it fits the range of a half float. He we scale it back to the original + // range. if (uGlareIntensity > 0) { vec3 glare = texture(uGlareMipMap, vTexcoords, 0).rgb * uMaxLuminance / 65500; color = mix(color, glare, pow(uGlareIntensity, 2.0)); From c34d6bc49935aefa407d3618f22ff39f196861ee Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 6 Jan 2025 09:35:54 +0100 Subject: [PATCH 52/57] :memo: Add changelog entries --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b6be71b1e..4765fabe1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -15,12 +15,14 @@ SPDX-License-Identifier: CC-BY-4.0 #### New Features +- The `csp-stars` plugin now comes with two new rendering modes: Glare Discs and Software-Rasterized Points. The latter is the new default mode and is much faster than the old ones. - The `csp-trajectories` now draws proxies for celestial objects when they are smaller than a few pixels in HDR mode. This makes them visible even if they are very small, such as when looking in the sky at night. This also drastically reduces flickering in HDR mode when the bodies are very small on the screen. - The `/capture` endpoint of the `csp-web-api` now supports an optional `restoreState` parameter. If set to `true`, the size of the window and the visibility of the user interface will be restored after capturing the image. Thanks to [@DanielPSchenk](https://github.com/DanielPSchenk) for this contribution! #### Other Changes - The simulation time can now be adjusted in a continuous manner using the slider below the timeline. +- The glare effect of the HDR rendering has been improved. It is now much faster even if set to a higher quality. - The depth-write for trajectory lines has been disabled. This improves their rendering on many GPUs. They are now not always correctly composited with transparent objects, but this will produce only minor artifacts in most cases. - It is now possible to use the vista debug text rendering again. - New callback `CosmoScout.callbacks.input.reloadDFNs()` to hot reload DFN xml files. From 678a7c1ad1929a9cf577a2c325e17cc553ffad35 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 7 Jan 2025 11:19:27 +0100 Subject: [PATCH 53/57] :wrench: Discard stars outside of the frustum --- plugins/csp-stars/shaders/starsBillboard.geom | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/csp-stars/shaders/starsBillboard.geom b/plugins/csp-stars/shaders/starsBillboard.geom index b00ac73c8..8e06e16a7 100644 --- a/plugins/csp-stars/shaders/starsBillboard.geom +++ b/plugins/csp-stars/shaders/starsBillboard.geom @@ -28,10 +28,22 @@ void main() { iMagnitude = vMagnitude[0]; + // Discard stars that are too bright or too dim. if (iMagnitude > uMaxMagnitude || iMagnitude < uMinMagnitude) { return; } + // Discard stars that are outside the frustum. We only test the center position. + vec4 center = uMatP * vec4(gl_in[0].gl_Position.xyz, 1); + if (center.w <= 0) { + return; + } + + center /= center.w; + if (center.x < -1 || center.x > 1 || center.y < -1 || center.y > 1) { + return; + } + float dist = length(gl_in[0].gl_Position.xyz); vec3 y = vec3(0, 1, 0); vec3 z = gl_in[0].gl_Position.xyz / dist; From b860e43843a5ddb11cb8820e03197589651caeba Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 7 Jan 2025 15:46:15 +0100 Subject: [PATCH 54/57] :wrench: Make glare discs a bit more similar to other modes with glare --- plugins/csp-stars/shaders/starsBillboard.frag | 2 +- plugins/csp-stars/shaders/starsBillboard.geom | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/csp-stars/shaders/starsBillboard.frag b/plugins/csp-stars/shaders/starsBillboard.frag index 6edec9ab0..23b568ea8 100644 --- a/plugins/csp-stars/shaders/starsBillboard.frag +++ b/plugins/csp-stars/shaders/starsBillboard.frag @@ -42,7 +42,7 @@ void main() { #endif #ifdef DRAWMODE_GLARE_DISC - float scaleFac = mapRange(sqrt(luminance), 0, 5, 1.0, 100.0); + float scaleFac = mapRange(sqrt(luminance), 0, 5, 2.0, 100.0); // In this mode, 20% of the brightness is drawn using a small smooth disc in the center // (just like DRAWMODE_SMOOTH_DISC) and the other 80% are drawn as glare using an inverse diff --git a/plugins/csp-stars/shaders/starsBillboard.geom b/plugins/csp-stars/shaders/starsBillboard.geom index 8e06e16a7..b8623229a 100644 --- a/plugins/csp-stars/shaders/starsBillboard.geom +++ b/plugins/csp-stars/shaders/starsBillboard.geom @@ -63,7 +63,7 @@ void main() { #ifdef DRAWMODE_GLARE_DISC float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); - scale *= mapRange(sqrt(luminance), 0, 5, 1.0, 100.0); + scale *= mapRange(sqrt(luminance), 0, 5, 2.0, 100.0); #endif #ifdef DRAWMODE_SPRITE From 9be6710cc24125a0c36a9352bb3a05e4948163de Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 9 Jan 2025 12:12:34 +0100 Subject: [PATCH 55/57] :wrench: Improve glare mode performance --- plugins/csp-stars/shaders/starsBillboard.frag | 2 +- plugins/csp-stars/shaders/starsBillboard.geom | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/csp-stars/shaders/starsBillboard.frag b/plugins/csp-stars/shaders/starsBillboard.frag index 23b568ea8..6edec9ab0 100644 --- a/plugins/csp-stars/shaders/starsBillboard.frag +++ b/plugins/csp-stars/shaders/starsBillboard.frag @@ -42,7 +42,7 @@ void main() { #endif #ifdef DRAWMODE_GLARE_DISC - float scaleFac = mapRange(sqrt(luminance), 0, 5, 2.0, 100.0); + float scaleFac = mapRange(sqrt(luminance), 0, 5, 1.0, 100.0); // In this mode, 20% of the brightness is drawn using a small smooth disc in the center // (just like DRAWMODE_SMOOTH_DISC) and the other 80% are drawn as glare using an inverse diff --git a/plugins/csp-stars/shaders/starsBillboard.geom b/plugins/csp-stars/shaders/starsBillboard.geom index b8623229a..8e06e16a7 100644 --- a/plugins/csp-stars/shaders/starsBillboard.geom +++ b/plugins/csp-stars/shaders/starsBillboard.geom @@ -63,7 +63,7 @@ void main() { #ifdef DRAWMODE_GLARE_DISC float luminance = magnitudeToLuminance(iMagnitude, uSolidAngle); - scale *= mapRange(sqrt(luminance), 0, 5, 2.0, 100.0); + scale *= mapRange(sqrt(luminance), 0, 5, 1.0, 100.0); #endif #ifdef DRAWMODE_SPRITE From faa0375a90688284e0dde289a494685d056377a0 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 9 Jan 2025 14:26:56 +0100 Subject: [PATCH 56/57] :wrench: Rename star types in the UI --- plugins/csp-stars/gui/stars_settings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/csp-stars/gui/stars_settings.html b/plugins/csp-stars/gui/stars_settings.html index 3b07b9aa6..d2f0c5414 100644 --- a/plugins/csp-stars/gui/stars_settings.html +++ b/plugins/csp-stars/gui/stars_settings.html @@ -38,7 +38,7 @@ type="radio" data-callback="stars.setDrawMode0" /> - Points + GL-Points
From e87ce6a57337626979bfc04d9b41a3a70ea49cde Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 23 Jan 2025 12:53:41 +0100 Subject: [PATCH 57/57] :wrench: Apply suggestions from code review Co-authored-by: Jonas Gilg --- plugins/csp-stars/shaders/starSnippets.glsl | 14 +++++++------- resources/shaders/tonemap.frag | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/csp-stars/shaders/starSnippets.glsl b/plugins/csp-stars/shaders/starSnippets.glsl index 5fecba482..7885b3398 100644 --- a/plugins/csp-stars/shaders/starSnippets.glsl +++ b/plugins/csp-stars/shaders/starSnippets.glsl @@ -95,13 +95,13 @@ vec3 getObserverPosition(mat4 invMV) { } // http://filmicworlds.com/blog/filmic-tonemapping-operators/ -float A = 0.15; -float B = 0.50; -float C = 0.10; -float D = 0.20; -float E = 0.02; -float F = 0.30; -float W = 11.2; +const float A = 0.15; +const float B = 0.50; +const float C = 0.10; +const float D = 0.20; +const float E = 0.02; +const float F = 0.30; +const float W = 11.2; vec3 Uncharted2Tonemap(vec3 x) { return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; diff --git a/resources/shaders/tonemap.frag b/resources/shaders/tonemap.frag index 7d25997b7..2a8419a9f 100644 --- a/resources/shaders/tonemap.frag +++ b/resources/shaders/tonemap.frag @@ -34,7 +34,7 @@ float E = 0.02; float F = 0.30; float W = 11.2; -vec3 Uncharted2Tonemap(vec3 x) { +vec3 uncharted2Tonemap(vec3 x) { return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; } @@ -78,8 +78,8 @@ void main() { // Filmic #if TONE_MAPPING_MODE == 2 - color = Uncharted2Tonemap(color); - vec3 whiteScale = vec3(1.0) / Uncharted2Tonemap(vec3(W)); + color = uncharted2Tonemap(color); + vec3 whiteScale = vec3(1.0) / uncharted2Tonemap(vec3(W)); oColor = linear_to_srgb(color * whiteScale); // Gamma only