MR104: Added the Milky Way onto the night sky

From Chris RINGEVAL

Squashed commit of the following:

commit ee2ec625ef10ab372a99397a82d1272670d767ec
Author: Chris Ringeval <eatdirt@protonmail.com>
Date:   Mon Jan 31 00:01:39 2022 +0100

    Replace galaxy texture by our own and update galactic coordinates

commit 26f3d80df6385913668635013912f1c4fef50e60
Author: Chris Ringeval <eatdirt@protonmail.com>
Date:   Tue Dec 7 22:02:10 2021 +0100

    Cleanup white lines and comments

commit 55e9f9b4bcbe21a34be7be874b120bda73c49cb8
Author: Chris Ringeval <eatdirt@protonmail.com>
Date:   Sun Dec 5 15:53:15 2021 +0100

    Cleanup cxx parts to the minimal required, moving all rendering to the shaders

commit 0cff2827bb3c18f85780cce5c0178ee37c590fe5
Author: Chris Ringeval <eatdirt@protonmail.com>
Date:   Wed Dec 1 22:55:41 2021 +0100

    Adding moon direction uniform vector used in the Milky Way shader

commit 24485079673c299bad95fc1f800542a6d73e8e95
Author: Chris Ringeval <eatdirt@protonmail.com>
Date:   Wed Dec 1 22:54:54 2021 +0100

    Adding Milky Way texturing and dark sky brightness effects
This commit is contained in:
Stuart Buchanan 2022-02-01 16:13:44 +00:00
parent a6624b8c89
commit 28e2ccda01
13 changed files with 482 additions and 63 deletions

View File

@ -11,6 +11,7 @@ set(HEADERS
sky.hxx sky.hxx
sphere.hxx sphere.hxx
stars.hxx stars.hxx
galaxy.hxx
) )
set(SOURCES set(SOURCES
@ -24,6 +25,7 @@ set(SOURCES
sky.cxx sky.cxx
sphere.cxx sphere.cxx
stars.cxx stars.cxx
galaxy.cxx
) )
simgear_scene_component(sky scene/sky "${SOURCES}" "${HEADERS}") simgear_scene_component(sky scene/sky "${SOURCES}" "${HEADERS}")

View File

@ -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 <simgear_config.h>
#endif
#include <simgear/compiler.h>
#include <stdio.h>
#include <iostream>
#include <osg/Array>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/CullFace>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Node>
#include <osg/ShadeModel>
#include <osg/TexEnv>
#include <osg/Texture2D>
#include <simgear/constants.h>
#include <simgear/screen/colors.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#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<SGReaderWriterOptions> 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 <<" "<<mudarksky << endl;
//the uniform feeding the shaders
zenith_brightness_magnitude->set((float)musky);
return true;
}

View File

@ -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 <osg/ref_ptr>
#include <osg/MatrixTransform>
#include <osg/Material>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/misc/sg_path.hxx>
class SGGalaxy : public SGReferenced {
osg::ref_ptr<osg::MatrixTransform> galaxy_transform;
osg::ref_ptr<osg::Uniform> 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_

View File

@ -101,6 +101,9 @@ void SGSky::build( double h_radius_m,
stars = new SGStars(property_tree_node); stars = new SGStars(property_tree_node);
_ephTransform->addChild( stars->build(eph.getNumStars(), eph.getStars(), h_radius_m, options) ); _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; moon = new SGMoon;
_ephTransform->addChild( moon->build(tex_path, moon_size) ); _ephTransform->addChild( moon->build(tex_path, moon_size) );
@ -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, dome->repaint( sc.adj_sky_color, sc.sky_color, sc.fog_color,
sc.sun_angle, effective_visibility ); sc.sun_angle, effective_visibility );
stars->repaint( sc.sun_angle, eph.getNumStars(), eph.getStars() ); stars->repaint( sc.sun_angle, sc.altitude_m, eph.getNumStars(), eph.getStars() );
planets->repaint( sc.sun_angle, eph.getNumPlanets(), eph.getPlanets() ); planets->repaint( sc.sun_angle, sc.altitude_m, eph.getNumPlanets(), eph.getPlanets() );
oursun->repaint( sc.sun_angle, effective_visibility ); oursun->repaint( sc.sun_angle, effective_visibility );
moon->repaint( sc.moon_angle ); moon->repaint( sc.moon_angle );
galaxy->repaint( sc.sun_angle, sc.altitude_m );
for ( unsigned i = 0; i < cloud_layers.size(); ++i ) { for ( unsigned i = 0; i < cloud_layers.size(); ++i ) {
if (cloud_layers[i]->getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR){ if (cloud_layers[i]->getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR){

View File

@ -48,6 +48,7 @@
#include <simgear/scene/sky/moon.hxx> #include <simgear/scene/sky/moon.hxx>
#include <simgear/scene/sky/oursun.hxx> #include <simgear/scene/sky/oursun.hxx>
#include <simgear/scene/sky/stars.hxx> #include <simgear/scene/sky/stars.hxx>
#include <simgear/scene/sky/galaxy.hxx>
namespace simgear { namespace simgear {
class SGReaderWriterOptions; class SGReaderWriterOptions;
@ -75,7 +76,7 @@ struct SGSkyColor
SGVec3f fog_color; SGVec3f fog_color;
SGVec3f cloud_color; SGVec3f cloud_color;
double sun_angle, double sun_angle,
moon_angle; moon_angle, altitude_m;
}; };
/** /**
@ -224,6 +225,7 @@ private:
SGSharedPtr<SGMoon> moon; SGSharedPtr<SGMoon> moon;
SGSharedPtr<SGStars> planets; SGSharedPtr<SGStars> planets;
SGSharedPtr<SGStars> stars; SGSharedPtr<SGStars> stars;
SGSharedPtr<SGGalaxy> galaxy;
layer_list_type cloud_layers; layer_list_type cloud_layers;
osg::ref_ptr<osg::Group> pre_root, pre_transform; osg::ref_ptr<osg::Group> pre_root, pre_transform;

View File

@ -31,19 +31,19 @@
#include <osg/Node> #include <osg/Node>
#include <osg/Geometry> #include <osg/Geometry>
#include <osg/Geode>
#include <osg/Array> #include <osg/Array>
#include <simgear/scene/material/EffectGeode.hxx>
// return a sphere object as an ssgBranch // return a sphere object as an ssgBranch
osg::Node* simgear::EffectGeode*
SGMakeSphere(double radius, int slices, int stacks) SGMakeSphere(double radius, int slices, int stacks)
{ {
float rho, drho, dtheta; float rho, drho, dtheta;
float s, t, ds, dt; float s, t, ds, dt;
int i, j, imin, imax; int i, j, imin, imax;
float nsign = 1.0; float nsign = 1.0;
osg::Geode* geode = new osg::Geode; simgear::EffectGeode* geode = new simgear::EffectGeode;
drho = SGD_PI / (float) stacks; drho = SGD_PI / (float) stacks;
dtheta = SGD_2PI / (float) slices; dtheta = SGD_2PI / (float) slices;

View File

@ -23,9 +23,10 @@
#include <osg/Node> #include <osg/Node>
#include <simgear/scene/material/EffectGeode.hxx>
// return a sphere object as an ssgBranch (and connect in the // return a sphere object as an ssgBranch (and connect in the
// specified ssgSimpleState // specified ssgSimpleState
osg::Node* SGMakeSphere(double radius, int slices, int stacks); simgear::EffectGeode* SGMakeSphere(double radius, int slices, int stacks);

View File

@ -7,6 +7,11 @@
// //
// Separated out rendering pieces and converted to ssg by Curt Olson, // Separated out rendering pieces and converted to ssg by Curt Olson,
// March 2000 // 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 // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public // modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
@ -58,9 +63,9 @@ SGStars::SGStars( SGPropertyNode* props ) :
old_phase(-1) old_phase(-1)
{ {
if (props) { 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 // from a property
_cutoffProperty = props->getNode("star-magnitude-cutoff"); _magDarkSkyProperty = props->getNode("darksky-brightness-magnitude");
} }
} }
@ -136,88 +141,154 @@ SGStars::build( int num, const SGVec3d star_data[], double star_dist,
// 0 degrees = high noon // 0 degrees = high noon
// 90 degrees = sun rise/set // 90 degrees = sun rise/set
// 180 degrees = darkest midnight // 180 degrees = darkest midnight
bool SGStars::repaint( double sun_angle, double altitude_m, int num, const SGVec3d star_data[] )
bool SGStars::repaint( double sun_angle, 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)
For (19.5 µsky 22) double magmax;
mmax = 0.383 musky - 1.44 - 2.5 * log(F) double sundeg, mindeg;
double musky, mudarksky;
//observer visual acuity in the model used below (F=2)
const double logF = 0.30;
Let's take F = 1.4 for healthy young pilot // same as moon.cxx
mudarksky ~ 22 mag/arcsec^2 => mmax=6.2 const double earth_radius_in_meters = 6371000.0;
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
*/
double mag_nakedeye = 6.2; //sundeg is elevation above the horizon
double mag_twilight_astro = 5.4; sundeg = 90.0 - sun_angle * SGD_RADIANS_TO_DEGREES;
double mag_twilight_nautic = 4.7;
// 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
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;
}
}
// 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) // sirius, brightest star (not brightest object)
double mag_min = -1.46; double mag_min = -1.46;
int phase; int phase;
// determine which star structure to draw //continously changed at each call to repaint, but we use "phase"
if ( sun_angle > (SGD_PI_2 + 18.0 * SGD_DEGREES_TO_RADIANS ) ) { //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 // deep night, atmosphere is not lighten by the sun
factor = 1.0; factor = 1.0;
cutoff = mag_nakedeye;
phase = 0; 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 // less than 18deg and more than 12deg is astronomical twilight
factor = 1.0; factor = 1.0;
cutoff = mag_twilight_astro;
phase = 1; 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 // less 12deg and more than 6deg is is nautical twilight
factor = 1.0; factor = 1.0;
cutoff = mag_twilight_nautic;
phase = 2; 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; factor = 0.95;
cutoff = 3.1;
phase = 3; 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; factor = 0.9;
cutoff = 2.4;
phase = 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; factor = 0.85;
cutoff = 1.8;
phase = 5; 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; factor = 0.8;
cutoff = 1.2;
phase = 6; 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; factor = 0.75;
cutoff = 0.6;
phase = 7; phase = 7;
} else { } else {
// early dusk or late dawn // early dusk or late dawn
factor = 0.7; factor = 0.7;
cutoff = 0.0; cutoff = 0.0;
phase = 8; 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) { // repaint only for change of phase or if darksky property has been changed
double propCutoff = _cutoffProperty->getDoubleValue();
cutoff = std::min(propCutoff, cutoff);
}
if ((phase != old_phase) || (cutoff != _cachedCutoff)) { if ((phase != old_phase) || (mudarksky != _cachedMagDarkSky)) {
// cout << " phase change, repainting stars, num = " << num << endl;
old_phase = phase; 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 ) { for ( int i = 0; i < num; ++i ) {
// if ( star_data[i][2] < min ) { min = star_data[i][2]; } // if ( star_data[i][2] < min ) { min = 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]; mag = star_data[i][2];
if ( mag < cutoff ) { if ( mag < cutoff ) {
nmag = ( cutoff - mag ) / (cutoff - mag_min); // translate to 0 ... 1.0 scale 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 alpha *= factor; // dim when the sun is brighter
} else { } else {
alpha = 0.0; alpha = 0.0;

View File

@ -45,8 +45,13 @@ class SGStars : public SGReferenced {
int old_phase; // data for optimization int old_phase; // data for optimization
double _cachedCutoff = 0.0; // the darkest sky at zenith has a brightness equals to (in
SGPropertyNode_ptr _cutoffProperty; // magnitude per arcsec^2 for the V band)
const double _magDarkSkyDefault = 22.0;
double _cachedMagDarkSky = 0.0;
SGPropertyNode_ptr _magDarkSkyProperty;
public: public:
// Constructor // Constructor
@ -59,13 +64,13 @@ public:
osg::Node* build( int num, const SGVec3d star_data[], double star_dist, osg::Node* build( int num, const SGVec3d star_data[], double star_dist,
simgear::SGReaderWriterOptions* options ); 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 // sun_angle in degrees relative to verticle (so we can make them
// relatively dimmer during dawn and dusk // relatively dimmer during dawn and dusk
// 0 degrees = high noon // 0 degrees = high noon
// 90 degrees = sun rise/set // 90 degrees = sun rise/set
// 180 degrees = darkest midnight // 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[] );
}; };

View File

@ -108,11 +108,13 @@ public:
const SGVec3d& getHorizLocalDown() const const SGVec3d& getHorizLocalDown() const
{ return mHorizLocalDown; } { 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& diffuse, const SGVec4f& specular,
const SGVec4f& fogColor, double sunAngleDeg) const SGVec4f& fogColor, double sunAngleDeg)
{ {
mLightDirection = direction; mLightDirection = sundirection;
mSecondLightDirection = moondirection;
mAmbientLight = ambient; mAmbientLight = ambient;
mDiffuseLight = diffuse; mDiffuseLight = diffuse;
mSpecularLight = specular; mSpecularLight = specular;
@ -122,6 +124,8 @@ public:
const SGVec3f& getLightDirection() const const SGVec3f& getLightDirection() const
{ return mLightDirection; } { return mLightDirection; }
const SGVec3f& getSecondLightDirection() const
{ return mSecondLightDirection; }
const SGVec4f& getAmbientLight() const const SGVec4f& getAmbientLight() const
{ return mAmbientLight; } { return mAmbientLight; }
const SGVec4f& getDiffuseLight() const const SGVec4f& getDiffuseLight() const
@ -217,6 +221,7 @@ private:
double mGroundLightsFogExp2Density; double mGroundLightsFogExp2Density;
SGVec3f mLightDirection; SGVec3f mLightDirection;
SGVec3f mSecondLightDirection;
SGVec4f mAmbientLight; SGVec4f mAmbientLight;
SGVec4f mDiffuseLight; SGVec4f mDiffuseLight;
SGVec4f mSpecularLight; SGVec4f mSpecularLight;

View File

@ -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<SGUpdateVisitor *>(nv);
osg::Vec3f l = toOsg(uv->getSecondLightDirection());
l.normalize();
uniform->set(l);
}
};
namespace simgear { namespace simgear {
namespace compositor { namespace compositor {
@ -155,11 +165,16 @@ Compositor::Compositor(osg::View *view,
new osg::Uniform("fg_SunDirection", osg::Vec3f()), new osg::Uniform("fg_SunDirection", osg::Vec3f()),
new osg::Uniform("fg_SunDirectionWorld", osg::Vec3f()), new osg::Uniform("fg_SunDirectionWorld", osg::Vec3f()),
new osg::Uniform("fg_SunZenithCosTheta", 0.0f), 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), new osg::Uniform("fg_EarthRadius", 0.0f),
} }
{ {
_uniforms[SG_UNIFORM_SUN_DIRECTION_WORLD]->setUpdateCallback( _uniforms[SG_UNIFORM_SUN_DIRECTION_WORLD]->setUpdateCallback(
new SunDirectionWorldCallback); new SunDirectionWorldCallback);
_uniforms[SG_UNIFORM_MOON_DIRECTION_WORLD]->setUpdateCallback(
new MoonDirectionWorldCallback);
} }
Compositor::~Compositor() Compositor::~Compositor()
@ -215,6 +230,11 @@ Compositor::update(const osg::Matrix &view_matrix,
osg::Vec4f sun_dir_view = osg::Vec4f( osg::Vec4f sun_dir_view = osg::Vec4f(
sun_dir_world.x(), sun_dir_world.y(), sun_dir_world.z(), 0.0f) * view_matrix; 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) { for (int i = 0; i < SG_TOTAL_BUILTIN_UNIFORMS; ++i) {
osg::ref_ptr<osg::Uniform> u = _uniforms[i]; osg::ref_ptr<osg::Uniform> u = _uniforms[i];
switch (i) { switch (i) {
@ -259,6 +279,12 @@ Compositor::update(const osg::Matrix &view_matrix,
case SG_UNIFORM_SUN_ZENITH_COSTHETA: case SG_UNIFORM_SUN_ZENITH_COSTHETA:
u->set(float(sun_dir_world * world_up)); u->set(float(sun_dir_world * world_up));
break; 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: case SG_UNIFORM_EARTH_RADIUS:
u->set(float(camera_pos.length() - camera_pos_geod.getElevationM())); u->set(float(camera_pos.length() - camera_pos_geod.getElevationM()));
break; break;

View File

@ -66,6 +66,9 @@ public:
SG_UNIFORM_SUN_DIRECTION, SG_UNIFORM_SUN_DIRECTION,
SG_UNIFORM_SUN_DIRECTION_WORLD, SG_UNIFORM_SUN_DIRECTION_WORLD,
SG_UNIFORM_SUN_ZENITH_COSTHETA, SG_UNIFORM_SUN_ZENITH_COSTHETA,
SG_UNIFORM_MOON_DIRECTION,
SG_UNIFORM_MOON_DIRECTION_WORLD,
SG_UNIFORM_MOON_ZENITH_COSTHETA,
SG_UNIFORM_EARTH_RADIUS, SG_UNIFORM_EARTH_RADIUS,
SG_TOTAL_BUILTIN_UNIFORMS SG_TOTAL_BUILTIN_UNIFORMS
}; };

View File

@ -786,6 +786,9 @@ public:
ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_DIRECTION]); ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_DIRECTION]);
ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_DIRECTION_WORLD]); ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_DIRECTION_WORLD]);
ss->addUniform(uniforms[Compositor::SG_UNIFORM_SUN_ZENITH_COSTHETA]); 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]); ss->addUniform(uniforms[Compositor::SG_UNIFORM_EARTH_RADIUS]);
osg::ref_ptr<osg::Uniform> clustered_shading_enabled = osg::ref_ptr<osg::Uniform> clustered_shading_enabled =