globular.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:20k
- // qlobular.cpp
- //
- // Copyright (C) 2008, Celestia Development Team
- // Initial code by Dr. Fridger Schrempp <fridger.schrempp@desy.de>
- //
- // Simulation of globular clusters, theoretical framework by
- // Ivan King, Astron. J. 67 (1962) 471; ibid. 71 (1966) 64
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- #include <fstream>
- #include <algorithm>
- #include <cstdio>
- #include <cassert>
- #include "celestia.h"
- #include <celmath/mathlib.h>
- #include <celmath/perlin.h>
- #include <celmath/intersect.h>
- #include "astro.h"
- #include "globular.h"
- #include <celutil/util.h>
- #include <celutil/debug.h>
- #include "gl.h"
- #include "vecgl.h"
- #include "render.h"
- #include "texture.h"
- #include <math.h>
- using namespace std;
- static int cntrTexWidth = 512, cntrTexHeight = 512;
- static int starTexWidth = 128, starTexHeight = 128;
- static Color colorTable[256];
- static const unsigned int GLOBULAR_POINTS = 8192;
- static const float LumiShape = 3.0f, Lumi0 = exp(-LumiShape);
- // Reference values ( = data base averages) of core radius, King concentration
- // and mu25 isophote radius:
-
- static const float R_c_ref = 0.83f, C_ref = 2.1f, R_mu25 = 40.32f;
- // min/max c-values of globular cluster data
- static const float MinC = 0.50f, MaxC = 2.58f, BinWidth = (MaxC - MinC) / 8.0f + 0.02f;
- // P1 determines the zoom level, where individual cluster stars start to appear.
- // The smaller P2 (< 1), the faster stars show up when resolution increases.
- static const float P1 = 65.0f, P2 = 0.75f;
- static const float RRatio_min = pow(10.0f, 1.7f);
- static float CBin, RRatio, XI, DiskSizeInPixels, Rr = 1.0f, Gg = 1.0f, Bb = 1.0f;
- static GlobularForm** globularForms = NULL;
- static Texture* globularTex = NULL;
- static Texture* centerTex[8] = {NULL};
- static void InitializeForms();
- static GlobularForm* buildGlobularForms(float);
- static bool formsInitialized = false;
- static bool decreasing (const GBlob& b1, const GBlob& b2)
- {
- return (b1.radius_2d > b2.radius_2d);
- }
- static void GlobularTextureEval(float u, float v, float /*w*/, unsigned char *pixel)
- {
- // use an exponential luminosity shape for the individual stars
- // giving sort of a halo for the brighter (i.e.bigger) stars.
- float lumi = exp(- LumiShape * sqrt(u * u + v * v)) - Lumi0;
- if (lumi <= 0.0f)
- lumi = 0.0f;
- int pixVal = (int) (lumi * 255.99f);
- #ifdef HDR_COMPRESS
- pixel[0] = 127;
- pixel[1] = 127;
- pixel[2] = 127;
- #else
- pixel[0] = 255;
- pixel[1] = 255;
- pixel[2] = 255;
- #endif
- pixel[3] = pixVal;
- }
- float relStarDensity(float eta)
- {
- /*! As alpha blending weight (relStarDensity) I take the theoretical
- * number of globular stars in 2d projection at a distance
- * rho = r / r_c = eta * r_t from the center (cf. King_1962's Eq.(18)),
- * divided by the area = PI * rho * rho . This number density of stars
- * I normalized to 1 at rho=0.
- * The resulting blending weight increases strongly -> 1 if the
- * 2d number density of stars rises, i.e for rho -> 0.
- */
- // Since the central "cloud" is due to lack of visual resolution,
- // rather than cluster morphology, we limit it's size by
- // taking max(C_ref, CBin). Smaller c gives a shallower distribution!
-
- float rRatio = max(RRatio_min, RRatio);
- float Xi = 1.0f / sqrt(1.0f + rRatio * rRatio);
- float XI2 = Xi * Xi;
- float rho2 = 1.0001f + eta * eta * rRatio * rRatio; //add 1e-4 as regulator near rho=0
- return ((log(rho2) + 4.0f * (1.0f - sqrt(rho2)) * Xi) / (rho2 - 1.0f) + XI2) / (1.0f - 2.0f * Xi + XI2);
- }
- static void CenterCloudTexEval(float u, float v, float /*w*/, unsigned char *pixel)
- {
- /*! For reasons of speed, calculate central "cloud" texture only for
- * 8 bins of King_1962 concentration, c = CBin, XI(CBin), RRatio(CBin).
- */
-
- // Skyplane projected King_1962 profile at center (rho = eta = 0):
- float c2d = 1.0f - XI;
-
- float eta = sqrt(u * u + v * v); // u,v = (-1..1)
-
- // eta^2 = u * u + v * v = 1 is the biggest circle fitting into the quadratic
- // procedural texture. Hence clipping
-
- if (eta >= 1.0f)
- eta = 1.0f;
- // eta = 1 corresponds to tidalRadius:
- float rho = eta * RRatio;
- float rho2 = 1.0f + rho * rho;
- // Skyplane projected King_1962 profile (Eq.(14)), vanishes for eta = 1:
- // i.e. absolutely no globular stars for r > tidalRadius:
-
- float profile_2d = (1.0f / sqrt(rho2) - 1.0f)/c2d + 1.0f ;
- profile_2d = profile_2d * profile_2d;
- #ifdef HDR_COMPRESS
- pixel[0] = 127;
- pixel[1] = 127;
- pixel[2] = 127;
- #else
- pixel[0] = 255;
- pixel[1] = 255;
- pixel[2] = 255;
- #endif
- pixel[3] = (int) (relStarDensity(eta) * profile_2d * 255.99f);
- }
- Globular::Globular() :
- detail (1.0f),
- customTmpName (NULL),
- form (NULL),
- r_c (R_c_ref),
- c (C_ref),
- tidalRadius(0.0f)
- {
- recomputeTidalRadius();
- }
- unsigned int Globular::cSlot(float conc) const
- {
- // map the physical range of c, minC <= c <= maxC,
- // to 8 integers (bin numbers), 0 < cSlot <= 7:
-
- if (conc <= MinC)
- conc = MinC;
- if (conc >= MaxC)
- conc = MaxC;
-
- return (unsigned int) floor((conc - MinC) / BinWidth);
- }
-
- const char* Globular::getType() const
- {
- return "Globular";
- }
- void Globular::setType(const std::string& /*typeStr*/)
- {
- }
- float Globular::getDetail() const
- {
- return detail;
- }
- void Globular::setDetail(float d)
- {
- detail = d;
- }
- string Globular::getCustomTmpName() const
- {
- if (customTmpName == NULL)
- return "";
- else
- return *customTmpName;
- }
- void Globular::setCustomTmpName(const string& tmpNameStr)
- {
- if (customTmpName == NULL)
- customTmpName = new string(tmpNameStr);
- else
- *customTmpName = tmpNameStr;
- }
- float Globular::getCoreRadius() const
- {
- return r_c;
- }
- void Globular::setCoreRadius(const float coreRadius)
- {
- r_c = coreRadius;
- recomputeTidalRadius();
- }
-
- float Globular::getHalfMassRadius() const
- {
- // Aproximation to the half-mass radius r_h [ly]
- // (~ 20% accuracy)
-
- return std::tan(degToRad(r_c / 60.0f)) * (float) getPosition().distanceFromOrigin() * pow(10.0f, 0.6f * c - 0.4f);
- }
- float Globular::getConcentration() const
- {
- return c;
- }
- void Globular::setConcentration(const float conc)
- {
- c = conc;
- if (!formsInitialized)
- InitializeForms();
-
- // For saving time, account for the c dependence via 8 bins only,
- form = globularForms[cSlot(conc)];
- recomputeTidalRadius();
- }
- size_t Globular::getDescription(char* buf, size_t bufLength) const
- {
- return snprintf(buf, bufLength, _("Globular (core radius: %4.2f', King concentration: %4.2f)"), r_c, c);
- }
- GlobularForm* Globular::getForm() const
- {
- return form;
- }
- const char* Globular::getObjTypeName() const
- {
- return "globular";
- }
- static const float RADIUS_CORRECTION = 0.025f;
- bool Globular::pick(const Ray3d& ray,
- double& distanceToPicker,
- double& cosAngleToBoundCenter) const
- {
- if (!isVisible())
- return false;
- /*
- * The selection ellipsoid should be slightly larger to compensate for the fact
- * that blobs are considered points when globulars are built, but have size
- * when they are drawn.
- */
- Vec3d ellipsoidAxes(getRadius() * (form->scale.x + RADIUS_CORRECTION),
- getRadius() * (form->scale.y + RADIUS_CORRECTION),
- getRadius() * (form->scale.z + RADIUS_CORRECTION));
- Quatf qf= getOrientation();
- Quatd qd(qf.w, qf.x, qf.y, qf.z);
- return testIntersection(Ray3d(Point3d() + (ray.origin - getPosition()), ray.direction) * conjugate(qd).toMatrix3(),
- Ellipsoidd(ellipsoidAxes),
- distanceToPicker,
- cosAngleToBoundCenter);
- }
- bool Globular::load(AssociativeArray* params, const string& resPath)
- {
- // Load the basic DSO parameters first
- bool ok = DeepSkyObject::load(params, resPath);
- if (!ok)
- return false;
- if(params->getNumber("Detail", detail))
- setDetail((float) detail);
-
- string customTmpName;
- if(params->getString("CustomTemplate", customTmpName ))
- setCustomTmpName(customTmpName);
-
- if(params->getNumber("CoreRadius", r_c))
- setCoreRadius(r_c);
-
- if(params->getNumber("KingConcentration", c))
- setConcentration(c);
-
- return true;
- }
- void Globular::render(const GLContext& context,
- const Vec3f& offset,
- const Quatf& viewerOrientation,
- float brightness,
- float pixelSize)
- {
- renderGlobularPointSprites(context, offset, viewerOrientation, brightness, pixelSize);
- }
- void Globular::renderGlobularPointSprites(const GLContext&,
- const Vec3f& offset,
- const Quatf& viewerOrientation,
- float brightness,
- float pixelSize)
- {
- if (form == NULL)
- return;
-
- float distanceToDSO = offset.length() - getRadius();
- if (distanceToDSO < 0)
- distanceToDSO = 0;
-
- float minimumFeatureSize = 0.5f * pixelSize * distanceToDSO;
-
- DiskSizeInPixels = getRadius() / minimumFeatureSize;
-
- /*
- * Is the globular's apparent size big enough to
- * be noticeable on screen? If it's not, break right here to
- * avoid all the overhead of the matrix transformations and
- * GL state changes:
- */
-
- if (DiskSizeInPixels < 1.0f)
- return;
- /*
- * When resolution (zoom) varies, the blended texture opacity is controlled by the
- * factor 'pixelWeight'. At low resolution, the latter starts at 1, but tends to 0,
- * if the resolution increases sufficiently (DiskSizeInPixels >= P1 pixels)!
- * The smaller P2 (<1), the faster pixelWeight -> 0, for DiskSizeInPixels >= P1.
- */
-
- float pixelWeight = (DiskSizeInPixels >= P1)? 1.0f/(P2 + (1.0f - P2) * DiskSizeInPixels / P1): 1.0f;
-
- // Use same 8 c-bins as in globularForms below!
-
- unsigned int ic = cSlot(c);
- CBin = MinC + ((float) ic + 0.5f) * BinWidth; // center value of (ic+1)th c-bin
- RRatio = pow(10.0f, CBin);
- XI = 1.0f / sqrt(1.0f + RRatio * RRatio);
-
- if(centerTex[ic] == NULL)
- {
- centerTex[ic] = CreateProceduralTexture( cntrTexWidth, cntrTexHeight, GL_RGBA, CenterCloudTexEval);
- }
- assert(centerTex[ic] != NULL);
-
- if (globularTex == NULL)
- {
- globularTex = CreateProceduralTexture( starTexWidth, starTexHeight, GL_RGBA,
- GlobularTextureEval);
- }
- assert(globularTex != NULL);
- glEnable (GL_BLEND);
- glEnable (GL_TEXTURE_2D);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- Mat3f viewMat = viewerOrientation.toMatrix3();
- Vec3f v0 = Vec3f(-1, -1, 0) * viewMat;
- Vec3f v1 = Vec3f( 1, -1, 0) * viewMat;
- Vec3f v2 = Vec3f( 1, 1, 0) * viewMat;
- Vec3f v3 = Vec3f(-1, 1, 0) * viewMat;
-
- float tidalSize = 2 * tidalRadius;
- Mat3f m =
- Mat3f::scaling(form->scale) * getOrientation().toMatrix3() *
- Mat3f::scaling(tidalSize);
-
- vector<GBlob>* points = form->gblobs;
- unsigned int nPoints =
- (unsigned int) (points->size() * clamp(getDetail()));
-
- /* Render central cloud sprite (centerTex). It fades away when
- * distance from center or resolution increases sufficiently.
- */
-
- centerTex[ic]->bind();
-
- float br = 2 * brightness;
- glColor4f(Rr, Gg, Bb, min(br * pixelWeight, 1.0f));
- glBegin(GL_QUADS);
-
- glTexCoord2f(0, 0); glVertex(v0 * tidalSize);
- glTexCoord2f(1, 0); glVertex(v1 * tidalSize);
- glTexCoord2f(1, 1); glVertex(v2 * tidalSize);
- glTexCoord2f(0, 1); glVertex(v3 * tidalSize);
-
- glEnd();
-
- /*! Next, render globular cluster via distinct "star" sprites (globularTex)
- * for sufficiently large resolution and distance from center of globular.
- *
- * This RGBA texture fades away when resolution decreases (e.g. via automag!),
- * or when distance from globular center decreases.
- */
-
-
- globularTex->bind();
-
-
- int pow2 = 128; // Associate "Red Giants" with the 128 biggest star-sprites
- float starSize = br * 0.5f; // Maximal size of star sprites -> "Red Giants"
- float clipDistance = 100.0f; // observer distance [ly] from globular, where we
- // start "morphing" the star-sprite sizes towards
- // their physical values
- glBegin(GL_QUADS);
-
- for (unsigned int i = 0; i < nPoints; ++i)
- {
- GBlob b = (*points)[i];
- Point3f p = b.position * m;
- float eta_2d = b.radius_2d;
-
- /*! Note that the [axis,angle] input in globulars.dsc transforms the
- * 2d projected star distance r_2d in the globular frame to refer to the
- * skyplane frame for each globular! That's what I need here.
- *
- * The [axis,angle] input will be needed anyway, when upgrading to
- * account for ellipticities, with corresponding inclinations and
- * position angles...
- */
- if ((i & pow2) != 0)
- {
- pow2 <<= 1;
- starSize /= 1.25f;
-
- if (starSize < minimumFeatureSize)
- break;
- }
- float obsDistanceToStarRatio = (p + offset).distanceFromOrigin() / clipDistance;
- float saveSize = starSize;
- if (obsDistanceToStarRatio < 1.0f)
- {
- // "Morph" the star-sprite sizes at close observer distance such that
- // the overdense globular core is dissolved upon closing in.
-
- starSize = starSize * min(obsDistanceToStarRatio, 1.0f);
- }
-
- /* Colors of normal globular stars are given by color profile.
- * Associate orange "Red Giant" stars with the largest sprite
- * sizes (while pow2 = 128).
- */
- Color col = (pow2 < 256)? colorTable[255]: colorTable[b.colorIndex];
- glColor4f(col.red(), col.green(), col.blue(),
- min(br * (1.0f - pixelWeight * relStarDensity(eta_2d)), 1.0f));
-
- glTexCoord2f(0, 0); glVertex(p + (v0 * starSize));
- glTexCoord2f(1, 0); glVertex(p + (v1 * starSize));
- glTexCoord2f(1, 1); glVertex(p + (v2 * starSize));
- glTexCoord2f(0, 1); glVertex(p + (v3 * starSize));
-
- starSize = saveSize;
- }
- glEnd();
- }
- unsigned int Globular::getRenderMask() const
- {
- return Renderer::ShowGlobulars;
- }
- unsigned int Globular::getLabelMask() const
- {
- return Renderer::GlobularLabels;
- }
-
- void Globular::recomputeTidalRadius()
- {
- // Convert the core radius from arcminutes to light years
- // Compute the tidal radius in light years
-
- float coreRadiusLy = std::tan(degToRad(r_c / 60.0f)) * (float) getPosition().distanceFromOrigin();
- tidalRadius = coreRadiusLy * std::pow(10.0f, c);
- }
- GlobularForm* buildGlobularForms(float c)
- {
- GBlob b;
- vector<GBlob>* globularPoints = new vector<GBlob>;
- float rRatio = pow(10.0f, c); // = r_t / r_c
- float prob;
- float cc = 1.0f + rRatio * rRatio;
- unsigned int i = 0, k = 0;
-
- // Value of King_1962 luminosity profile at center:
-
- float prob0 = sqrt(cc) - 1.0f;
- /*! Generate the globular star distribution randomly, according
- * to the King_1962 surface density profile f(r), eq.(14).
- *
- * rho = r / r_c = eta r_t / r_c, 0 <= eta <= 1,
- * coreRadius r_c, tidalRadius r_t, King concentration c = log10(r_t/r_c).
- */
-
- while (i < GLOBULAR_POINTS)
- {
- /*!
- * Use a combination of the Inverse Transform method and
- * Von Neumann's Acceptance-Rejection method for generating sprite stars
- * with eta distributed according to the exact King luminosity profile.
- *
- * This algorithm leads to almost 100% efficiency for all values of
- * parameters and variables!
- */
-
- float uu = Mathf::frand();
-
- /* First step: eta distributed as inverse power distribution (~1/Z^2)
- * that majorizes the exact King profile. Compute eta in terms of uniformly
- * distributed variable uu! Normalization to 1 for eta -> 0.
- */
-
- float eta = tan(uu *atan(rRatio))/rRatio;
- float rho = eta * rRatio;
- float cH = 1.0f/(1.0f + rho * rho);
- float Z = sqrt((1.0f + rho * rho)/cc); // scaling variable
-
- // Express King_1962 profile in terms of the UNIVERSAL variable 0 < Z <= 1,
-
- prob = (1.0f - 1.0f / Z) / prob0;
- prob = prob * prob;
-
- /* Second step: Use Acceptance-Rejection method (Von Neumann) for
- * correcting the power distribution of eta into the exact,
- * desired King form 'prob'!
- */
- k++;
- if (Mathf::frand() < prob / cH)
- {
- /* Generate 3d points of globular cluster stars in polar coordinates:
- * Distribution in eta (<=> r) according to King's profile.
- * Uniform distribution on any spherical surface for given eta.
- * Note: u = cos(phi) must be used as a stochastic variable to get uniformity in angle!
- */
- float u = Mathf::sfrand();
- float theta = 2 * (float) PI * Mathf::frand();
- float sthetu2 = sin(theta) * sqrt(1.0f - u * u);
-
- // x,y,z points within -0.5..+0.5, as required for consistency:
- b.position = 0.5f * Point3f(eta * sqrt(1.0f - u * u) * cos(theta), eta * sthetu2 , eta * u);
-
- /*
- * Note: 2d projection in x-z plane, according to Celestia's
- * conventions! Hence...
- */
- b.radius_2d = eta * sqrt(1.0f - sthetu2 * sthetu2);
- /* For now, implement only a generic spectrum for normal cluster
- * stars, modelled from Hubble photo of M80.
- * Blue Stragglers are qualitatively accounted for...
- * assume color index poportional to Z as function of which the King profile
- * becomes universal!
- */
-
- b.colorIndex = (unsigned int) (Z * 254);
-
- globularPoints->push_back(b);
- i++;
- }
- }
-
- // Check for efficiency of sprite-star generation => close to 100 %!
- //cout << "c = "<< c <<" i = " << i - 1 <<" k = " << k - 1 << " Efficiency: " << 100.0f * i / (float)k<<"%" << endl;
-
- GlobularForm* globularForm = new GlobularForm();
- globularForm->gblobs = globularPoints;
- globularForm->scale = Vec3f(1.0f, 1.0f, 1.0f);
-
- return globularForm;
- }
- void InitializeForms()
- {
- // Build RGB color table, using hue, saturation, value as input.
- // Hue in degrees.
-
- // Location of hue transition and saturation peak in color index space:
- int i0 = 36, i_satmax = 16;
- // Width of hue transition in color index space:
- int i_width = 3;
-
- float sat_l = 0.08f, sat_h = 0.1f, hue_r = 27.0f, hue_b = 220.0f;
-
- // Red Giant star color: i = 255:
- // -------------------------------
- // Convert hue, saturation and value to RGB
- DeepSkyObject::hsv2rgb(&Rr, &Gg, &Bb, 25.0f, 0.65f, 1.0f);
- colorTable[255] = Color(Rr, Gg, Bb);
-
- // normal stars: i < 255, generic color profile for now, improve later
- // --------------------------------------------------------------------
- // Convert hue, saturation, value to RGB
-
- for (int i = 254; i >=0; i--)
- {
- // simple qualitative saturation profile:
- // i_satmax is value of i where sat = sat_h + sat_l maximal
- float x = (float) i / (float) i_satmax, x2 = x ;
- float sat = sat_l + 2 * sat_h /(x2 + 1.0f / x2);
-
- // Fast transition from hue_r to hue_b at i = i0 within a width
- // i_width in color index space:
- float hue = hue_r + 0.5f * (hue_b - hue_r) * (std::tanh((float)(i - i0) / (float) i_width) + 1.0f);
-
- DeepSkyObject::hsv2rgb(&Rr, &Gg, &Bb, hue, sat, 0.85f);
- colorTable[i] = Color(Rr, Gg, Bb);
- }
- // Define globularForms corresponding to 8 different bins of King concentration c
-
- globularForms = new GlobularForm*[8];
- for (unsigned int ic = 0; ic <= 7; ++ic)
- {
- float CBin = MinC + ((float) ic + 0.5f) * BinWidth;
- globularForms[ic] = buildGlobularForms(CBin);
- }
- formsInitialized = true;
- }