Clone of Bael'Zharon's Respite @ https://github.com/boardwalk/bzr

SkyModel.cpp 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Bael'Zharon's Respite
  3. * Copyright (C) 2014 Daniel Skorupski
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include "graphics/SkyModel.h"
  19. #include <algorithm>
  20. // A Practical Analytic Model for Daylight
  21. // A. J. Preetham et al
  22. // http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
  23. static fp_t F(fp_t coeff[5], fp_t theta, fp_t gamma)
  24. {
  25. return (fp_t(1.0) + coeff[0] * glm::exp(coeff[1] / glm::cos(theta))) *
  26. (fp_t(1.0) + coeff[2] * glm::exp(coeff[3] * gamma) + coeff[4] * glm::pow(glm::cos(gamma), fp_t(2.0)));
  27. }
  28. static fp_t tonemap(fp_t luminance)
  29. {
  30. luminance *= 16.0;
  31. fp_t x = max(fp_t(0.0), luminance - fp_t(0.004));
  32. return (x * (fp_t(6.2) * x + fp_t(0.5))) / (x * (fp_t(6.2) * x + fp_t(1.7)) + fp_t(0.06));
  33. }
  34. void SkyModel::prepare(const Params& p)
  35. {
  36. // calculate solar time
  37. fp_t t_s = p.tm * fp_t(24.0); // standard time in decimal hours
  38. fp_t J = p.dt; // Julian day
  39. fp_t SM = static_cast<int>(p.lng / fp_t(15.0)) * fp_t(15.0); // standard meridian for time zone
  40. fp_t L = p.lng; // longitude in radians
  41. fp_t t = t_s + fp_t(0.170) * glm::sin(fp_t(4.0) * pi() * (J - fp_t(80.0)) / fp_t(373.0)) - fp_t(0.129) * glm::sin(fp_t(2.0) * pi() * (J - fp_t(8.0)) / fp_t(355.0)) + fp_t(12.0) * (SM - L) / pi();
  42. // calculate solar declination
  43. fp_t delta = fp_t(0.4093) * glm::sin(fp_t(2.0) * pi() * (J - fp_t(81.0)) / fp_t(368.0));
  44. // calculate solar position
  45. fp_t l = p.lat; // latitude in radians
  46. theta_s_ = pi() / fp_t(2.0) - glm::asin(sin(l) * sin(delta) - cos(l) * cos(delta) * cos(pi() * t / fp_t(12.0)));
  47. phi_s_ = (fp_t)atan2(-glm::cos(delta) * glm::sin(pi() * t / fp_t(12.0)), (glm::cos(l) * glm::sin(delta) - glm::sin(l) * glm::cos(delta) * glm::cos(pi() * t / fp_t(12.0))));
  48. // calculate A B, C, D and E coefficients
  49. coeffs_Y_[0] = fp_t(0.1787) * p.tu - fp_t(1.4630);
  50. coeffs_Y_[1] = fp_t(-0.3554) * p.tu + fp_t(0.4275);
  51. coeffs_Y_[2] = fp_t(-0.0227) * p.tu + fp_t(5.3251);
  52. coeffs_Y_[3] = fp_t(0.1206) * p.tu - fp_t(2.5771);
  53. coeffs_Y_[4] = fp_t(-0.0670) * p.tu + fp_t(0.3703);
  54. coeffs_x_[0] = fp_t(-0.0193) * p.tu - fp_t(0.2592);
  55. coeffs_x_[1] = fp_t(-0.0665) * p.tu + fp_t(0.0008);
  56. coeffs_x_[2] = fp_t(-0.0004) * p.tu + fp_t(0.2125);
  57. coeffs_x_[3] = fp_t(-0.0641) * p.tu - fp_t(0.8989);
  58. coeffs_x_[4] = fp_t(-0.0033) * p.tu + fp_t(0.0452);
  59. coeffs_y_[0] = fp_t(-0.0167) * p.tu - fp_t(0.2608);
  60. coeffs_y_[1] = fp_t(-0.0950) * p.tu + fp_t(0.0092);
  61. coeffs_y_[2] = fp_t(-0.0079) * p.tu + fp_t(0.2102);
  62. coeffs_y_[3] = fp_t(-0.0441) * p.tu - fp_t(1.6537);
  63. coeffs_y_[4] = fp_t(-0.0109) * p.tu + fp_t(0.0529);
  64. // calculate Y sub z
  65. fp_t chi = (fp_t(4.0 / 9.0) - p.tu / fp_t(120.0)) * (pi() - fp_t(2.0) * theta_s_);
  66. Y_z_ = (fp_t(4.0453) * p.tu - fp_t(4.9710)) * tan(chi) - fp_t(0.2155) * p.tu + fp_t(2.4192);
  67. Y_z_ *= fp_t(1000.0); // convert kcd/m^2 to cd/m^2
  68. fp_t tu_sq = p.tu * p.tu;
  69. fp_t theta_s_sq = theta_s_ * theta_s_;
  70. fp_t theta_s_cu = theta_s_sq * theta_s_;
  71. // calculate x sub z
  72. fp_t c1 = tu_sq * fp_t(0.00166) + p.tu * fp_t(-0.02903) + fp_t(0.11693);
  73. fp_t c2 = tu_sq * fp_t(-0.00375) + p.tu * fp_t(0.06377) + fp_t(-0.21196);
  74. fp_t c3 = tu_sq * fp_t(0.00209) + p.tu * fp_t(-0.03202) + fp_t(0.06052);
  75. fp_t c4 = p.tu * fp_t(0.00394) + fp_t(0.25886);
  76. x_z_ = c1 * theta_s_cu + c2 * theta_s_sq + c3 * theta_s_ + c4;
  77. // calculate y sub z
  78. c1 = tu_sq * fp_t(0.00275) + p.tu * fp_t(-0.04214) + fp_t(0.15346);
  79. c2 = tu_sq * fp_t(-0.00610) + p.tu * fp_t(0.08970) + fp_t(-0.26756);
  80. c3 = tu_sq * fp_t(0.00317) + p.tu * fp_t(-0.04153) + fp_t(0.06670);
  81. c4 = p.tu * fp_t(0.00516) + fp_t(0.26688);
  82. y_z_ = c1 * theta_s_cu + c2 * theta_s_sq + c3 * theta_s_ + c4;
  83. }
  84. glm::vec3 toCartesian(fp_t theta, fp_t phi)
  85. {
  86. return glm::vec3(
  87. glm::sin(theta)*glm::cos(phi),
  88. glm::sin(theta)*glm::sin(phi),
  89. glm::cos(theta));
  90. }
  91. glm::vec3 SkyModel::getColor(fp_t theta, fp_t phi)
  92. {
  93. // this is the angle between theta_s, phi_s and theta, phi
  94. fp_t gamma = glm::acos(glm::dot(toCartesian(theta, phi), toCartesian(theta_s_, phi_s_)));
  95. fp_t Y = F(coeffs_Y_, theta, gamma) / F(coeffs_Y_, 0.0, theta_s_) * Y_z_;
  96. fp_t x = F(coeffs_x_, theta, gamma) / F(coeffs_x_, 0.0, theta_s_) * x_z_;
  97. fp_t y = F(coeffs_y_, theta, gamma) / F(coeffs_y_, 0.0, theta_s_) * y_z_;
  98. // Y is luminance in candela/m^2 (aka nit)
  99. Y = min(tonemap(abs(Y)), fp_t(1.0));
  100. // convert xyY to XYZ
  101. // http://www.brucelindbloom.com/index.html?Equations.html
  102. glm::vec3 XYZ;
  103. if(y > 0)
  104. {
  105. XYZ.x = x * Y / y;
  106. XYZ.y = Y;
  107. XYZ.z = (fp_t(1.0) - x - y) * Y / y;
  108. }
  109. glm::vec3 RGB
  110. {
  111. XYZ.x * fp_t(3.2406) + XYZ.y * fp_t(-1.5372) + XYZ.z * fp_t(-0.4986),
  112. XYZ.x * fp_t(-0.9689) + XYZ.y * fp_t( 1.8758) + XYZ.z * fp_t( 0.0415),
  113. XYZ.x * fp_t(0.0557) + XYZ.y * fp_t(-0.2040) + XYZ.z * fp_t( 1.0570)
  114. };
  115. RGB.x = min(RGB.x, fp_t(1.0));
  116. RGB.y = min(RGB.y, fp_t(1.0));
  117. RGB.z = min(RGB.z, fp_t(1.0));
  118. return RGB;
  119. }
  120. fp_t SkyModel::thetaSun() const
  121. {
  122. return theta_s_;
  123. }
  124. fp_t SkyModel::phiSun() const
  125. {
  126. return phi_s_;
  127. }