Blender V5.0
sky_hosek.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2012-2013 Lukas Hosek and Alexander Wilkie. All rights reserved.
2 *
3 * SPDX-License-Identifier: BSD-3-Clause */
4
5/* ============================================================================
6
7This file is part of a sample implementation of the analytical skylight and
8solar radiance models presented in the SIGGRAPH 2012 paper
9
10
11 "An Analytic Model for Full Spectral Sky-Dome Radiance"
12
13and the 2013 IEEE CG&A paper
14
15 "Adding a Solar Radiance Function to the Hosek Skylight Model"
16
17 both by
18
19 Lukas Hosek and Alexander Wilkie
20 Charles University in Prague, Czech Republic
21
22
23 Version: 1.4a, February 22nd, 2013
24
25Version history:
26
271.4a February 22nd, 2013
28 Removed unnecessary and counter-intuitive solar radius parameters
29 from the interface of the color-space sky dome initialization functions.
30
311.4 February 11th, 2013
32 Fixed a bug which caused the relative brightness of the solar disc
33 and the sky dome to be off by a factor of about 6. The sun was too
34 bright: this affected both normal and alien sun scenarios. The
35 coefficients of the solar radiance function were changed to fix this.
36
371.3 January 21st, 2013 (not released to the public)
38 Added support for solar discs that are not exactly the same size as
39 the terrestrial sun. Also added support for suns with a different
40 emission spectrum ("Alien World" functionality).
41
421.2a December 18th, 2012
43 Fixed a mistake and some inaccuracies in the solar radiance function
44 explanations found in ArHosekSkyModel.h. The actual source code is
45 unchanged compared to version 1.2.
46
471.2 December 17th, 2012
48 Native RGB data and a solar radiance function that matches the turbidity
49 conditions were added.
50
511.1 September 2012
52 The coefficients of the spectral model are now scaled so that the output
53 is given in physical units: W / (m^-2 * sr * nm). Also, the output of the
54 XYZ model is now no longer scaled to the range [0...1]. Instead, it is
55 the result of a simple conversion from spectral data via the CIE 2 degree
56 standard observer matching functions. Therefore, after multiplication
57 with 683 lm / W, the Y channel now corresponds to luminance in lm.
58
591.0 May 11th, 2012
60 Initial release.
61
62
63Please visit http://cgg.mff.cuni.cz/projects/SkylightModelling/ to check if
64an updated version of this code has been published!
65
66============================================================================ */
67
68/*
69
70All instructions on how to use this code are in the accompanying header file.
71
72*/
73
77
78#include "sky_hosek.h"
79#include "sky_hosek_data.h"
80
81#include <cassert>
82#include <cmath>
83#include <cstdlib>
84
85// Some macro definitions that occur elsewhere in ART, and that have to be
86// replicated to make this a stand-alone module.
87
88#ifndef MATH_PI
89# define MATH_PI 3.141592653589793
90#endif
91
92#ifndef MATH_DEG_TO_RAD
93# define MATH_DEG_TO_RAD (MATH_PI / 180.0)
94#endif
95
96#ifndef DEGREES
97# define DEGREES *MATH_DEG_TO_RAD
98#endif
99
100#ifndef TERRESTRIAL_SOLAR_RADIUS
101# define TERRESTRIAL_SOLAR_RADIUS ((0.51 DEGREES) / 2.0)
102#endif
103
104#ifndef ALLOC
105# define ALLOC(_struct) ((_struct *)malloc(sizeof(_struct)))
106#endif
107
108/* Not defined on all platforms (macOS & WIN32). */
109using uint = unsigned int;
110
111// internal definitions
112
113using ArHosekSkyModel_Dataset = const double *;
114using ArHosekSkyModel_Radiance_Dataset = const double *;
115
116// internal functions
117
120 double turbidity,
121 double albedo,
122 double solar_elevation)
123{
124 const double *elev_matrix;
125
126 int int_turbidity = int(turbidity);
127 double turbidity_rem = turbidity - double(int_turbidity);
128
129 solar_elevation = pow(solar_elevation / (MATH_PI / 2.0), (1.0 / 3.0));
130
131 // alb 0 low turb
132
133 elev_matrix = dataset + (9 * 6 * (int_turbidity - 1));
134
135 for (uint i = 0; i < 9; ++i) {
136 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
137 config[i] =
138 (1.0 - albedo) * (1.0 - turbidity_rem) *
139 (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
140 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
141 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
142 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
143 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
144 pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
145 }
146
147 // alb 1 low turb
148 elev_matrix = dataset + (9 * 6 * 10 + 9 * 6 * (int_turbidity - 1));
149 for (uint i = 0; i < 9; ++i) {
150 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
151 config[i] +=
152 (albedo) * (1.0 - turbidity_rem) *
153 (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
154 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
155 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
156 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
157 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
158 pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
159 }
160
161 if (int_turbidity == 10) {
162 return;
163 }
164
165 // alb 0 high turb
166 elev_matrix = dataset + (9 * 6 * (int_turbidity));
167 for (uint i = 0; i < 9; ++i) {
168 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
169 config[i] +=
170 (1.0 - albedo) * (turbidity_rem) *
171 (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
172 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
173 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
174 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
175 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
176 pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
177 }
178
179 // alb 1 high turb
180 elev_matrix = dataset + (9 * 6 * 10 + 9 * 6 * (int_turbidity));
181 for (uint i = 0; i < 9; ++i) {
182 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
183 config[i] +=
184 (albedo) * (turbidity_rem) *
185 (pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
186 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
187 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
188 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
189 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
190 pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
191 }
192}
193
195 double turbidity,
196 double albedo,
197 double solar_elevation)
198{
199 const double *elev_matrix;
200
201 int int_turbidity = int(turbidity);
202 double turbidity_rem = turbidity - double(int_turbidity);
203 double res;
204 solar_elevation = pow(solar_elevation / (MATH_PI / 2.0), (1.0 / 3.0));
205
206 // alb 0 low turb
207 elev_matrix = dataset + (6 * (int_turbidity - 1));
208 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
209 res = (1.0 - albedo) * (1.0 - 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
217 // alb 1 low turb
218 elev_matrix = dataset + (6 * 10 + 6 * (int_turbidity - 1));
219 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
220 res += (albedo) * (1.0 - turbidity_rem) *
221 (pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
222 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
223 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
224 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
225 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
226 pow(solar_elevation, 5.0) * elev_matrix[5]);
227 if (int_turbidity == 10) {
228 return res;
229 }
230
231 // alb 0 high turb
232 elev_matrix = dataset + (6 * (int_turbidity));
233 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
234 res += (1.0 - albedo) * (turbidity_rem) *
235 (pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
236 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
237 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
238 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
239 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
240 pow(solar_elevation, 5.0) * elev_matrix[5]);
241
242 // alb 1 high turb
243 elev_matrix = dataset + (6 * 10 + 6 * (int_turbidity));
244 //(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
245 res += (albedo) * (turbidity_rem) *
246 (pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
247 5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
248 10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
249 10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
250 5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
251 pow(solar_elevation, 5.0) * elev_matrix[5]);
252 return res;
253}
254
256 const SKY_ArHosekSkyModelConfiguration configuration, const double theta, const double gamma)
257{
258 const double expM = exp(configuration[4] * gamma);
259 const double rayM = cos(gamma) * cos(gamma);
260 const double mieM =
261 (1.0 + cos(gamma) * cos(gamma)) /
262 pow((1.0 + configuration[8] * configuration[8] - 2.0 * configuration[8] * cos(gamma)), 1.5);
263 const double zenith = sqrt(cos(theta));
264
265 return (1.0 + configuration[0] * exp(configuration[1] / (cos(theta) + 0.01))) *
266 (configuration[2] + configuration[3] * expM + configuration[5] * rayM +
267 configuration[6] * mieM + configuration[7] * zenith);
268}
269
274
276 double theta,
277 double gamma,
278 double wavelength)
279{
280 int low_wl = int((wavelength - 320.0) / 40.0);
281
282 if (low_wl < 0 || low_wl >= 11) {
283 return 0.0;
284 }
285
286 double interp = fmod((wavelength - 320.0) / 40.0, 1.0);
287
288 double val_low = ArHosekSkyModel_GetRadianceInternal(state->configs[low_wl], theta, gamma) *
289 state->radiances[low_wl] * state->emission_correction_factor_sky[low_wl];
290
291 if (interp < 1e-6) {
292 return val_low;
293 }
294
295 double result = (1.0 - interp) * val_low;
296
297 if (low_wl + 1 < 11) {
298 result += interp *
299 ArHosekSkyModel_GetRadianceInternal(state->configs[low_wl + 1], theta, gamma) *
300 state->radiances[low_wl + 1] * state->emission_correction_factor_sky[low_wl + 1];
301 }
302
303 return result;
304}
305
306// xyz and rgb versions
307
309 const double albedo,
310 const double elevation)
311{
313
314 state->solar_radius = TERRESTRIAL_SOLAR_RADIUS;
315 state->turbidity = turbidity;
316 state->albedo = albedo;
317 state->elevation = elevation;
318
319 for (uint channel = 0; channel < 3; ++channel) {
321 datasetsXYZ[channel], state->configs[channel], turbidity, albedo, elevation);
322
324 datasetsXYZRad[channel], turbidity, albedo, elevation);
325 }
326
327 return state;
328}
void BLI_kdtree_nd_ free(KDTree *tree)
unsigned int uint
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
#define pow
#define exp
#define cos
#define sqrt
ccl_device_inline float interp(const float a, const float b, const float t)
Definition math_base.h:502
ccl_device_inline float2 fmod(const float2 a, const float b)
static ulong state[N]
const double * ArHosekSkyModel_Dataset
static void ArHosekSkyModel_CookConfiguration(ArHosekSkyModel_Dataset dataset, SKY_ArHosekSkyModelConfiguration config, double turbidity, double albedo, double solar_elevation)
void SKY_arhosekskymodelstate_free(SKY_ArHosekSkyModelState *state)
#define TERRESTRIAL_SOLAR_RADIUS
SKY_ArHosekSkyModelState * SKY_arhosek_xyz_skymodelstate_alloc_init(const double turbidity, const double albedo, const double elevation)
static double ArHosekSkyModel_CookRadianceConfiguration(ArHosekSkyModel_Radiance_Dataset dataset, double turbidity, double albedo, double solar_elevation)
#define ALLOC(_struct)
double SKY_arhosekskymodel_radiance(SKY_ArHosekSkyModelState *state, double theta, double gamma, double wavelength)
#define MATH_PI
Definition sky_hosek.cpp:89
const double * ArHosekSkyModel_Radiance_Dataset
static double ArHosekSkyModel_GetRadianceInternal(const SKY_ArHosekSkyModelConfiguration configuration, const double theta, const double gamma)
double[9] SKY_ArHosekSkyModelConfiguration
Definition sky_hosek.h:281
static const double * datasetsXYZ[]
static const double * datasetsXYZRad[]
i
Definition text_draw.cc:230