Blender V4.3
sky_nishita.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2020 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
9#include "sky_float3.h"
10#include "sky_model.h"
11
12/* Constants */
13static const float rayleigh_scale = 8e3f; /* Rayleigh scale height (m). */
14static const float mie_scale = 1.2e3f; /* Mie scale height (m). */
15static const float mie_coeff = 2e-5f; /* Mie scattering coefficient (m^-1). */
16static const float mie_G = 0.76f; /* aerosols anisotropy. */
17static const float sqr_G = mie_G * mie_G; /* squared aerosols anisotropy. */
18static const float earth_radius = 6360e3f; /* radius of Earth (m). */
19static const float atmosphere_radius = 6420e3f; /* radius of atmosphere (m). */
20static const int steps = 32; /* segments of primary ray. */
21static const int num_wavelengths = 21; /* number of wavelengths. */
22static const int min_wavelength = 380; /* lowest sampled wavelength (nm). */
23static const int max_wavelength = 780; /* highest sampled wavelength (nm). */
24/* Step between each sampled wavelength (nm). */
26/* Sun irradiance on top of the atmosphere (W*m^-2*nm^-1) */
27static const float irradiance[] = {
28 1.45756829855592995315f, 1.56596305559738380175f, 1.65148449067670455293f,
29 1.71496242737209314555f, 1.75797983805020541226f, 1.78256407885924539336f,
30 1.79095108475838560302f, 1.78541550133410664714f, 1.76815554864306845317f,
31 1.74122069647250410362f, 1.70647127164943679389f, 1.66556087452739887134f,
32 1.61993437242451854274f, 1.57083597368892080581f, 1.51932335059305478886f,
33 1.46628494965214395407f, 1.41245852740172450623f, 1.35844961970384092709f,
34 1.30474913844739281998f, 1.25174963272610817455f, 1.19975998755420620867f};
35/* Rayleigh scattering coefficient (m^-1) */
36static const float rayleigh_coeff[] = {
37 0.00005424820087636473f, 0.00004418549866505454f, 0.00003635151910165377f,
38 0.00003017929012024763f, 0.00002526320226989157f, 0.00002130859310621843f,
39 0.00001809838025320633f, 0.00001547057129129042f, 0.00001330284977336850f,
40 0.00001150184784075764f, 0.00000999557429990163f, 0.00000872799973630707f,
41 0.00000765513700977967f, 0.00000674217203751443f, 0.00000596134125832052f,
42 0.00000529034598065810f, 0.00000471115687557433f, 0.00000420910481110487f,
43 0.00000377218381260133f, 0.00000339051255477280f, 0.00000305591531679811f};
44/* Ozone absorption coefficient (m^-1) */
45static const float ozone_coeff[] = {
46 0.00000000325126849861f, 0.00000000585395365047f, 0.00000001977191155085f,
47 0.00000007309568762914f, 0.00000020084561514287f, 0.00000040383958096161f,
48 0.00000063551335912363f, 0.00000096707041180970f, 0.00000154797400424410f,
49 0.00000209038647223331f, 0.00000246128056164565f, 0.00000273551299461512f,
50 0.00000215125863128643f, 0.00000159051840791988f, 0.00000112356197979857f,
51 0.00000073527551487574f, 0.00000046450130357806f, 0.00000033096079921048f,
52 0.00000022512612292678f, 0.00000014879129266490f, 0.00000016828623364192f};
53/* CIE XYZ color matching functions */
54static const float cmf_xyz[][3] = {{0.00136800000f, 0.00003900000f, 0.00645000100f},
55 {0.01431000000f, 0.00039600000f, 0.06785001000f},
56 {0.13438000000f, 0.00400000000f, 0.64560000000f},
57 {0.34828000000f, 0.02300000000f, 1.74706000000f},
58 {0.29080000000f, 0.06000000000f, 1.66920000000f},
59 {0.09564000000f, 0.13902000000f, 0.81295010000f},
60 {0.00490000000f, 0.32300000000f, 0.27200000000f},
61 {0.06327000000f, 0.71000000000f, 0.07824999000f},
62 {0.29040000000f, 0.95400000000f, 0.02030000000f},
63 {0.59450000000f, 0.99500000000f, 0.00390000000f},
64 {0.91630000000f, 0.87000000000f, 0.00165000100f},
65 {1.06220000000f, 0.63100000000f, 0.00080000000f},
66 {0.85444990000f, 0.38100000000f, 0.00019000000f},
67 {0.44790000000f, 0.17500000000f, 0.00002000000f},
68 {0.16490000000f, 0.06100000000f, 0.00000000000f},
69 {0.04677000000f, 0.01700000000f, 0.00000000000f},
70 {0.01135916000f, 0.00410200000f, 0.00000000000f},
71 {0.00289932700f, 0.00104700000f, 0.00000000000f},
72 {0.00069007860f, 0.00024920000f, 0.00000000000f},
73 {0.00016615050f, 0.00006000000f, 0.00000000000f},
74 {0.00004150994f, 0.00001499000f, 0.00000000000f}};
75
76/* Parameters for optical depth quadrature.
77 * See the comment in ray_optical_depth for more detail.
78 * Computed using sympy and following Python code:
79 * # from sympy.integrals.quadrature import gauss_laguerre
80 * # from sympy import exp
81 * # x, w = gauss_laguerre(8, 50)
82 * # xend = 25
83 * # print([(xi / xend).evalf(10) for xi in x])
84 * # print([(wi * exp(xi) / xend).evalf(10) for xi, wi in zip(x, w)])
85 */
86static const int quadrature_steps = 8;
87static const float quadrature_nodes[] = {0.006811185292f,
88 0.03614807107f,
89 0.09004346519f,
90 0.1706680068f,
91 0.2818362161f,
92 0.4303406404f,
93 0.6296271457f,
94 0.9145252695f};
95static const float quadrature_weights[] = {0.01750893642f,
96 0.04135477391f,
97 0.06678839063f,
98 0.09507698807f,
99 0.1283416365f,
100 0.1707430204f,
101 0.2327233347f,
102 0.3562490486f};
103
104static float3 geographical_to_direction(float lat, float lon)
105{
106 return make_float3(cosf(lat) * cosf(lon), cosf(lat) * sinf(lon), sinf(lat));
107}
108
109static float3 spec_to_xyz(const float *spectrum)
110{
111 float3 xyz = make_float3(0.0f, 0.0f, 0.0f);
112 for (int i = 0; i < num_wavelengths; i++) {
113 xyz.x += cmf_xyz[i][0] * spectrum[i];
114 xyz.y += cmf_xyz[i][1] * spectrum[i];
115 xyz.z += cmf_xyz[i][2] * spectrum[i];
116 }
117 return xyz * step_lambda;
118}
119
120/* Atmosphere volume models */
121static float density_rayleigh(float height)
122{
123 return expf(-height / rayleigh_scale);
124}
125
126static float density_mie(float height)
127{
128 return expf(-height / mie_scale);
129}
130
131static float density_ozone(float height)
132{
133 float den = 0.0f;
134 if (height >= 10000.0f && height < 25000.0f) {
135 den = 1.0f / 15000.0f * height - 2.0f / 3.0f;
136 }
137 else if (height >= 25000 && height < 40000) {
138 den = -(1.0f / 15000.0f * height - 8.0f / 3.0f);
139 }
140 return den;
141}
142
143static float phase_rayleigh(float mu)
144{
145 return 3.0f / (16.0f * M_PI_F) * (1.0f + sqr(mu));
146}
147
148static float phase_mie(float mu)
149{
150 return (3.0f * (1.0f - sqr_G) * (1.0f + sqr(mu))) /
151 (8.0f * M_PI_F * (2.0f + sqr_G) * powf((1.0f + sqr_G - 2.0f * mie_G * mu), 1.5));
152}
153
154/* Intersection helpers */
156{
157 if (dir.z >= 0) {
158 return false;
159 }
160 float b = -2.0f * dot(dir, -pos);
161 float c = len_squared(pos) - sqr(earth_radius);
162 float t = b * b - 4.0f * c;
163 if (t >= 0.0f) {
164 return true;
165 }
166 return false;
167}
168
170{
171 float b = -2.0f * dot(dir, -pos);
172 float c = len_squared(pos) - sqr(atmosphere_radius);
173 float t = (-b + sqrtf(b * b - 4.0f * c)) / 2.0f;
174 return make_float3(pos.x + dir.x * t, pos.y + dir.y * t, pos.z + dir.z * t);
175}
176
177static float3 ray_optical_depth(float3 ray_origin, float3 ray_dir)
178{
179 /* This function computes the optical depth along a ray.
180 * Instead of using classic ray marching, the code is based on Gauss-Laguerre quadrature,
181 * which is designed to compute the integral of f(x)*exp(-x) from 0 to infinity.
182 * This works well here, since the optical depth along the ray tends to decrease exponentially.
183 * By setting f(x) = g(x) exp(x), the exponentials cancel out and we get the integral of g(x).
184 * The nodes and weights used here are the standard n=6 Gauss-Laguerre values, except that
185 * the exp(x) scaling factor is already included in the weights.
186 * The parametrization along the ray is scaled so that the last quadrature node is still within
187 * the atmosphere. */
188 float3 ray_end = atmosphere_intersection(ray_origin, ray_dir);
189 float ray_length = distance(ray_origin, ray_end);
190
191 float3 segment = ray_length * ray_dir;
192
193 /* instead of tracking the transmission spectrum across all wavelengths directly,
194 * we use the fact that the density always has the same spectrum for each type of
195 * scattering, so we split the density into a constant spectrum and a factor and
196 * only track the factors */
197 float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f);
198
199 for (int i = 0; i < quadrature_steps; i++) {
200 float3 P = ray_origin + quadrature_nodes[i] * segment;
201
202 /* height above sea level */
203 float height = len(P) - earth_radius;
204
205 float3 density = make_float3(
206 density_rayleigh(height), density_mie(height), density_ozone(height));
207 optical_depth += density * quadrature_weights[i];
208 }
209
210 return optical_depth * ray_length;
211}
212
213static void single_scattering(float3 ray_dir,
214 float3 sun_dir,
215 float3 ray_origin,
216 float air_density,
217 float dust_density,
218 float ozone_density,
219 float *r_spectrum)
220{
221 /* this code computes single-inscattering along a ray through the atmosphere */
222 float3 ray_end = atmosphere_intersection(ray_origin, ray_dir);
223 float ray_length = distance(ray_origin, ray_end);
224
225 /* to compute the inscattering, we step along the ray in segments and accumulate
226 * the inscattering as well as the optical depth along each segment */
227 float segment_length = ray_length / steps;
228 float3 segment = segment_length * ray_dir;
229
230 /* instead of tracking the transmission spectrum across all wavelengths directly,
231 * we use the fact that the density always has the same spectrum for each type of
232 * scattering, so we split the density into a constant spectrum and a factor and
233 * only track the factors */
234 float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f);
235
236 /* zero out light accumulation */
237 for (int wl = 0; wl < num_wavelengths; wl++) {
238 r_spectrum[wl] = 0.0f;
239 }
240
241 /* phase function for scattering and the density scale factor */
242 float mu = dot(ray_dir, sun_dir);
243 float3 phase_function = make_float3(phase_rayleigh(mu), phase_mie(mu), 0.0f);
244 float3 density_scale = make_float3(air_density, dust_density, ozone_density);
245
246 /* the density and in-scattering of each segment is evaluated at its middle */
247 float3 P = ray_origin + 0.5f * segment;
248
249 for (int i = 0; i < steps; i++) {
250 /* height above sea level */
251 float height = len(P) - earth_radius;
252
253 /* evaluate and accumulate optical depth along the ray */
254 float3 density = density_scale * make_float3(density_rayleigh(height),
255 density_mie(height),
256 density_ozone(height));
257 optical_depth += segment_length * density;
258
259 /* if the Earth isn't in the way, evaluate inscattering from the sun */
260 if (!surface_intersection(P, sun_dir)) {
261 float3 light_optical_depth = density_scale * ray_optical_depth(P, sun_dir);
262 float3 total_optical_depth = optical_depth + light_optical_depth;
263
264 /* attenuation of light */
265 for (int wl = 0; wl < num_wavelengths; wl++) {
266 float3 extinction_density = total_optical_depth * make_float3(rayleigh_coeff[wl],
267 1.11f * mie_coeff,
268 ozone_coeff[wl]);
269 float attenuation = expf(-reduce_add(extinction_density));
270
271 float3 scattering_density = density * make_float3(rayleigh_coeff[wl], mie_coeff, 0.0f);
272
273 /* the total inscattered radiance from one segment is:
274 * Tr(A<->B) * Tr(B<->C) * sigma_s * phase * L * segment_length
275 *
276 * These terms are:
277 * Tr(A<->B): Transmission from start to scattering position (tracked in optical_depth)
278 * Tr(B<->C): Transmission from scattering position to light (computed in
279 * ray_optical_depth) sigma_s: Scattering density phase: Phase function of the scattering
280 * type (Rayleigh or Mie) L: Radiance coming from the light source segment_length: The
281 * length of the segment
282 *
283 * The code here is just that, with a bit of additional optimization to not store full
284 * spectra for the optical depth
285 */
286 r_spectrum[wl] += attenuation * reduce_add(phase_function * scattering_density) *
287 irradiance[wl] * segment_length;
288 }
289 }
290
291 /* advance along ray */
292 P += segment;
293 }
294}
295
297 int stride,
298 int start_y,
299 int end_y,
300 int width,
301 int height,
302 float sun_elevation,
303 float altitude,
304 float air_density,
305 float dust_density,
306 float ozone_density)
307{
308 /* calculate texture pixels */
309 float spectrum[num_wavelengths];
310 int half_width = width / 2;
311 float3 cam_pos = make_float3(0, 0, earth_radius + altitude);
312 float3 sun_dir = geographical_to_direction(sun_elevation, 0.0f);
313
314 float latitude_step = M_PI_2_F / height;
315 float longitude_step = M_2PI_F / width;
316 float half_lat_step = latitude_step / 2.0f;
317
318 for (int y = start_y; y < end_y; y++) {
319 /* sample more pixels toward the horizon */
320 float latitude = (M_PI_2_F + half_lat_step) * sqr(float(y) / height);
321
322 float *pixel_row = pixels + (y * width * stride);
323 for (int x = 0; x < half_width; x++) {
324 float longitude = longitude_step * x - M_PI_F;
325
326 float3 dir = geographical_to_direction(latitude, longitude);
327 single_scattering(dir, sun_dir, cam_pos, air_density, dust_density, ozone_density, spectrum);
328 float3 xyz = spec_to_xyz(spectrum);
329
330 /* store pixels */
331 int pos_x = x * stride;
332 pixel_row[pos_x] = xyz.x;
333 pixel_row[pos_x + 1] = xyz.y;
334 pixel_row[pos_x + 2] = xyz.z;
335 /* mirror sky */
336 int mirror_x = (width - x - 1) * stride;
337 pixel_row[mirror_x] = xyz.x;
338 pixel_row[mirror_x + 1] = xyz.y;
339 pixel_row[mirror_x + 2] = xyz.z;
340 }
341 }
342}
343
344/*********** Sun ***********/
345static void sun_radiation(float3 cam_dir,
346 float altitude,
347 float air_density,
348 float dust_density,
349 float solid_angle,
350 float *r_spectrum)
351{
352 float3 cam_pos = make_float3(0, 0, earth_radius + altitude);
353 float3 optical_depth = ray_optical_depth(cam_pos, cam_dir);
354
355 /* compute final spectrum */
356 for (int i = 0; i < num_wavelengths; i++) {
357 /* combine spectra and the optical depth into transmittance */
358 float transmittance = rayleigh_coeff[i] * optical_depth.x * air_density +
359 1.11f * mie_coeff * optical_depth.y * dust_density;
360 r_spectrum[i] = irradiance[i] * expf(-transmittance) / solid_angle;
361 }
362}
363
364void SKY_nishita_skymodel_precompute_sun(float sun_elevation,
365 float angular_diameter,
366 float altitude,
367 float air_density,
368 float dust_density,
369 float *r_pixel_bottom,
370 float *r_pixel_top)
371{
372 /* definitions */
373 float half_angular = angular_diameter / 2.0f;
374 float solid_angle = M_2PI_F * (1.0f - cosf(half_angular));
375 float spectrum[num_wavelengths];
376 float bottom = sun_elevation - half_angular;
377 float top = sun_elevation + half_angular;
378 float elevation_bottom, elevation_top;
379 float3 pix_bottom, pix_top, sun_dir;
380
381 /* compute 2 pixels for sun disc */
382 elevation_bottom = (bottom > 0.0f) ? bottom : 0.0f;
383 elevation_top = (top > 0.0f) ? top : 0.0f;
384 sun_dir = geographical_to_direction(elevation_bottom, 0.0f);
385 sun_radiation(sun_dir, altitude, air_density, dust_density, solid_angle, spectrum);
386 pix_bottom = spec_to_xyz(spectrum);
387 sun_dir = geographical_to_direction(elevation_top, 0.0f);
388 sun_radiation(sun_dir, altitude, air_density, dust_density, solid_angle, spectrum);
389 pix_top = spec_to_xyz(spectrum);
390
391 /* store pixels */
392 r_pixel_bottom[0] = pix_bottom.x;
393 r_pixel_bottom[1] = pix_bottom.y;
394 r_pixel_bottom[2] = pix_bottom.z;
395 r_pixel_top[0] = pix_top.x;
396 r_pixel_top[1] = pix_top.y;
397 r_pixel_top[2] = pix_top.z;
398}
local_group_size(16, 16) .push_constant(Type b
additional_info("compositor_sum_squared_difference_float_shared") .push_constant(Type output_img float dot(value.rgb, luminance_coefficients)") .define("LOAD(value)"
#define sinf(x)
#define cosf(x)
#define expf(x)
#define powf(x, y)
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define sqrtf(x)
int len
ccl_device_inline float len_squared(const float2 a)
ccl_device_inline float reduce_add(const float2 a)
#define M_PI_F
Definition mikk_util.hh:15
float distance(float a, float b)
#define M_PI_2_F
Definition sky_float3.h:20
#define M_2PI_F
Definition sky_float3.h:23
static const int num_wavelengths
static float3 ray_optical_depth(float3 ray_origin, float3 ray_dir)
static float3 spec_to_xyz(const float *spectrum)
static const float rayleigh_coeff[]
static float phase_rayleigh(float mu)
static const float mie_coeff
static const int quadrature_steps
static void single_scattering(float3 ray_dir, float3 sun_dir, float3 ray_origin, float air_density, float dust_density, float ozone_density, float *r_spectrum)
static const float sqr_G
static const float earth_radius
static float3 geographical_to_direction(float lat, float lon)
static const float irradiance[]
static float phase_mie(float mu)
static void sun_radiation(float3 cam_dir, float altitude, float air_density, float dust_density, float solid_angle, float *r_spectrum)
static const float atmosphere_radius
static float density_mie(float height)
static bool surface_intersection(float3 pos, float3 dir)
static const float ozone_coeff[]
static float density_rayleigh(float height)
static const float rayleigh_scale
static const float quadrature_nodes[]
static const float step_lambda
void SKY_nishita_skymodel_precompute_sun(float sun_elevation, float angular_diameter, float altitude, float air_density, float dust_density, float *r_pixel_bottom, float *r_pixel_top)
static float3 atmosphere_intersection(float3 pos, float3 dir)
static const float cmf_xyz[][3]
static float density_ozone(float height)
static const int min_wavelength
static const int max_wavelength
static const float mie_G
static const int steps
static const float quadrature_weights[]
static const float mie_scale
void SKY_nishita_skymodel_precompute_texture(float *pixels, int stride, int start_y, int end_y, int width, int height, float sun_elevation, float altitude, float air_density, float dust_density, float ozone_density)
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27
ccl_device_inline float sqr(float a)
Definition util/math.h:782