diff --git a/simgear/scene/sky/CMakeLists.txt b/simgear/scene/sky/CMakeLists.txt index b71c5cba..0730fec8 100644 --- a/simgear/scene/sky/CMakeLists.txt +++ b/simgear/scene/sky/CMakeLists.txt @@ -11,6 +11,7 @@ set(HEADERS sky.hxx sphere.hxx stars.hxx + galaxy.hxx ) set(SOURCES @@ -24,6 +25,7 @@ set(SOURCES sky.cxx sphere.cxx stars.cxx + galaxy.cxx ) simgear_scene_component(sky scene/sky "${SOURCES}" "${HEADERS}") diff --git a/simgear/scene/sky/galaxy.cxx b/simgear/scene/sky/galaxy.cxx new file mode 100644 index 00000000..ab4bf764 --- /dev/null +++ b/simgear/scene/sky/galaxy.cxx @@ -0,0 +1,225 @@ +// galaxy.cxx -- model the celestial sphere brightness by unresolved +// sources, i.e. the milky way and its nebulae: our Galaxy +// +// Started November 2021 (Chris Ringeval) +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. +// +// $Id$ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sphere.hxx" +#include "galaxy.hxx" + +using namespace simgear; + +// Constructor +SGGalaxy::SGGalaxy( SGPropertyNode* props ) +{ + if (props) { + _magDarkSkyProperty = props->getNode("darksky-brightness-magnitude"); + } +} + + +// Destructor +SGGalaxy::~SGGalaxy( void ) { +} + + +// build the galaxy sphere object +osg::Node* +SGGalaxy::build( SGPath path, double galaxy_size, simgear::SGReaderWriterOptions *options ) { + + simgear::EffectGeode* orb = SGMakeSphere(galaxy_size, 64, 32); + + Effect *effect = makeEffect("Effects/galaxy", true, options); + if (effect) { + orb->setEffect(effect); + } + + // set up the orb state + osg::StateSet* stateSet = orb->getOrCreateStateSet(); + stateSet->setRenderBinDetails(-9, "RenderBin"); + + osg::ref_ptr poptions; + poptions = SGReaderWriterOptions::fromPath(path); + + osg::Texture2D* texture = SGLoadTexture2D("allsky_brightness_magten.png", poptions.get(),true,true); + texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + + stateSet->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); + + osg::TexEnv* texEnv = new osg::TexEnv; + texEnv->setMode(osg::TexEnv::MODULATE); + stateSet->setTextureAttribute(0, texEnv, osg::StateAttribute::ON); + + osg::ShadeModel* shadeModel = new osg::ShadeModel; + shadeModel->setMode(osg::ShadeModel::SMOOTH); + stateSet->setAttributeAndModes(shadeModel); + + osg::BlendFunc* blendFunc = new osg::BlendFunc; + blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); + stateSet->setAttributeAndModes(blendFunc); + + // needed shader side + zenith_brightness_magnitude = new osg::Uniform("fg_ZenithSkyBrightness", 0.0f); + stateSet->addUniform(zenith_brightness_magnitude); + + // force a repaint of the galaxy colors with arbitrary defaults + repaint( 0.0, 0.0 ); + + // build the scene graph sub tree for the Galaxy + galaxy_transform = new osg::MatrixTransform; + galaxy_transform->addChild( orb ); + + // reposition the Galaxy's texture, which is in galactic + // coordinates, into the "fake" geocentric frame (which is carried + // along our current position (p)) + // + // Coordinates of the galactic north pole used with Gaia data (from + // which our Milky Way Texture is built). + // + // https://www.cosmos.esa.int/web/gaia-users/archive/gedr3-documentation-pdf + // Section 4.1.7.1 page 198 + + const double galactic_north_pole_RA = 192.85948; + const double galactic_north_pole_DEC = 27.12825; + const double equatorial_north_pole_THETA = 122.93192; + + osg::Matrix RA, DEC, THETA; + + // RA origin at 90 degrees + RA.makeRotate((galactic_north_pole_RA-90.0)*SGD_DEGREES_TO_RADIANS, osg::Vec3(0, 0, 1)); + // Rotate along rotated x-axis by -(90-DEC) + DEC.makeRotate((galactic_north_pole_DEC-90.0)*SGD_DEGREES_TO_RADIANS, osg::Vec3(1, 0, 0)); + // Set the origin of the galactic longitude in Sagittarius, rotate + // along rotated z-axis by -theta + THETA.makeRotate(-equatorial_north_pole_THETA*SGD_DEGREES_TO_RADIANS, osg::Vec3(0, 0, 1)); + + galaxy_transform->setMatrix(THETA*DEC*RA); + + return galaxy_transform.get(); +} + + +// Basic evaluation of the dark sky brightness magnitude at zenith, +// according to the sun angles above the horizon. The actual painting +// and coloring of the Galaxy is done within the shaders using this +// value as a starting point, and adding angular dependent scattering +// and Moon illumation effects when relevant. The idea being that the +// intrinsic Galaxy brightness is eventually masked by the brightness +// of the atmosphere and we do see their relative contrast +bool SGGalaxy::repaint( double sun_angle, double altitude_m ) { + + osg::Vec4 rhodcolor; + + double sundeg; + double mindeg; + + double mudarksky; + double musky; + + // same as moon.cxx + const double earth_radius_in_meters = 6371000.0; + + //sundeg is elevation above the horizon + sundeg = 90.0 - sun_angle * SGD_RADIANS_TO_DEGREES; + + // mindeg is the elevation above the horizon at which the sun is + // no longer obstructed by the Earth (mindeg <= 0) + mindeg = 0.0; + if (altitude_m >=0) { + mindeg = -90.0 + SGD_RADIANS_TO_DEGREES * asin(earth_radius_in_meters/(altitude_m + earth_radius_in_meters)); + } + + // the darkest possible value of the sky brightness at zenith + // (darker = larger number). If a prop is defined, we use it, or we + // default to the one coded here (should be 22 for the darkest skies + // on Earth) + if (_magDarkSkyProperty) { + mudarksky = _magDarkSkyProperty->getDoubleValue(); + } + else { + mudarksky = _magDarkSkyDefault; + } + + // initializing sky brightness to a lot + musky = 0.0; + + // in space, we either have sun in the face or not. If sundeg >= + // mindeg, the sun is visible, we see nothing, then we do + // nothing. Otherwise, we have + if (sundeg <= mindeg) { + + // sun illumination of the atmosphere at zenith (fit to SQM zenital measurements) + // from http://www.hnsky.org/sqm_twilight.htm, slightly modified + // to be continuous at 0deg, -12deg and -18deg + // musky is in magV / arcsec^2 + if ( (sundeg >= -12.0) && (sundeg < 0.0) ) { + musky = - 1.057*sundeg + mudarksky - 14.7528; + } + + if ( (sundeg >= -18.0 ) && (sundeg < -12.0) ) { + musky = -0.0744*sundeg*sundeg - 2.5768*sundeg + mudarksky - 22.2768; + musky = min(musky,mudarksky); + } + + if ( sundeg < -18.0) { + musky = mudarksky; + } + + } + + //cout << "sundeg= " << sundeg << endl; + //cout << "mindeg= " << mindeg << endl; + //cout << "altitude= " << altitude_m << endl; + //cout << "musky= mudarksky= " << musky <<" "<set((float)musky); + + return true; +} + + diff --git a/simgear/scene/sky/galaxy.hxx b/simgear/scene/sky/galaxy.hxx new file mode 100644 index 00000000..3c5385c1 --- /dev/null +++ b/simgear/scene/sky/galaxy.hxx @@ -0,0 +1,65 @@ +// galaxy.cxx -- model the celestial sphere brightness by unresolved +// sources, i.e. the milky way and its nebulae: our Galaxy +// +// Started November 2021 (Chris Ringeval) +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifndef _SG_GALAXY_HXX_ +#define _SG_GALAXY_HXX_ + + +#include +#include +#include + +#include +#include + +#include + + +class SGGalaxy : public SGReferenced { + + osg::ref_ptr galaxy_transform; + osg::ref_ptr zenith_brightness_magnitude; + + SGPropertyNode_ptr _magDarkSkyProperty; + + // the darkest sky at zenith has a brightness equals to (in + // magnitude per arcsec^2 for the V band) + const double _magDarkSkyDefault = 22.0; + +public: + + // Constructor + SGGalaxy( SGPropertyNode* props = nullptr ); + + // Destructor + ~SGGalaxy( void ); + + // build the galaxy object + osg::Node *build( SGPath path, double galaxy_size, simgear::SGReaderWriterOptions *options); + + // basic repainting according to sky lighting + bool repaint( double sun_angle, double altitude_m ); + +}; + + +#endif // _SG_GALAXY_HXX_ diff --git a/simgear/scene/sky/sky.cxx b/simgear/scene/sky/sky.cxx index 6b8924dd..6e069875 100644 --- a/simgear/scene/sky/sky.cxx +++ b/simgear/scene/sky/sky.cxx @@ -100,13 +100,16 @@ void SGSky::build( double h_radius_m, stars = new SGStars(property_tree_node); _ephTransform->addChild( stars->build(eph.getNumStars(), eph.getStars(), h_radius_m, options) ); + + galaxy = new SGGalaxy(property_tree_node); + _ephTransform->addChild( galaxy->build(tex_path, h_radius_m, options) ); moon = new SGMoon; _ephTransform->addChild( moon->build(tex_path, moon_size) ); oursun = new SGSun; _ephTransform->addChild( oursun->build(tex_path, sun_size, property_tree_node ) ); - + pre_root->addChild( pre_transform.get() ); } @@ -123,10 +126,11 @@ bool SGSky::repaint( const SGSkyColor &sc, const SGEphemeris& eph ) dome->repaint( sc.adj_sky_color, sc.sky_color, sc.fog_color, sc.sun_angle, effective_visibility ); - stars->repaint( sc.sun_angle, eph.getNumStars(), eph.getStars() ); - planets->repaint( sc.sun_angle, eph.getNumPlanets(), eph.getPlanets() ); + stars->repaint( sc.sun_angle, sc.altitude_m, eph.getNumStars(), eph.getStars() ); + planets->repaint( sc.sun_angle, sc.altitude_m, eph.getNumPlanets(), eph.getPlanets() ); oursun->repaint( sc.sun_angle, effective_visibility ); moon->repaint( sc.moon_angle ); + galaxy->repaint( sc.sun_angle, sc.altitude_m ); for ( unsigned i = 0; i < cloud_layers.size(); ++i ) { if (cloud_layers[i]->getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR){ @@ -186,7 +190,7 @@ bool SGSky::reposition( const SGSkyState &st, const SGEphemeris& eph, double dt //moon is closer to the center of Earth, times any articial extra factors double moon_dist_factor = moon_r * st.moon_dist_factor; moon->reposition( moon_ra, moon_dec, st.moon_dist_bare, moon_dist_factor, lst, lat, alt ); - + for ( unsigned i = 0; i < cloud_layers.size(); ++i ) { if ( cloud_layers[i]->getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR || cloud_layers[i]->get_layer3D()->isDefined3D() ) { diff --git a/simgear/scene/sky/sky.hxx b/simgear/scene/sky/sky.hxx index 6e6e228b..f296bfef 100644 --- a/simgear/scene/sky/sky.hxx +++ b/simgear/scene/sky/sky.hxx @@ -48,6 +48,7 @@ #include #include #include +#include namespace simgear { class SGReaderWriterOptions; @@ -75,7 +76,7 @@ struct SGSkyColor SGVec3f fog_color; SGVec3f cloud_color; double sun_angle, - moon_angle; + moon_angle, altitude_m; }; /** @@ -224,6 +225,7 @@ private: SGSharedPtr moon; SGSharedPtr planets; SGSharedPtr stars; + SGSharedPtr galaxy; layer_list_type cloud_layers; osg::ref_ptr pre_root, pre_transform; diff --git a/simgear/scene/sky/sphere.cxx b/simgear/scene/sky/sphere.cxx index 90f166d3..02f0ec8a 100644 --- a/simgear/scene/sky/sphere.cxx +++ b/simgear/scene/sky/sphere.cxx @@ -31,19 +31,19 @@ #include #include -#include #include +#include // return a sphere object as an ssgBranch -osg::Node* +simgear::EffectGeode* SGMakeSphere(double radius, int slices, int stacks) { float rho, drho, dtheta; float s, t, ds, dt; int i, j, imin, imax; float nsign = 1.0; - osg::Geode* geode = new osg::Geode; + simgear::EffectGeode* geode = new simgear::EffectGeode; drho = SGD_PI / (float) stacks; dtheta = SGD_2PI / (float) slices; diff --git a/simgear/scene/sky/sphere.hxx b/simgear/scene/sky/sphere.hxx index 5c5eced0..acade586 100644 --- a/simgear/scene/sky/sphere.hxx +++ b/simgear/scene/sky/sphere.hxx @@ -23,9 +23,10 @@ #include +#include // return a sphere object as an ssgBranch (and connect in the // specified ssgSimpleState -osg::Node* SGMakeSphere(double radius, int slices, int stacks); +simgear::EffectGeode* SGMakeSphere(double radius, int slices, int stacks); diff --git a/simgear/scene/sky/stars.cxx b/simgear/scene/sky/stars.cxx index 84af1fea..a1ac45c7 100644 --- a/simgear/scene/sky/stars.cxx +++ b/simgear/scene/sky/stars.cxx @@ -7,6 +7,11 @@ // // Separated out rendering pieces and converted to ssg by Curt Olson, // March 2000 +// +// Switch to sky brightness as a mean to sort visible stars to be +// consistent with Milky Way visibility, can be modified from the +// property tree from local lighting environment + // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either @@ -58,9 +63,9 @@ SGStars::SGStars( SGPropertyNode* props ) : old_phase(-1) { if (props) { - // don't create here - if it's not defined, we won't use the cutoff + // don't create here - if it's not defined, we won't use the value // from a property - _cutoffProperty = props->getNode("star-magnitude-cutoff"); + _magDarkSkyProperty = props->getNode("darksky-brightness-magnitude"); } } @@ -136,89 +141,155 @@ SGStars::build( int num, const SGVec3d star_data[], double star_dist, // 0 degrees = high noon // 90 degrees = sun rise/set // 180 degrees = darkest midnight - -bool SGStars::repaint( double sun_angle, int num, const SGVec3d star_data[] ) +bool SGStars::repaint( double sun_angle, double altitude_m, int num, const SGVec3d star_data[] ) { - double mag, nmag, alpha, factor, cutoff; + + double mag, nmag, alpha, factor, cutoff; - /* - maximal magnitudes under dark sky on Earth, from Eq.(90) and (91) of astro-ph/1405.4209 - For (18 < musky < 20) - mmax = 0.27 musky + 0.8 - 2.5 * log(F) + double magmax; + double sundeg, mindeg; + double musky, mudarksky; - For (19.5 µsky 22) - mmax = 0.383 musky - 1.44 - 2.5 * log(F) + //observer visual acuity in the model used below (F=2) + const double logF = 0.30; + // same as moon.cxx + const double earth_radius_in_meters = 6371000.0; + + //sundeg is elevation above the horizon + sundeg = 90.0 - sun_angle * SGD_RADIANS_TO_DEGREES; + + // mindeg is the elevation above the horizon at which the sun is + // no longer obstructed by the Earth (mindeg <= 0) + mindeg = 0.0; + if (altitude_m >=0) { + mindeg = -90.0 + SGD_RADIANS_TO_DEGREES * asin(earth_radius_in_meters/(altitude_m + earth_radius_in_meters)); + } + // if the prop exists, let's use its value, can be real time changed + // due to lighting conditions. Otherwise, we use the default + if (_magDarkSkyProperty) { + mudarksky = _magDarkSkyProperty->getDoubleValue(); + } + else { + mudarksky = _magDarkSkyDefault; + } + + // initializing sky brightness to a lot + musky = 0.0; + + // same as in galaxy.cxx + // in space, we either have the sun in the face or not. If sundeg >= mindeg, + // the sun is visible, we see nothing. Otherwise we have: + if (sundeg <= mindeg) { + + // same little model as galaxy.cxx, sun illumination of the + // atmosphere at zenith (fit to SQM zenital measurements) from + // http://www.hnsky.org/sqm_twilight.htm, slightly modified to be + // continuous at 0deg, -12deg and -18deg musky is in magV / + // arcsec^2 - Let's take F = 1.4 for healthy young pilot - mudarksky ~ 22 mag/arcsec^2 => mmax=6.2 - muastrotwilight ~ 20 mag/arsec^2 => mmax=5.4 - mu99deg ~ 17.5 mag/arcsec^2 => mmax=4.7 - mu97.5deg ~ 16 mag/arcsec^2 => ? let's keep it rough - */ + if ( (sundeg >= -12.0) && (sundeg < 0.0) ) { + musky = - 1.057*sundeg + mudarksky - 14.7528; + } + + if ( (sundeg >= -18.0 ) && (sundeg < -12.0) ) { + musky = -0.0744*sundeg*sundeg - 2.5768*sundeg + mudarksky - 22.2768; + musky = min(musky,mudarksky); + } - double mag_nakedeye = 6.2; - double mag_twilight_astro = 5.4; - double mag_twilight_nautic = 4.7; + if ( sundeg < -18.0) { + musky = mudarksky; + } + } + + // Simple relation between maximal star magnitudes visible by the + // naked eye on Earth, from Eq.(90) and (91) of astro-ph/1405.4209 + // + // For 19.5 < musky < 22 + // mmax = 0.3834 musky - 1.4400 - 2.5 * log(F) + // + // For 18 < musky < 20 + // mmax = 0.270 musky _0.8 - 2.5 * log(F) + // + // Typical values, let's take F = 2 for healthy pilot. With mudarksky ~ 22 + // mag/arcsec^2 => mmax=6.2 + // + // We use these linear formulae and switch from one to the other at + // their intersection point + + if (musky >= 19.823) { + magmax = 0.383 * musky - 1.44 - 2.5 * logF; + } + else { + //extrapolated to all bright (small) musky values + magmax = 0.270 * musky + 0.80 - 2.5 * logF; + } + // sirius, brightest star (not brightest object) double mag_min = -1.46; int phase; - // determine which star structure to draw - if ( sun_angle > (SGD_PI_2 + 18.0 * SGD_DEGREES_TO_RADIANS ) ) { + //continously changed at each call to repaint, but we use "phase" + //to actually check for repainting, not magmax + + cutoff = magmax; + + // determine which star structure to draw when the sun is not + // directly visible + if (sundeg <= mindeg) { + if ( sun_angle > (SGD_PI_2 + 18.0 * SGD_DEGREES_TO_RADIANS ) ) { // deep night, atmosphere is not lighten by the sun factor = 1.0; - cutoff = mag_nakedeye; phase = 0; - } else if ( sun_angle > (SGD_PI_2 + 12.0 * SGD_DEGREES_TO_RADIANS ) ) { + } else if ( sun_angle > (SGD_PI_2 + 12.0 * SGD_DEGREES_TO_RADIANS ) ) { // less than 18deg and more than 12deg is astronomical twilight factor = 1.0; - cutoff = mag_twilight_astro; phase = 1; - } else if ( sun_angle > (SGD_PI_2 + 9.0 * SGD_DEGREES_TO_RADIANS ) ) { + } else if ( sun_angle > (SGD_PI_2 + 9.0 * SGD_DEGREES_TO_RADIANS ) ) { // less 12deg and more than 6deg is is nautical twilight factor = 1.0; - cutoff = mag_twilight_nautic; phase = 2; - } else if ( sun_angle > (SGD_PI_2 + 7.5 * SGD_DEGREES_TO_RADIANS ) ) { + } else if ( sun_angle > (SGD_PI_2 + 7.5 * SGD_DEGREES_TO_RADIANS ) ) { factor = 0.95; - cutoff = 3.1; phase = 3; - } else if ( sun_angle > (SGD_PI_2 + 7.0 * SGD_DEGREES_TO_RADIANS ) ) { + } else if ( sun_angle > (SGD_PI_2 + 7.0 * SGD_DEGREES_TO_RADIANS ) ) { factor = 0.9; - cutoff = 2.4; phase = 4; - } else if ( sun_angle > (SGD_PI_2 + 6.5 * SGD_DEGREES_TO_RADIANS ) ) { + } else if ( sun_angle > (SGD_PI_2 + 6.5 * SGD_DEGREES_TO_RADIANS ) ) { factor = 0.85; - cutoff = 1.8; phase = 5; - } else if ( sun_angle > (SGD_PI_2 + 6.0 * SGD_DEGREES_TO_RADIANS ) ) { + } else if ( sun_angle > (SGD_PI_2 + 6.0 * SGD_DEGREES_TO_RADIANS ) ) { factor = 0.8; - cutoff = 1.2; phase = 6; - } else if ( sun_angle > (SGD_PI_2 + 5.5 * SGD_DEGREES_TO_RADIANS ) ) { + } else if ( sun_angle > (SGD_PI_2 + 5.5 * SGD_DEGREES_TO_RADIANS ) ) { factor = 0.75; - cutoff = 0.6; phase = 7; - } else { + } else { // early dusk or late dawn factor = 0.7; cutoff = 0.0; phase = 8; + } + } else { + // at large altitudes (in space), this conditional is triggered + // for sun >=mindeg, the sun is directly visible, let's call it + // phase 9 + factor = 1.0; + cutoff = 0.0; + phase = 9; } - if (_cutoffProperty) { - double propCutoff = _cutoffProperty->getDoubleValue(); - cutoff = std::min(propCutoff, cutoff); - } + // repaint only for change of phase or if darksky property has been changed - if ((phase != old_phase) || (cutoff != _cachedCutoff)) { - // cout << " phase change, repainting stars, num = " << num << endl; + if ((phase != old_phase) || (mudarksky != _cachedMagDarkSky)) { old_phase = phase; - _cachedCutoff = cutoff; + _cachedMagDarkSky = mudarksky; + //cout << " phase change -> repainting stars, num = " << num << endl; + //cout << "mudarksky= musky= cutoff= " << mudarksky << " " << musky << " " << cutoff << endl; + for ( int i = 0; i < num; ++i ) { // if ( star_data[i][2] < min ) { min = star_data[i][2]; } // if ( star_data[i][2] > max ) { max = star_data[i][2]; } @@ -232,7 +303,14 @@ bool SGStars::repaint( double sun_angle, int num, const SGVec3d star_data[] ) mag = star_data[i][2]; if ( mag < cutoff ) { nmag = ( cutoff - mag ) / (cutoff - mag_min); // translate to 0 ... 1.0 scale - alpha = nmag * 0.85 + 0.15; // translate to a 0.15 ... 1.0 scale + //with Milky Way on, it is more realistic to make the + //stars fainting to total darkness when matching the + //background sky brightness + // + //alpha = nmag * 0.85 + 0.15; // + //translate to a 0.15 ... 1.0 scale + // + alpha = nmag; alpha *= factor; // dim when the sun is brighter } else { alpha = 0.0; diff --git a/simgear/scene/sky/stars.hxx b/simgear/scene/sky/stars.hxx index 6efd7f1d..14b62bc5 100644 --- a/simgear/scene/sky/stars.hxx +++ b/simgear/scene/sky/stars.hxx @@ -44,9 +44,14 @@ class SGStars : public SGReferenced { osg::ref_ptr cl; int old_phase; // data for optimization - - double _cachedCutoff = 0.0; - SGPropertyNode_ptr _cutoffProperty; + + // the darkest sky at zenith has a brightness equals to (in + // magnitude per arcsec^2 for the V band) + const double _magDarkSkyDefault = 22.0; + + double _cachedMagDarkSky = 0.0; + SGPropertyNode_ptr _magDarkSkyProperty; + public: // Constructor @@ -59,13 +64,13 @@ public: osg::Node* build( int num, const SGVec3d star_data[], double star_dist, simgear::SGReaderWriterOptions* options ); - // repaint the planet magnitudes based on current value of + // repaint the star and planet magnitudes based on current value of // sun_angle in degrees relative to verticle (so we can make them // relatively dimmer during dawn and dusk // 0 degrees = high noon // 90 degrees = sun rise/set // 180 degrees = darkest midnight - bool repaint( double sun_angle, int num, const SGVec3d star_data[] ); + bool repaint( double sun_angle, double altitude_m, int num, const SGVec3d star_data[] ); }; diff --git a/simgear/scene/util/SGUpdateVisitor.hxx b/simgear/scene/util/SGUpdateVisitor.hxx index 8f16a270..012a9f72 100644 --- a/simgear/scene/util/SGUpdateVisitor.hxx +++ b/simgear/scene/util/SGUpdateVisitor.hxx @@ -108,11 +108,13 @@ public: const SGVec3d& getHorizLocalDown() const { return mHorizLocalDown; } - void setLight(const SGVec3f& direction, const SGVec4f& ambient, + void setLight(const SGVec3f& sundirection, const SGVec3f& moondirection, + const SGVec4f& ambient, const SGVec4f& diffuse, const SGVec4f& specular, const SGVec4f& fogColor, double sunAngleDeg) { - mLightDirection = direction; + mLightDirection = sundirection; + mSecondLightDirection = moondirection; mAmbientLight = ambient; mDiffuseLight = diffuse; mSpecularLight = specular; @@ -122,6 +124,8 @@ public: const SGVec3f& getLightDirection() const { return mLightDirection; } + const SGVec3f& getSecondLightDirection() const + { return mSecondLightDirection; } const SGVec4f& getAmbientLight() const { return mAmbientLight; } const SGVec4f& getDiffuseLight() const @@ -217,6 +221,7 @@ private: double mGroundLightsFogExp2Density; SGVec3f mLightDirection; + SGVec3f mSecondLightDirection; SGVec4f mAmbientLight; SGVec4f mDiffuseLight; SGVec4f mSpecularLight; diff --git a/simgear/scene/viewer/Compositor.cxx b/simgear/scene/viewer/Compositor.cxx index 5e2f5fce..25780a1b 100644 --- a/simgear/scene/viewer/Compositor.cxx +++ b/simgear/scene/viewer/Compositor.cxx @@ -50,6 +50,16 @@ public: } }; +class MoonDirectionWorldCallback : public osg::Uniform::Callback { +public: + virtual void operator()(osg::Uniform *uniform, osg::NodeVisitor *nv) { + SGUpdateVisitor *uv = dynamic_cast(nv); + osg::Vec3f l = toOsg(uv->getSecondLightDirection()); + l.normalize(); + uniform->set(l); + } +}; + namespace simgear { namespace compositor { @@ -155,11 +165,16 @@ Compositor::Compositor(osg::View *view, new osg::Uniform("fg_SunDirection", osg::Vec3f()), new osg::Uniform("fg_SunDirectionWorld", osg::Vec3f()), new osg::Uniform("fg_SunZenithCosTheta", 0.0f), + new osg::Uniform("fg_MoonDirection", osg::Vec3f()), + new osg::Uniform("fg_MoonDirectionWorld", osg::Vec3f()), + new osg::Uniform("fg_MoonZenithCosTheta", 0.0f), new osg::Uniform("fg_EarthRadius", 0.0f), } { _uniforms[SG_UNIFORM_SUN_DIRECTION_WORLD]->setUpdateCallback( new SunDirectionWorldCallback); + _uniforms[SG_UNIFORM_MOON_DIRECTION_WORLD]->setUpdateCallback( + new MoonDirectionWorldCallback); } Compositor::~Compositor() @@ -214,7 +229,12 @@ Compositor::update(const osg::Matrix &view_matrix, _uniforms[SG_UNIFORM_SUN_DIRECTION_WORLD]->get(sun_dir_world); osg::Vec4f sun_dir_view = osg::Vec4f( sun_dir_world.x(), sun_dir_world.y(), sun_dir_world.z(), 0.0f) * view_matrix; - + + osg::Vec3f moon_dir_world; + _uniforms[SG_UNIFORM_MOON_DIRECTION_WORLD]->get(moon_dir_world); + osg::Vec4f moon_dir_view = osg::Vec4f( + moon_dir_world.x(), moon_dir_world.y(), moon_dir_world.z(), 0.0f) * view_matrix; + for (int i = 0; i < SG_TOTAL_BUILTIN_UNIFORMS; ++i) { osg::ref_ptr u = _uniforms[i]; switch (i) { @@ -259,6 +279,12 @@ Compositor::update(const osg::Matrix &view_matrix, case SG_UNIFORM_SUN_ZENITH_COSTHETA: u->set(float(sun_dir_world * world_up)); break; + case SG_UNIFORM_MOON_DIRECTION: + u->set(osg::Vec3f(moon_dir_view.x(), moon_dir_view.y(), moon_dir_view.z())); + break; + case SG_UNIFORM_MOON_ZENITH_COSTHETA: + u->set(float(moon_dir_world * world_up)); + break; case SG_UNIFORM_EARTH_RADIUS: u->set(float(camera_pos.length() - camera_pos_geod.getElevationM())); break; diff --git a/simgear/scene/viewer/Compositor.hxx b/simgear/scene/viewer/Compositor.hxx index 2fcb9037..8b087cb2 100644 --- a/simgear/scene/viewer/Compositor.hxx +++ b/simgear/scene/viewer/Compositor.hxx @@ -66,6 +66,9 @@ public: SG_UNIFORM_SUN_DIRECTION, SG_UNIFORM_SUN_DIRECTION_WORLD, SG_UNIFORM_SUN_ZENITH_COSTHETA, + SG_UNIFORM_MOON_DIRECTION, + SG_UNIFORM_MOON_DIRECTION_WORLD, + SG_UNIFORM_MOON_ZENITH_COSTHETA, SG_UNIFORM_EARTH_RADIUS, SG_TOTAL_BUILTIN_UNIFORMS }; diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index bf3f74b9..8b782ac1 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -786,6 +786,9 @@ public: ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_DIRECTION]); ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_DIRECTION_WORLD]); ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_ZENITH_COSTHETA]); + ss->addUniform(uniforms[Compositor::SG_UNIFORM_MOON_DIRECTION]); + ss->addUniform(uniforms[Compositor::SG_UNIFORM_MOON_DIRECTION_WORLD]); + ss->addUniform(uniforms[Compositor::SG_UNIFORM_MOON_ZENITH_COSTHETA]); ss->addUniform(uniforms[Compositor::SG_UNIFORM_EARTH_RADIUS]); osg::ref_ptr clustered_shading_enabled =