1 /*
2 * Stellarium
3 * Copyright (C) 2003 Fabien Chereau
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "Atmosphere.hpp"
21 #include "StelUtils.hpp"
22 #include "StelApp.hpp"
23 #include "StelProjector.hpp"
24 #include "StelToneReproducer.hpp"
25 #include "StelCore.hpp"
26 #include "StelPainter.hpp"
27 #include "StelFileMgr.hpp"
28 #include "Dithering.hpp"
29
30 #include <QFile>
31 #include <QDebug>
32 #include <QSettings>
33 #include <QOpenGLShaderProgram>
34
35
Atmosphere(void)36 Atmosphere::Atmosphere(void)
37 : viewport(0,0,0,0)
38 , skyResolutionY(44)
39 , skyResolutionX(44)
40 , posGrid(Q_NULLPTR)
41 , posGridBuffer(QOpenGLBuffer::VertexBuffer)
42 , indicesBuffer(QOpenGLBuffer::IndexBuffer)
43 , colorGrid(Q_NULLPTR)
44 , colorGridBuffer(QOpenGLBuffer::VertexBuffer)
45 , averageLuminance(0.f)
46 , overrideAverageLuminance(false)
47 , eclipseFactor(1.f)
48 , lightPollutionLuminance(0)
49 {
50 setFadeDuration(1.5f);
51
52 QOpenGLShader vShader(QOpenGLShader::Vertex);
53 {
54 QFile vert(":/shaders/atmosphere.vert");
55 if(!vert.open(QFile::ReadOnly))
56 qFatal("Failed to open atmosphere vertex shader source");
57 QFile toneRepro(":/shaders/xyYToRGB.glsl");
58 if(!toneRepro.open(QFile::ReadOnly))
59 qFatal("Failed to open ToneReproducer shader source");
60 if (!vShader.compileSourceCode(vert.readAll()+toneRepro.readAll()))
61 qFatal("Error while compiling atmosphere vertex shader: %s", vShader.log().toLatin1().constData());
62 }
63 if (!vShader.log().isEmpty())
64 {
65 qWarning() << "Warnings while compiling atmosphere vertex shader: " << vShader.log();
66 }
67 QOpenGLShader fShader(QOpenGLShader::Fragment);
68 if (!fShader.compileSourceCode(
69 makeDitheringShader()+
70 "varying mediump vec3 resultSkyColor;\n"
71 "void main()\n"
72 "{\n"
73 " gl_FragColor = vec4(dither(resultSkyColor), 1.);\n"
74 "}"))
75 {
76 qFatal("Error while compiling atmosphere fragment shader: %s", fShader.log().toLatin1().constData());
77 }
78 if (!fShader.log().isEmpty())
79 {
80 qWarning() << "Warnings while compiling atmosphere fragment shader: " << vShader.log();
81 }
82 atmoShaderProgram = new QOpenGLShaderProgram();
83 atmoShaderProgram->addShader(&vShader);
84 atmoShaderProgram->addShader(&fShader);
85 StelPainter::linkProg(atmoShaderProgram, "atmosphere");
86
87 atmoShaderProgram->bind();
88 shaderAttribLocations.bayerPattern = atmoShaderProgram->uniformLocation("bayerPattern");
89 shaderAttribLocations.rgbMaxValue = atmoShaderProgram->uniformLocation("rgbMaxValue");
90 shaderAttribLocations.alphaWaOverAlphaDa = atmoShaderProgram->uniformLocation("alphaWaOverAlphaDa");
91 shaderAttribLocations.oneOverGamma = atmoShaderProgram->uniformLocation("oneOverGamma");
92 shaderAttribLocations.term2TimesOneOverMaxdLpOneOverGamma = atmoShaderProgram->uniformLocation("term2TimesOneOverMaxdLpOneOverGamma");
93 shaderAttribLocations.brightnessScale = atmoShaderProgram->uniformLocation("brightnessScale");
94 shaderAttribLocations.sunPos = atmoShaderProgram->uniformLocation("sunPos");
95 shaderAttribLocations.term_x = atmoShaderProgram->uniformLocation("term_x");
96 shaderAttribLocations.Ax = atmoShaderProgram->uniformLocation("Ax");
97 shaderAttribLocations.Bx = atmoShaderProgram->uniformLocation("Bx");
98 shaderAttribLocations.Cx = atmoShaderProgram->uniformLocation("Cx");
99 shaderAttribLocations.Dx = atmoShaderProgram->uniformLocation("Dx");
100 shaderAttribLocations.Ex = atmoShaderProgram->uniformLocation("Ex");
101 shaderAttribLocations.term_y = atmoShaderProgram->uniformLocation("term_y");
102 shaderAttribLocations.Ay = atmoShaderProgram->uniformLocation("Ay");
103 shaderAttribLocations.By = atmoShaderProgram->uniformLocation("By");
104 shaderAttribLocations.Cy = atmoShaderProgram->uniformLocation("Cy");
105 shaderAttribLocations.Dy = atmoShaderProgram->uniformLocation("Dy");
106 shaderAttribLocations.Ey = atmoShaderProgram->uniformLocation("Ey");
107 shaderAttribLocations.projectionMatrix = atmoShaderProgram->uniformLocation("projectionMatrix");
108 shaderAttribLocations.skyVertex = atmoShaderProgram->attributeLocation("skyVertex");
109 shaderAttribLocations.skyColor = atmoShaderProgram->attributeLocation("skyColor");
110 atmoShaderProgram->release();
111 }
112
~Atmosphere(void)113 Atmosphere::~Atmosphere(void)
114 {
115 delete [] posGrid;
116 posGrid = Q_NULLPTR;
117 delete[] colorGrid;
118 colorGrid = Q_NULLPTR;
119 delete atmoShaderProgram;
120 atmoShaderProgram = Q_NULLPTR;
121 }
122
computeColor(double JD,Vec3d _sunPos,Vec3d moonPos,float moonPhase,float moonMagnitude,StelCore * core,float latitude,float altitude,float temperature,float relativeHumidity)123 void Atmosphere::computeColor(double JD, Vec3d _sunPos, Vec3d moonPos, float moonPhase, float moonMagnitude,
124 StelCore* core, float latitude, float altitude, float temperature, float relativeHumidity)
125 {
126 const StelProjectorP prj = core->getProjection(StelCore::FrameAltAz, StelCore::RefractionOff);
127 if (viewport != prj->getViewport())
128 {
129 // The viewport changed: update the number of point of the grid
130 viewport = prj->getViewport();
131 delete[] colorGrid;
132 delete [] posGrid;
133 skyResolutionY = StelApp::getInstance().getSettings()->value("landscape/atmosphereybin", 44).toUInt();
134 skyResolutionX = static_cast<unsigned int>(floorf(0.5f+skyResolutionY*(0.5f*sqrtf(3.0f))*prj->getViewportWidth()/prj->getViewportHeight()));
135 posGrid = new Vec2f[static_cast<size_t>((1+skyResolutionX)*(1+skyResolutionY))];
136 colorGrid = new Vec4f[static_cast<size_t>((1+skyResolutionX)*(1+skyResolutionY))];
137 float stepX = static_cast<float>(prj->getViewportWidth()) / static_cast<float>(skyResolutionX-0.5f);
138 float stepY = static_cast<float>(prj->getViewportHeight()) / skyResolutionY;
139 float viewport_left = prj->getViewportPosX();
140 float viewport_bottom = prj->getViewportPosY();
141 for (unsigned int x=0; x<=skyResolutionX; ++x)
142 {
143 for(unsigned int y=0; y<=skyResolutionY; ++y)
144 {
145 Vec2f &v(posGrid[y*(1+skyResolutionX)+x]);
146 v[0] = viewport_left + ((x == 0) ? 0.f :
147 (x == skyResolutionX) ? prj->getViewportWidth() : (x-0.5f*(y&1))*stepX);
148 v[1] = viewport_bottom+y*stepY;
149 }
150 }
151 posGridBuffer.destroy();
152 //posGridBuffer = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
153 Q_ASSERT(posGridBuffer.type()==QOpenGLBuffer::VertexBuffer);
154 posGridBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
155 posGridBuffer.create();
156 posGridBuffer.bind();
157 posGridBuffer.allocate(posGrid, static_cast<int>((1+skyResolutionX)*(1+skyResolutionY))*8);
158 posGridBuffer.release();
159
160 // Generate the indices used to draw the quads
161 unsigned short* indices = new unsigned short[static_cast<size_t>((skyResolutionX+1)*skyResolutionY*2)];
162 int i=0;
163 for (unsigned int y2=0; y2<skyResolutionY; ++y2)
164 {
165 unsigned short g0 = static_cast<unsigned short>(y2*(1+skyResolutionX));
166 unsigned short g1 = static_cast<unsigned short>((y2+1)*(1+skyResolutionX));
167 for (unsigned int x2=0; x2<=skyResolutionX; ++x2)
168 {
169 indices[i++]=g0++;
170 indices[i++]=g1++;
171 }
172 }
173 indicesBuffer.destroy();
174 //indicesBuffer = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
175 Q_ASSERT(indicesBuffer.type()==QOpenGLBuffer::IndexBuffer);
176 indicesBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
177 indicesBuffer.create();
178 indicesBuffer.bind();
179 indicesBuffer.allocate(indices, static_cast<int>((skyResolutionX+1)*skyResolutionY*2*2));
180 indicesBuffer.release();
181 delete[] indices;
182 indices=Q_NULLPTR;
183
184 colorGridBuffer.destroy();
185 colorGridBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
186 colorGridBuffer.create();
187 colorGridBuffer.bind();
188 colorGridBuffer.allocate(colorGrid, static_cast<int>((1+skyResolutionX)*(1+skyResolutionY)*4*4));
189 colorGridBuffer.release();
190 }
191
192 if (qIsNaN(_sunPos.length()))
193 _sunPos.set(0.,0.,-1.*AU);
194 if (qIsNaN(moonPos.length()))
195 moonPos.set(0.,0.,-1.*AU);
196
197 // Update the eclipse intensity factor to apply on atmosphere model
198 // these are for radii
199 const double sun_angular_size = atan(696000./AU/_sunPos.length());
200 const double moon_angular_size = atan(1738./AU/moonPos.length());
201 const double touch_angle = sun_angular_size + moon_angular_size;
202
203 // determine luminance falloff during solar eclipses
204 _sunPos.normalize();
205 moonPos.normalize();
206 // Calculate the atmosphere RGB for each point of the grid. We can use abbreviated numbers here.
207 Vec3f sunPosF=_sunPos.toVec3f();
208 Vec3f moonPosF=moonPos.toVec3f();
209
210 double separation_angle = std::acos(_sunPos.dot(moonPos)); // angle between them
211 // qDebug("touch at %f\tnow at %f (%f)\n", touch_angle, separation_angle, separation_angle/touch_angle);
212 // bright stars should be visible at total eclipse
213 // TODO: correct for atmospheric diffusion
214 // TODO: use better coverage function (non-linear)
215 // because of above issues, this algorithm darkens more quickly than reality
216 // Note: On Earth only, else moon would brighten other planets' atmospheres (LP:1673283)
217 if ((core->getCurrentLocation().planetName=="Earth") && (separation_angle < touch_angle))
218 {
219 double dark_angle = moon_angular_size - sun_angular_size;
220 double min = 0.0025; // 0.005f; // 0.0001f; // so bright stars show up at total eclipse
221 if (dark_angle < 0.)
222 {
223 // annular eclipse
224 double asun = sun_angular_size*sun_angular_size;
225 min = (asun - moon_angular_size*moon_angular_size)/asun; // minimum proportion of sun uncovered
226 dark_angle *= -1;
227 }
228
229 if (separation_angle < dark_angle)
230 eclipseFactor = static_cast<float>(min);
231 else
232 eclipseFactor = static_cast<float>(min + (1.0-min)*(separation_angle-dark_angle)/(touch_angle-dark_angle));
233 }
234 else
235 eclipseFactor = 1.f;
236 // TODO: compute eclipse factor also for Lunar eclipses! (lp:#1471546)
237
238 // No need to calculate if not visible
239 if (!fader.getInterstate())
240 {
241 // GZ 20180114: Why did we add light pollution if atmosphere was not visible?????
242 // And what is the meaning of 0.001? Approximate contribution of stellar background? Then why is it 0.0001 below???
243 averageLuminance = 0.001f;
244 return;
245 }
246
247 sky.setParamsv(sunPosF, 5.f);
248 skyb.setLocation(latitude * M_PI_180f, altitude, temperature, relativeHumidity);
249 skyb.setSunMoon(moonPosF[2], sunPosF[2]);
250
251 // Calculate the date from the julian day.
252 int year, month, day;
253 StelUtils::getDateFromJulianDay(JD, &year, &month, &day);
254 skyb.setDate(year, month, moonPhase, moonMagnitude);
255
256 // Variables used to compute the average sky luminance
257 float sum_lum = 0.f;
258
259 Vec3d point(1., 0., 0.);
260 float lumi;
261
262 // Compute the sky color for every point above the ground
263 for (unsigned int i=0; i<(1+skyResolutionX)*(1+skyResolutionY); ++i)
264 {
265 const Vec2f &v(posGrid[i]);
266 prj->unProject(static_cast<double>(v[0]),static_cast<double>(v[1]),point);
267
268 Q_ASSERT(fabs(point.lengthSquared()-1.0) < 1e-10);
269
270 Vec3f pointF=point.toVec3f();
271 // Use mirroring for sun only
272 if (pointF[2]<=0.f)
273 {
274 pointF[2] *= -1.f;
275 // The sky below the ground is the symmetric of the one above :
276 // it looks nice and gives proper values for brightness estimation
277 // Use the Skybright.cpp 's models for brightness which gives better results.
278 }
279 lumi = skyb.getLuminance(moonPosF[0]*pointF[0]+moonPosF[1]*pointF[1]+
280 moonPosF[2]*pointF[2], sunPosF[0]*pointF[0]+sunPosF[1]*pointF[1]+
281 sunPosF[2]*pointF[2], pointF[2]);
282 lumi *= eclipseFactor;
283 // Add star background luminance
284 lumi += 0.0001f;
285 // Multiply by the input scale of the ToneConverter (is not done automatically by the xyYtoRGB method called later)
286 //lumi*=eye->getInputScale();
287
288 // Add the light pollution luminance AFTER the scaling to avoid scaling it because it is the cause
289 // of the scaling itself
290 lumi += fader.getInterstate()*lightPollutionLuminance;
291
292 // Store for later statistics
293 sum_lum+=lumi;
294
295 // Now need to compute the xy part of the color component
296 // This is done in the openGL shader
297 // Store the back projected position + luminance in the input color to the shader
298 colorGrid[i].set(pointF[0], pointF[1], pointF[2], lumi);
299 }
300
301 colorGridBuffer.bind();
302 colorGridBuffer.write(0, colorGrid, static_cast<int>((1+skyResolutionX)*(1+skyResolutionY)*4*4));
303 colorGridBuffer.release();
304
305 // Update average luminance
306 if (!overrideAverageLuminance)
307 averageLuminance = sum_lum/((1+skyResolutionX)*(1+skyResolutionY));
308 }
309
310 // override computable luminance. This is for special operations only, e.g. for scripting of brightness-balanced image export.
311 // To return to auto-computed values, set any negative value.
setAverageLuminance(float overrideLum)312 void Atmosphere::setAverageLuminance(float overrideLum)
313 {
314 if (overrideLum<0.f)
315 {
316 overrideAverageLuminance=false;
317 averageLuminance=0.f;
318 }
319 else
320 {
321 overrideAverageLuminance=true;
322 averageLuminance=overrideLum;
323 }
324 }
325
326 // Draw the atmosphere using the precalc values stored in tab_sky
draw(StelCore * core)327 void Atmosphere::draw(StelCore* core)
328 {
329 if (StelApp::getInstance().getVisionModeNight())
330 return;
331
332 StelToneReproducer* eye = core->getToneReproducer();
333
334 if (!fader.getInterstate())
335 return;
336
337 StelPainter sPainter(core->getProjection2d());
338 sPainter.setBlending(true, GL_ONE, GL_ONE);
339
340 const float atm_intensity = fader.getInterstate();
341
342 GL(atmoShaderProgram->bind());
343 float a, b, c;
344 GL(eye->getShadersParams(a, b, c));
345 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.alphaWaOverAlphaDa, a));
346 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.oneOverGamma, b));
347 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.term2TimesOneOverMaxdLpOneOverGamma, c));
348 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.brightnessScale, atm_intensity));
349 Vec3f sunPos;
350 float term_x, Ax, Bx, Cx, Dx, Ex, term_y, Ay, By, Cy, Dy, Ey;
351 sky.getShadersParams(sunPos, term_x, Ax, Bx, Cx, Dx, Ex, term_y, Ay, By, Cy, Dy, Ey);
352 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.sunPos, sunPos[0], sunPos[1], sunPos[2]));
353 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.term_x, term_x));
354 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Ax, Ax));
355 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Bx, Bx));
356 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Cx, Cx));
357 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Dx, Dx));
358 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Ex, Ex));
359 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.term_y, term_y));
360 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Ay, Ay));
361 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.By, By));
362 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Cy, Cy));
363 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Dy, Dy));
364 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.Ey, Ey));
365 const Mat4f& m = sPainter.getProjector()->getProjectionMatrix();
366 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.projectionMatrix,
367 QMatrix4x4(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15])));
368
369 const auto rgbMaxValue=calcRGBMaxValue(sPainter.getDitheringMode());
370 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.rgbMaxValue, rgbMaxValue[0], rgbMaxValue[1], rgbMaxValue[2]));
371 auto& gl=*sPainter.glFuncs();
372 gl.glActiveTexture(GL_TEXTURE1);
373 if(!bayerPatternTex)
374 bayerPatternTex=makeBayerPatternTexture(*sPainter.glFuncs());
375 gl.glBindTexture(GL_TEXTURE_2D, bayerPatternTex);
376 GL(atmoShaderProgram->setUniformValue(shaderAttribLocations.bayerPattern, 1));
377
378 GL(colorGridBuffer.bind());
379 GL(atmoShaderProgram->setAttributeBuffer(shaderAttribLocations.skyColor, GL_FLOAT, 0, 4, 0));
380 GL(colorGridBuffer.release());
381 GL(atmoShaderProgram->enableAttributeArray(shaderAttribLocations.skyColor));
382 GL(posGridBuffer.bind());
383 GL(atmoShaderProgram->setAttributeBuffer(shaderAttribLocations.skyVertex, GL_FLOAT, 0, 2, 0));
384 GL(posGridBuffer.release());
385 GL(atmoShaderProgram->enableAttributeArray(shaderAttribLocations.skyVertex));
386
387 // And draw everything at once
388 GL(indicesBuffer.bind());
389 std::size_t shift=0;
390 for (unsigned int y=0;y<skyResolutionY;++y)
391 {
392 sPainter.glFuncs()->glDrawElements(GL_TRIANGLE_STRIP, static_cast<int>((skyResolutionX+1)*2), GL_UNSIGNED_SHORT, reinterpret_cast<void*>(shift));
393 shift += static_cast<size_t>((skyResolutionX+1)*2*2);
394 }
395 GL(indicesBuffer.release());
396
397 GL(atmoShaderProgram->disableAttributeArray(shaderAttribLocations.skyVertex));
398 GL(atmoShaderProgram->disableAttributeArray(shaderAttribLocations.skyColor));
399 GL(atmoShaderProgram->release());
400 // GZ: debug output
401 //const StelProjectorP prj = core->getProjection(StelCore::FrameEquinoxEqu);
402 //StelPainter painter(prj);
403 //painter.setFont(font);
404 //sPainter.setColor(0.7, 0.7, 0.7);
405 //sPainter.drawText(83, 120, QString("Atmosphere::getAverageLuminance(): %1" ).arg(getAverageLuminance()));
406 //qDebug() << atmosphere->getAverageLuminance();
407 }
408