From 6036e691158677d419acb1b46d5ab36dba23a163 Mon Sep 17 00:00:00 2001 From: Hleb Valoshka <375gnu@gmail.com> Date: Tue, 24 Oct 2023 20:08:51 +0300 Subject: [PATCH 1/4] wip --- shaders/star_frag.glsl | 42 +++++++++++++++++++++-- shaders/star_vert.glsl | 44 +++++++++++++++++++++---- src/celengine/pointstarrenderer.cpp | 20 ++++------- src/celengine/pointstarvertexbuffer.cpp | 7 ++-- src/celengine/render.cpp | 22 +++++++------ 5 files changed, 100 insertions(+), 35 deletions(-) diff --git a/shaders/star_frag.glsl b/shaders/star_frag.glsl index 6f7fe49b08..3f9177ff90 100644 --- a/shaders/star_frag.glsl +++ b/shaders/star_frag.glsl @@ -1,7 +1,43 @@ -uniform sampler2D starTex; -varying vec4 color; +const float degree_per_px = 0.05; +const float br_limit = 1.0 / (255.0 * 12.92); + +varying vec3 v_color; +varying vec3 v_tetha_k; +varying float pointSize; + +float psf_square(float theta, float min_theta, float max_theta, float k) +{ + // Human eye's point source function, optimized to fit a square. + // Lower limit on brightness and angular size: 1 Vega and 0.05 degrees per pixel. + // No upper limits. + + if (theta < min_theta) + return 1.0; // overexposed + + if (theta < max_theta) + { + float brackets = max_theta / theta - 1.0; + return k * brackets * brackets; + } + + return 0.0; +} void main(void) { - gl_FragColor = texture2D(starTex, gl_PointCoord) * color; + float max_theta = v_tetha_k.x; + if (max_theta == -1.0) + { + gl_FragColor = vec4(v_color, 1.0); + } + else + { + float min_theta = v_tetha_k.y; + float k = v_tetha_k.z; + // Option 2: glare square render + vec2 offset = (gl_PointCoord.xy - vec2(0.5)) * pointSize; + float theta = length(offset) * degree_per_px; + + gl_FragColor = vec4(v_color * psf_square(theta, min_theta, max_theta, k), 1.0); + } } diff --git a/shaders/star_vert.glsl b/shaders/star_vert.glsl index 6202ed58f3..e20a343a8b 100644 --- a/shaders/star_vert.glsl +++ b/shaders/star_vert.glsl @@ -1,11 +1,41 @@ -attribute vec3 in_Position; -attribute vec4 in_Color; -attribute float in_PointSize; -varying vec4 color; + +const float degree_per_px = 0.05; +const float br_limit = 1.0 / (255.0 * 12.92); + +varying vec3 v_color; // 12 +varying vec3 v_tetha_k; // 24 +varying float pointSize; // 28 + +uniform vec2 viewportSize; + +attribute vec4 in_Position; +attribute vec3 in_Color; +attribute float in_PointSize; // scaled brightness measured in Vegas + void main(void) { - gl_PointSize = in_PointSize; - color = in_Color; - set_vp(vec4(in_Position, 1.0)); + float linearBr = pow(10.0, 0.4f * in_PointSize) * br_limit; + vec3 color0 = in_Color * (linearBr / in_Color.g); // scaling on brightness and normalizing by green channel + + vec3 check_vec = step(vec3(1.0), color0); // step(edge, x) - For element i of the return value, 0.0 is returned if x[i] < edge[i], and 1.0 is returned otherwise. + float check = check_vec.x + check_vec.y + check_vec.z; + if (check == 0.0) + { + pointSize = 1.0; + v_tetha_k = vec3(-1.0); + } + else + { + float max_br = sqrt(max(color0.r, max(color0.g, color0.b))); + float max_theta = 0.2 * sqrt(max_br); // glare radius + float k = 3.3e-5 * pow(max_theta, -2.5); // common constant, depending originally on star brightness + float min_theta = max_theta / (pow(k, -0.5) + 1.0); + pointSize = floor(max_theta / (sqrt(0.5 * br_limit / (k * max_br)) + 1.0) / degree_per_px); + v_tetha_k = vec3(max_theta, min_theta, k); + } + + gl_PointSize = pointSize; + v_color = color0; + set_vp(in_Position); } diff --git a/src/celengine/pointstarrenderer.cpp b/src/celengine/pointstarrenderer.cpp index 1c4dfa66a6..ea3dbe0457 100644 --- a/src/celengine/pointstarrenderer.cpp +++ b/src/celengine/pointstarrenderer.cpp @@ -11,10 +11,13 @@ #include #include #include +#include #include "pointstarvertexbuffer.h" #include "render.h" #include "pointstarrenderer.h" +#include + using namespace std; using namespace Eigen; @@ -95,19 +98,10 @@ void PointStarRenderer::process(const Star& star, float distance, float appMag) // planets. if (distance > SolarSystemMaxDistance) { - float pointSize, alpha, glareSize, glareAlpha; - float size = BaseStarDiscSize * static_cast(renderer->getScreenDpi()) / 96.0f; - renderer->calculatePointSize(appMag, - size, - pointSize, - alpha, - glareSize, - glareAlpha); - - if (glareSize != 0.0f) - glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), glareSize); - if (pointSize != 0.0f) - starVertexBuffer->addStar(relPos, Color(starColor, alpha), pointSize); + if (appMag < faintestMag) + { + starVertexBuffer->addStar(relPos, starColor, faintestMag - appMag); + } // Place labels for stars brighter than the specified label threshold brightness if (((labelMode & Renderer::StarLabels) != 0) && appMag < labelThresholdMag) diff --git a/src/celengine/pointstarvertexbuffer.cpp b/src/celengine/pointstarvertexbuffer.cpp index 31b98ea4e8..fc48908358 100644 --- a/src/celengine/pointstarvertexbuffer.cpp +++ b/src/celengine/pointstarvertexbuffer.cpp @@ -48,7 +48,7 @@ void PointStarVertexBuffer::startBasicPoints() void PointStarVertexBuffer::render() { - if (m_nStars != 0) + if (m_nStars != 0 && m_prog != nullptr) { makeCurrent(); @@ -69,7 +69,7 @@ void PointStarVertexBuffer::render() void PointStarVertexBuffer::makeCurrent() { - if (current == this || m_prog == nullptr) + if (current == this) return; if (current != nullptr) @@ -79,6 +79,9 @@ void PointStarVertexBuffer::makeCurrent() m_prog->use(); m_prog->setMVPMatrices(m_renderer.getCurrentProjectionMatrix(), m_renderer.getCurrentModelViewMatrix()); + int x, y, w, h; + m_renderer.getViewport(&x, &y, &w, &h); + m_prog->vec2Param("viewportSize") = Eigen::Vector2f(w-x, h-y); if (m_pointSizeFromVertex) { m_prog->samplerParam("starTex") = 0; diff --git a/src/celengine/render.cpp b/src/celengine/render.cpp index ff1a09f9c7..a0f6d06484 100644 --- a/src/celengine/render.cpp +++ b/src/celengine/render.cpp @@ -264,7 +264,7 @@ Renderer::Renderer() : { pointStarVertexBuffer = new PointStarVertexBuffer(*this, 2048); - glareVertexBuffer = new PointStarVertexBuffer(*this, 2048); + //glareVertexBuffer = new PointStarVertexBuffer(*this, 2048); for (int i = 0; i < (int) FontCount; i++) { @@ -277,7 +277,7 @@ Renderer::Renderer() : Renderer::~Renderer() { delete pointStarVertexBuffer; - delete glareVertexBuffer; + //delete glareVertexBuffer; delete shaderManager; m_atmosphereRenderer->deinitGL(); @@ -1795,8 +1795,10 @@ void Renderer::renderObjectAsPoint(const Vector3f& position, gaussianGlareTex->bind(); if (glareSize > gl::maxPointSize) m_largeStarRenderer->render(center, {color, glareAlpha}, glareSize, mvp); + /* else glareVertexBuffer->addStar(center, {color, glareAlpha}, glareSize); + */ } } } @@ -3817,7 +3819,7 @@ void Renderer::renderPointStars(const StarDatabase& starDB, starRenderer.viewNormal = getCameraOrientationf().conjugate() * -Vector3f::UnitZ(); starRenderer.renderList = &renderList; starRenderer.starVertexBuffer = pointStarVertexBuffer; - starRenderer.glareVertexBuffer = glareVertexBuffer; + //starRenderer.glareVertexBuffer = glareVertexBuffer; starRenderer.fov = fov; starRenderer.cosFOV = std::cos(math::degToRad(calcMaxFOV(fov, getAspectRatio())) / 2.0f); @@ -3836,11 +3838,11 @@ void Renderer::renderPointStars(const StarDatabase& starDB, gaussianDiscTex->bind(); starRenderer.starVertexBuffer->setTexture(gaussianDiscTex); starRenderer.starVertexBuffer->setPointScale(screenDpi / 96.0f); - starRenderer.glareVertexBuffer->setTexture(gaussianGlareTex); - starRenderer.glareVertexBuffer->setPointScale(screenDpi / 96.0f); + //starRenderer.glareVertexBuffer->setTexture(gaussianGlareTex); + //starRenderer.glareVertexBuffer->setPointScale(screenDpi / 96.0f); PointStarVertexBuffer::enable(); - starRenderer.glareVertexBuffer->startSprites(); + //starRenderer.glareVertexBuffer->startSprites(); if (starStyle == PointStars) starRenderer.starVertexBuffer->startBasicPoints(); else @@ -3859,7 +3861,7 @@ void Renderer::renderPointStars(const StarDatabase& starDB, faintestMagNight); starRenderer.starVertexBuffer->finish(); - starRenderer.glareVertexBuffer->finish(); + //starRenderer.glareVertexBuffer->finish(); PointStarVertexBuffer::disable(); #ifndef GL_ES @@ -5332,9 +5334,9 @@ Renderer::renderSolarSystemObjects(const Observer &observer, setPipelineState(ps); PointStarVertexBuffer::enable(); - glareVertexBuffer->startSprites(); - glareVertexBuffer->render(); - glareVertexBuffer->finish(); + //glareVertexBuffer->startSprites(); + //glareVertexBuffer->render(); + //glareVertexBuffer->finish(); if (starStyle == PointStars) pointStarVertexBuffer->startBasicPoints(); else From 11ed2a0c3bf24883a42ff036624ac3823831f707 Mon Sep 17 00:00:00 2001 From: Hleb Valoshka <375gnu@gmail.com> Date: Mon, 29 Jan 2024 00:42:22 +0200 Subject: [PATCH 2/4] wip --- shaders/star_frag.glsl | 48 +++++++++++------ shaders/star_vert.glsl | 84 ++++++++++++++++++++++------- src/celengine/pointstarrenderer.cpp | 4 ++ 3 files changed, 102 insertions(+), 34 deletions(-) diff --git a/shaders/star_frag.glsl b/shaders/star_frag.glsl index 3f9177ff90..35fdb0f48a 100644 --- a/shaders/star_frag.glsl +++ b/shaders/star_frag.glsl @@ -1,43 +1,61 @@ const float degree_per_px = 0.05; const float br_limit = 1.0 / (255.0 * 12.92); +// empirical constants +const float a = 0.123; +const float k = 0.0016; varying vec3 v_color; -varying vec3 v_tetha_k; +varying float max_theta; varying float pointSize; +varying float br0; -float psf_square(float theta, float min_theta, float max_theta, float k) +// py: def PSF_Bounded(theta: float, max_theta: float, br_center: float): +// max_theta is common for all pixels so it's set via `varying` in the vertex shader +float psf_bounded(float theta, float br_center) { - // Human eye's point source function, optimized to fit a square. - // Lower limit on brightness and angular size: 1 Vega and 0.05 degrees per pixel. - // No upper limits. + // Human eye's point source function from the research by Greg Spencer et al., optimized to fit a square. + // Lower limit on brightness and angular size: 1 Vega and 0.05 degrees per pixel. No upper limits. - if (theta < min_theta) - return 1.0; // overexposed + // py: if theta == 0: + if (theta == 0) + // py: return br_center + return br_center; // the center is always overexposed (zero division error) + // py: elif theta < max_theta: if (theta < max_theta) { + // py: brackets = max_theta / theta - 1 float brackets = max_theta / theta - 1.0; + // py: return k * brackets * brackets return k * brackets * brackets; } - return 0.0; + // py: return 0. # after max_theta function starts to grow again + return 0.0; // after max_theta function starts to grow again } void main(void) { - float max_theta = v_tetha_k.x; + vec3 glow_colored; if (max_theta == -1.0) { - gl_FragColor = vec4(v_color, 1.0); + // just a one pixel star + glow_colored = v_color; + // py: arr[center[1], center[0]] += scaled_color + gl_FragColor = vec4(glow_colored, 1.0); } else { - float min_theta = v_tetha_k.y; - float k = v_tetha_k.z; // Option 2: glare square render + // py: theta = np.sqrt(xx*xx + yy*yy) * degree_per_px # array of distances to the center + // in fragment shader all points have virtual dimension 1x1, so gl_PointCoord has a value from [0; 1] vec2 offset = (gl_PointCoord.xy - vec2(0.5)) * pointSize; - float theta = length(offset) * degree_per_px; - - gl_FragColor = vec4(v_color * psf_square(theta, min_theta, max_theta, k), 1.0); + float theta = length(offset) * degree_per_px; + // py: glow_bw = PSF_Bounded(theta, max_theta, br0) # in the [0, 1] range, like in Celestia + float glow_bw = psf_bounded(theta, br0); + // py: glow_colored = color * np.repeat(np.expand_dims(glow_bw, axis=2), 3, axis=2) # scaling + glow_colored = v_color * glow_bw; + // py: arr[center[1]+y_min:center[1]+y_max, center[0]+x_min:center[0]+x_max] += glow_colored + gl_FragColor = vec4(glow_colored, 1.0); } } diff --git a/shaders/star_vert.glsl b/shaders/star_vert.glsl index e20a343a8b..5cedabb165 100644 --- a/shaders/star_vert.glsl +++ b/shaders/star_vert.glsl @@ -1,41 +1,87 @@ const float degree_per_px = 0.05; -const float br_limit = 1.0 / (255.0 * 12.92); +// empirical constants +const float a = 0.123; +const float k = 0.0016; +const float exposure = 1.0; -varying vec3 v_color; // 12 -varying vec3 v_tetha_k; // 24 -varying float pointSize; // 28 +// py: max_square_size = 512 # px +const float max_square_size = 256.0; +// py: max_br = (degree_per_px * max_square_size / algorithms.a)**2 / (2*np.pi) +const float max_br = pow((degree_per_px * max_square_size / a), 2.0) / (2.0 * 3.141592653); -uniform vec2 viewportSize; +varying vec3 v_color; +varying float max_theta; +varying float pointSize; +varying float br0; attribute vec4 in_Position; attribute vec3 in_Color; -attribute float in_PointSize; // scaled brightness measured in Vegas +attribute float in_PointSize; +const float color_saturation_limit = 0.1; // The ratio of the minimum color component to the maximum + +//! Normalizes the color by its green value and corrects extreme saturation +// py: def green_normalization(color: np.ndarray): +vec3 green_normalization(vec3 color) +{ + // py: color /= color.max() + // color /= max(color.r, max(color.g, color.b)); // we do this in XYZRGBConverter::convertUnnormalized() + + // py: delta = color_saturation_limit - color.min() + float delta = color_saturation_limit - min(color.r, min(color.g, color.b)); + + // py: if delta > 0: + if (delta > 0) + { + // py: color += delta * (1-color)**2 # desaturating to the saturation limit + vec3 diff = vec3(1.0) - color; + color += delta * (diff * diff); // desaturating to the saturation limit + } + // py: return color / color[1] + return color / color.g; +} void main(void) { - float linearBr = pow(10.0, 0.4f * in_PointSize) * br_limit; - vec3 color0 = in_Color * (linearBr / in_Color.g); // scaling on brightness and normalizing by green channel + // py: linear_br = 10**(-0.4 * star_mag) * exposure # scaled brightness measured in Vegas + // here i use +0.4 because `in_PointSize` is not the actual magnitude but `faintest` - `actual` + br0 = pow(10.0, 0.4 * in_PointSize) * exposure; + + // py: color = auxiliary.green_normalization(color0) + vec3 color = green_normalization(in_Color); + + // py: scaled_color = color * br0 + vec3 scaled_color = color * br0; - vec3 check_vec = step(vec3(1.0), color0); // step(edge, x) - For element i of the return value, 0.0 is returned if x[i] < edge[i], and 1.0 is returned otherwise. - float check = check_vec.x + check_vec.y + check_vec.z; - if (check == 0.0) + // py: if np.all(scaled_color < 1): + if (all(lessThan(scaled_color, vec3(1.0)))) { + // we set color in the fragment shader (using v_color) so here we just set point size to 1px pointSize = 1.0; - v_tetha_k = vec3(-1.0); + // use max_theta == -1 as as signal that the point size is 1px + max_theta = -1.0; } else { - float max_br = sqrt(max(color0.r, max(color0.g, color0.b))); - float max_theta = 0.2 * sqrt(max_br); // glare radius - float k = 3.3e-5 * pow(max_theta, -2.5); // common constant, depending originally on star brightness - float min_theta = max_theta / (pow(k, -0.5) + 1.0); - pointSize = floor(max_theta / (sqrt(0.5 * br_limit / (k * max_br)) + 1.0) / degree_per_px); - v_tetha_k = vec3(max_theta, min_theta, k); + // py: br = np.arctan(br0 / max_br) * max_br # dimmed brightness + float br = atan(br0 / max_br) * max_br; // dimmed brightness + // py: max_theta = a * np.sqrt(br) # glow radius + max_theta = a * sqrt(br); // glow radius + // py: half_sq = floor(max_theta / degree_per_px) + float half_sq = floor(max_theta / degree_per_px); + // py: x_min = -min(half_sq, center[0]) + // py: x_max = min(half_sq+1, width-center[0]) + // py: y_min = -min(half_sq, center[1]) + // py: y_max = min(half_sq+1, hight-center[1]) + // py: x = np.arange(x_min, x_max) + // py: y = np.arange(y_min, y_max) + // py: xx, yy = np.meshgrid(x, y) + // we just set a point size. all iteration over every px is done in the fragment shader + pointSize = 2.0 * half_sq; } gl_PointSize = pointSize; - v_color = color0; + v_color = scaled_color; set_vp(in_Position); } diff --git a/src/celengine/pointstarrenderer.cpp b/src/celengine/pointstarrenderer.cpp index ea3dbe0457..cc176579ac 100644 --- a/src/celengine/pointstarrenderer.cpp +++ b/src/celengine/pointstarrenderer.cpp @@ -37,6 +37,10 @@ PointStarRenderer::PointStarRenderer() : { } +//const float br_limit = 1.0f / (255.0f * 12.92f); +const float br_limit = 1.0f / 255.0f; +const float exposure = 1.0f; + void PointStarRenderer::process(const Star& star, float distance, float appMag) { if (distance > distanceLimit) From 6cbd344aaf47c87cee90ec565d96c224b510dc41 Mon Sep 17 00:00:00 2001 From: Hleb Valoshka <375gnu@gmail.com> Date: Mon, 29 Jan 2024 12:28:53 +0200 Subject: [PATCH 3/4] use br in psf instead of br0, fix point size --- shaders/star_frag.glsl | 10 ++++------ shaders/star_vert.glsl | 10 +++++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/shaders/star_frag.glsl b/shaders/star_frag.glsl index 35fdb0f48a..db6331eb88 100644 --- a/shaders/star_frag.glsl +++ b/shaders/star_frag.glsl @@ -7,7 +7,7 @@ const float k = 0.0016; varying vec3 v_color; varying float max_theta; varying float pointSize; -varying float br0; +varying float br; // py: def PSF_Bounded(theta: float, max_theta: float, br_center: float): // max_theta is common for all pixels so it's set via `varying` in the vertex shader @@ -36,13 +36,11 @@ float psf_bounded(float theta, float br_center) void main(void) { - vec3 glow_colored; if (max_theta == -1.0) { // just a one pixel star - glow_colored = v_color; // py: arr[center[1], center[0]] += scaled_color - gl_FragColor = vec4(glow_colored, 1.0); + gl_FragColor = vec4(v_color, 1.0); } else { @@ -52,9 +50,9 @@ void main(void) vec2 offset = (gl_PointCoord.xy - vec2(0.5)) * pointSize; float theta = length(offset) * degree_per_px; // py: glow_bw = PSF_Bounded(theta, max_theta, br0) # in the [0, 1] range, like in Celestia - float glow_bw = psf_bounded(theta, br0); + float glow_bw = psf_bounded(theta, br); // py: glow_colored = color * np.repeat(np.expand_dims(glow_bw, axis=2), 3, axis=2) # scaling - glow_colored = v_color * glow_bw; + vec3 glow_colored = v_color * glow_bw; // py: arr[center[1]+y_min:center[1]+y_max, center[0]+x_min:center[0]+x_max] += glow_colored gl_FragColor = vec4(glow_colored, 1.0); } diff --git a/shaders/star_vert.glsl b/shaders/star_vert.glsl index 5cedabb165..8b81a79243 100644 --- a/shaders/star_vert.glsl +++ b/shaders/star_vert.glsl @@ -13,7 +13,7 @@ const float max_br = pow((degree_per_px * max_square_size / a), 2.0) / (2.0 * 3. varying vec3 v_color; varying float max_theta; varying float pointSize; -varying float br0; +varying float br; attribute vec4 in_Position; attribute vec3 in_Color; @@ -46,7 +46,7 @@ void main(void) { // py: linear_br = 10**(-0.4 * star_mag) * exposure # scaled brightness measured in Vegas // here i use +0.4 because `in_PointSize` is not the actual magnitude but `faintest` - `actual` - br0 = pow(10.0, 0.4 * in_PointSize) * exposure; + float br0 = pow(10.0, 0.4 * in_PointSize) * exposure; // py: color = auxiliary.green_normalization(color0) vec3 color = green_normalization(in_Color); @@ -65,11 +65,11 @@ void main(void) else { // py: br = np.arctan(br0 / max_br) * max_br # dimmed brightness - float br = atan(br0 / max_br) * max_br; // dimmed brightness + br = atan(br0 / max_br) * max_br; // dimmed brightness // py: max_theta = a * np.sqrt(br) # glow radius max_theta = a * sqrt(br); // glow radius // py: half_sq = floor(max_theta / degree_per_px) - float half_sq = floor(max_theta / degree_per_px); + float half_sq = max_theta / degree_per_px; // py: x_min = -min(half_sq, center[0]) // py: x_max = min(half_sq+1, width-center[0]) // py: y_min = -min(half_sq, center[1]) @@ -78,7 +78,7 @@ void main(void) // py: y = np.arange(y_min, y_max) // py: xx, yy = np.meshgrid(x, y) // we just set a point size. all iteration over every px is done in the fragment shader - pointSize = 2.0 * half_sq; + pointSize = 2.0 * half_sq - 1.0; } gl_PointSize = pointSize; From 70890c8ad4180e33797f4e9bd51cac9bf448f4c8 Mon Sep 17 00:00:00 2001 From: Hleb Valoshka <375gnu@gmail.com> Date: Mon, 29 Jan 2024 13:54:44 +0200 Subject: [PATCH 4/4] wip --- shaders/star_vert.glsl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shaders/star_vert.glsl b/shaders/star_vert.glsl index 8b81a79243..59e64ffb72 100644 --- a/shaders/star_vert.glsl +++ b/shaders/star_vert.glsl @@ -61,6 +61,8 @@ void main(void) pointSize = 1.0; // use max_theta == -1 as as signal that the point size is 1px max_theta = -1.0; + + v_color = scaled_color; } else { @@ -79,9 +81,10 @@ void main(void) // py: xx, yy = np.meshgrid(x, y) // we just set a point size. all iteration over every px is done in the fragment shader pointSize = 2.0 * half_sq - 1.0; + + v_color = color; } gl_PointSize = pointSize; - v_color = scaled_color; set_vp(in_Position); }