util_sky_model.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /*
  2. This source is published under the following 3-clause BSD license.
  3. Copyright (c) 2012 - 2013, Lukas Hosek and Alexander Wilkie
  4. All rights reserved.
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions are met:
  7. * Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in the
  11. documentation and/or other materials provided with the distribution.
  12. * None of the names of the contributors may be used to endorse or promote
  13. products derived from this software without specific prior written
  14. permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  16. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  17. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
  19. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  22. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. /* ============================================================================
  27. This file is part of a sample implementation of the analytical skylight and
  28. solar radiance models presented in the SIGGRAPH 2012 paper
  29. "An Analytic Model for Full Spectral Sky-Dome Radiance"
  30. and the 2013 IEEE CG&A paper
  31. "Adding a Solar Radiance Function to the Hosek Skylight Model"
  32. both by
  33. Lukas Hosek and Alexander Wilkie
  34. Charles University in Prague, Czech Republic
  35. Version: 1.4a, February 22nd, 2013
  36. Version history:
  37. 1.4a February 22nd, 2013
  38. Removed unnecessary and counter-intuitive solar radius parameters
  39. from the interface of the colourspace sky dome initialisation functions.
  40. 1.4 February 11th, 2013
  41. Fixed a bug which caused the relative brightness of the solar disc
  42. and the sky dome to be off by a factor of about 6. The sun was too
  43. bright: this affected both normal and alien sun scenarios. The
  44. coefficients of the solar radiance function were changed to fix this.
  45. 1.3 January 21st, 2013 (not released to the public)
  46. Added support for solar discs that are not exactly the same size as
  47. the terrestrial sun. Also added support for suns with a different
  48. emission spectrum ("Alien World" functionality).
  49. 1.2a December 18th, 2012
  50. Fixed a mistake and some inaccuracies in the solar radiance function
  51. explanations found in ArHosekSkyModel.h. The actual source code is
  52. unchanged compared to version 1.2.
  53. 1.2 December 17th, 2012
  54. Native RGB data and a solar radiance function that matches the turbidity
  55. conditions were added.
  56. 1.1 September 2012
  57. The coefficients of the spectral model are now scaled so that the output
  58. is given in physical units: W / (m^-2 * sr * nm). Also, the output of the
  59. XYZ model is now no longer scaled to the range [0...1]. Instead, it is
  60. the result of a simple conversion from spectral data via the CIE 2 degree
  61. standard observer matching functions. Therefore, after multiplication
  62. with 683 lm / W, the Y channel now corresponds to luminance in lm.
  63. 1.0 May 11th, 2012
  64. Initial release.
  65. Please visit http://cgg.mff.cuni.cz/projects/SkylightModelling/ to check if
  66. an updated version of this code has been published!
  67. ============================================================================ */
  68. /*
  69. All instructions on how to use this code are in the accompanying header file.
  70. */
  71. #include "util/util_sky_model.h"
  72. #include "util/util_sky_model_data.h"
  73. #include <assert.h>
  74. #include <stdio.h>
  75. #include <stdlib.h>
  76. #include <math.h>
  77. CCL_NAMESPACE_BEGIN
  78. // Some macro definitions that occur elsewhere in ART, and that have to be
  79. // replicated to make this a stand-alone module.
  80. #ifndef MATH_PI
  81. # define MATH_PI 3.141592653589793
  82. #endif
  83. #ifndef MATH_DEG_TO_RAD
  84. # define MATH_DEG_TO_RAD (MATH_PI / 180.0)
  85. #endif
  86. #ifndef DEGREES
  87. # define DEGREES *MATH_DEG_TO_RAD
  88. #endif
  89. #ifndef TERRESTRIAL_SOLAR_RADIUS
  90. # define TERRESTRIAL_SOLAR_RADIUS ((0.51 DEGREES) / 2.0)
  91. #endif
  92. #ifndef ALLOC
  93. # define ALLOC(_struct) ((_struct *)malloc(sizeof(_struct)))
  94. #endif
  95. // internal definitions
  96. typedef const double *ArHosekSkyModel_Dataset;
  97. typedef const double *ArHosekSkyModel_Radiance_Dataset;
  98. // internal functions
  99. static void ArHosekSkyModel_CookConfiguration(ArHosekSkyModel_Dataset dataset,
  100. ArHosekSkyModelConfiguration config,
  101. double turbidity,
  102. double albedo,
  103. double solar_elevation)
  104. {
  105. const double *elev_matrix;
  106. int int_turbidity = (int)turbidity;
  107. double turbidity_rem = turbidity - (double)int_turbidity;
  108. solar_elevation = pow(solar_elevation / (MATH_PI / 2.0), (1.0 / 3.0));
  109. // alb 0 low turb
  110. elev_matrix = dataset + (9 * 6 * (int_turbidity - 1));
  111. for (unsigned int i = 0; i < 9; ++i) {
  112. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  113. config[i] =
  114. (1.0 - albedo) * (1.0 - turbidity_rem) *
  115. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
  116. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
  117. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
  118. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
  119. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
  120. pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
  121. }
  122. // alb 1 low turb
  123. elev_matrix = dataset + (9 * 6 * 10 + 9 * 6 * (int_turbidity - 1));
  124. for (unsigned int i = 0; i < 9; ++i) {
  125. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  126. config[i] +=
  127. (albedo) * (1.0 - turbidity_rem) *
  128. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
  129. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
  130. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
  131. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
  132. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
  133. pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
  134. }
  135. if (int_turbidity == 10)
  136. return;
  137. // alb 0 high turb
  138. elev_matrix = dataset + (9 * 6 * (int_turbidity));
  139. for (unsigned int i = 0; i < 9; ++i) {
  140. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  141. config[i] +=
  142. (1.0 - albedo) * (turbidity_rem) *
  143. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
  144. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
  145. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
  146. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
  147. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
  148. pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
  149. }
  150. // alb 1 high turb
  151. elev_matrix = dataset + (9 * 6 * 10 + 9 * 6 * (int_turbidity));
  152. for (unsigned int i = 0; i < 9; ++i) {
  153. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  154. config[i] +=
  155. (albedo) * (turbidity_rem) *
  156. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
  157. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
  158. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
  159. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
  160. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
  161. pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
  162. }
  163. }
  164. static double ArHosekSkyModel_CookRadianceConfiguration(ArHosekSkyModel_Radiance_Dataset dataset,
  165. double turbidity,
  166. double albedo,
  167. double solar_elevation)
  168. {
  169. const double *elev_matrix;
  170. int int_turbidity = (int)turbidity;
  171. double turbidity_rem = turbidity - (double)int_turbidity;
  172. double res;
  173. solar_elevation = pow(solar_elevation / (MATH_PI / 2.0), (1.0 / 3.0));
  174. // alb 0 low turb
  175. elev_matrix = dataset + (6 * (int_turbidity - 1));
  176. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  177. res = (1.0 - albedo) * (1.0 - turbidity_rem) *
  178. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
  179. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
  180. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
  181. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
  182. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
  183. pow(solar_elevation, 5.0) * elev_matrix[5]);
  184. // alb 1 low turb
  185. elev_matrix = dataset + (6 * 10 + 6 * (int_turbidity - 1));
  186. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  187. res += (albedo) * (1.0 - turbidity_rem) *
  188. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
  189. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
  190. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
  191. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
  192. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
  193. pow(solar_elevation, 5.0) * elev_matrix[5]);
  194. if (int_turbidity == 10)
  195. return res;
  196. // alb 0 high turb
  197. elev_matrix = dataset + (6 * (int_turbidity));
  198. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  199. res += (1.0 - albedo) * (turbidity_rem) *
  200. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
  201. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
  202. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
  203. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
  204. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
  205. pow(solar_elevation, 5.0) * elev_matrix[5]);
  206. // alb 1 high turb
  207. elev_matrix = dataset + (6 * 10 + 6 * (int_turbidity));
  208. //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
  209. res += (albedo) * (turbidity_rem) *
  210. (pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
  211. 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
  212. 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
  213. 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
  214. 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
  215. pow(solar_elevation, 5.0) * elev_matrix[5]);
  216. return res;
  217. }
  218. static double ArHosekSkyModel_GetRadianceInternal(ArHosekSkyModelConfiguration configuration,
  219. double theta,
  220. double gamma)
  221. {
  222. const double expM = exp(configuration[4] * gamma);
  223. const double rayM = cos(gamma) * cos(gamma);
  224. const double mieM =
  225. (1.0 + cos(gamma) * cos(gamma)) /
  226. pow((1.0 + configuration[8] * configuration[8] - 2.0 * configuration[8] * cos(gamma)), 1.5);
  227. const double zenith = sqrt(cos(theta));
  228. return (1.0 + configuration[0] * exp(configuration[1] / (cos(theta) + 0.01))) *
  229. (configuration[2] + configuration[3] * expM + configuration[5] * rayM +
  230. configuration[6] * mieM + configuration[7] * zenith);
  231. }
  232. void arhosekskymodelstate_free(ArHosekSkyModelState *state)
  233. {
  234. free(state);
  235. }
  236. double arhosekskymodel_radiance(ArHosekSkyModelState *state,
  237. double theta,
  238. double gamma,
  239. double wavelength)
  240. {
  241. int low_wl = (int)((wavelength - 320.0) / 40.0);
  242. if (low_wl < 0 || low_wl >= 11)
  243. return 0.0;
  244. double interp = fmod((wavelength - 320.0) / 40.0, 1.0);
  245. double val_low = ArHosekSkyModel_GetRadianceInternal(state->configs[low_wl], theta, gamma) *
  246. state->radiances[low_wl] * state->emission_correction_factor_sky[low_wl];
  247. if (interp < 1e-6)
  248. return val_low;
  249. double result = (1.0 - interp) * val_low;
  250. if (low_wl + 1 < 11) {
  251. result += interp *
  252. ArHosekSkyModel_GetRadianceInternal(state->configs[low_wl + 1], theta, gamma) *
  253. state->radiances[low_wl + 1] * state->emission_correction_factor_sky[low_wl + 1];
  254. }
  255. return result;
  256. }
  257. // xyz and rgb versions
  258. ArHosekSkyModelState *arhosek_xyz_skymodelstate_alloc_init(const double turbidity,
  259. const double albedo,
  260. const double elevation)
  261. {
  262. ArHosekSkyModelState *state = ALLOC(ArHosekSkyModelState);
  263. state->solar_radius = TERRESTRIAL_SOLAR_RADIUS;
  264. state->turbidity = turbidity;
  265. state->albedo = albedo;
  266. state->elevation = elevation;
  267. for (unsigned int channel = 0; channel < 3; ++channel) {
  268. ArHosekSkyModel_CookConfiguration(
  269. datasetsXYZ[channel], state->configs[channel], turbidity, albedo, elevation);
  270. state->radiances[channel] = ArHosekSkyModel_CookRadianceConfiguration(
  271. datasetsXYZRad[channel], turbidity, albedo, elevation);
  272. }
  273. return state;
  274. }
  275. CCL_NAMESPACE_END