diff --git a/sandbox/tests/test scenes/sun and sky/20 - nishita93 - theta 0 - ptne.appleseed b/sandbox/tests/test scenes/sun and sky/20 - nishita93 - theta 0 - ptne.appleseed new file mode 100644 index 0000000000..66fc910b34 --- /dev/null +++ b/sandbox/tests/test scenes/sun and sky/20 - nishita93 - theta 0 - ptne.appleseed @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + 0.95313944699513697 -0.00430888342411300 -0.30250062496702701 -1.21740753536959101 + 0.00000000000000000 0.99989856662897403 -0.01424276845712700 0.35374149338383298 + 0.30253131173781700 0.01357534445090600 0.95304276684796996 4.67618990067894735 + 0.00000000000000000 0.00000000000000000 0.00000000000000000 1.00000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.729412019 0.729412019 0.729412019 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/tests/test scenes/sun and sky/21 - nishita93 - theta 60 - ptne.appleseed b/sandbox/tests/test scenes/sun and sky/21 - nishita93 - theta 60 - ptne.appleseed new file mode 100644 index 0000000000..4e6119ed31 --- /dev/null +++ b/sandbox/tests/test scenes/sun and sky/21 - nishita93 - theta 60 - ptne.appleseed @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + 0.95313944699513697 -0.00430888342411300 -0.30250062496702701 -1.21740753536959101 + 0.00000000000000000 0.99989856662897403 -0.01424276845712700 0.35374149338383298 + 0.30253131173781700 0.01357534445090600 0.95304276684796996 4.67618990067894735 + 0.00000000000000000 0.00000000000000000 0.00000000000000000 1.00000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.729412019 0.729412019 0.729412019 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/tests/test scenes/sun and sky/22 - nishita93 - theta 86 - ptne.appleseed b/sandbox/tests/test scenes/sun and sky/22 - nishita93 - theta 86 - ptne.appleseed new file mode 100644 index 0000000000..75b6672775 --- /dev/null +++ b/sandbox/tests/test scenes/sun and sky/22 - nishita93 - theta 86 - ptne.appleseed @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + 0.95313944699513697 -0.00430888342411300 -0.30250062496702701 -1.21740753536959101 + 0.00000000000000000 0.99989856662897403 -0.01424276845712700 0.35374149338383298 + 0.30253131173781700 0.01357534445090600 0.95304276684796996 4.67618990067894735 + 0.00000000000000000 0.00000000000000000 0.00000000000000000 1.00000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.729412019 0.729412019 0.729412019 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/tests/test scenes/sun and sky/23 - nishita93 - theta 86 - 10000m - ptne.appleseed b/sandbox/tests/test scenes/sun and sky/23 - nishita93 - theta 86 - 10000m - ptne.appleseed new file mode 100644 index 0000000000..c73c74a93f --- /dev/null +++ b/sandbox/tests/test scenes/sun and sky/23 - nishita93 - theta 86 - 10000m - ptne.appleseed @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + 0.95313944699513697 -0.00430888342411300 -0.30250062496702701 -1.21740753536959101 + 0.00000000000000000 0.99989856662897403 -0.01424276845712700 0.35374149338383298 + 0.30253131173781700 0.01357534445090600 0.95304276684796996 4.67618990067894735 + 0.00000000000000000 0.00000000000000000 0.00000000000000000 1.00000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.729412019 0.729412019 0.729412019 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/tests/test scenes/sun and sky/24 - nishita93 - theta 86 - 25000m - ptne.appleseed b/sandbox/tests/test scenes/sun and sky/24 - nishita93 - theta 86 - 25000m - ptne.appleseed new file mode 100644 index 0000000000..6a63375b4e --- /dev/null +++ b/sandbox/tests/test scenes/sun and sky/24 - nishita93 - theta 86 - 25000m - ptne.appleseed @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + 0.95313944699513697 -0.00430888342411300 -0.30250062496702701 -1.21740753536959101 + 0.00000000000000000 0.99989856662897403 -0.01424276845712700 0.35374149338383298 + 0.30253131173781700 0.01357534445090600 0.95304276684796996 4.67618990067894735 + 0.00000000000000000 0.00000000000000000 0.00000000000000000 1.00000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.729412019 0.729412019 0.729412019 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/tests/test scenes/sun and sky/25 - nishita93 - theta 86 - 50000m - ptne.appleseed b/sandbox/tests/test scenes/sun and sky/25 - nishita93 - theta 86 - 50000m - ptne.appleseed new file mode 100644 index 0000000000..04b8f4e139 --- /dev/null +++ b/sandbox/tests/test scenes/sun and sky/25 - nishita93 - theta 86 - 50000m - ptne.appleseed @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + 0.95313944699513697 -0.00430888342411300 -0.30250062496702701 -1.21740753536959101 + 0.00000000000000000 0.99989856662897403 -0.01424276845712700 0.35374149338383298 + 0.30253131173781700 0.01357534445090600 0.95304276684796996 4.67618990067894735 + 0.00000000000000000 0.00000000000000000 0.00000000000000000 1.00000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.729412019 0.729412019 0.729412019 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/tests/test scenes/sun and sky/26 - nishita93 - theta 86 - 15000000m - ptne.appleseed b/sandbox/tests/test scenes/sun and sky/26 - nishita93 - theta 86 - 15000000m - ptne.appleseed new file mode 100644 index 0000000000..7a53c62059 --- /dev/null +++ b/sandbox/tests/test scenes/sun and sky/26 - nishita93 - theta 86 - 15000000m - ptne.appleseed @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + 0.99280120839318042 -0.11926363706205065 0.01104289308886783 5536.50999955567021971 + 0.00000000000000000 0.09219791032453716 0.99574070185555275 499224.97875194193329662 + -0.11977378934074309 -0.98857257204846638 0.09153419678152656 45892.28435730801720638 + 0.00000000000000000 0.00000000000000000 0.00000000000000000 1.00000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.729412019 0.729412019 0.729412019 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + 1.000000000 1.000000000 1.000000000 + + + 1.000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/tests/test scenes/sun and sky/ref/20 - nishita93 - theta 0 - ptne.png b/sandbox/tests/test scenes/sun and sky/ref/20 - nishita93 - theta 0 - ptne.png new file mode 100644 index 0000000000..50dd2e28f7 Binary files /dev/null and b/sandbox/tests/test scenes/sun and sky/ref/20 - nishita93 - theta 0 - ptne.png differ diff --git a/sandbox/tests/test scenes/sun and sky/ref/21 - nishita93 - theta 60 - ptne.png b/sandbox/tests/test scenes/sun and sky/ref/21 - nishita93 - theta 60 - ptne.png new file mode 100644 index 0000000000..34a484b24e Binary files /dev/null and b/sandbox/tests/test scenes/sun and sky/ref/21 - nishita93 - theta 60 - ptne.png differ diff --git a/sandbox/tests/test scenes/sun and sky/ref/22 - nishita93 - theta 86 - ptne.png b/sandbox/tests/test scenes/sun and sky/ref/22 - nishita93 - theta 86 - ptne.png new file mode 100644 index 0000000000..cda7473c6c Binary files /dev/null and b/sandbox/tests/test scenes/sun and sky/ref/22 - nishita93 - theta 86 - ptne.png differ diff --git a/sandbox/tests/test scenes/sun and sky/ref/23 - nishita93 - theta 86 - 10000m - ptne.png b/sandbox/tests/test scenes/sun and sky/ref/23 - nishita93 - theta 86 - 10000m - ptne.png new file mode 100644 index 0000000000..68ec908e3f Binary files /dev/null and b/sandbox/tests/test scenes/sun and sky/ref/23 - nishita93 - theta 86 - 10000m - ptne.png differ diff --git a/sandbox/tests/test scenes/sun and sky/ref/24 - nishita93 - theta 86 - 25000m - ptne.png b/sandbox/tests/test scenes/sun and sky/ref/24 - nishita93 - theta 86 - 25000m - ptne.png new file mode 100644 index 0000000000..cc6e7d036e Binary files /dev/null and b/sandbox/tests/test scenes/sun and sky/ref/24 - nishita93 - theta 86 - 25000m - ptne.png differ diff --git a/sandbox/tests/test scenes/sun and sky/ref/25 - nishita93 - theta 86 - 25000m - ptne.png b/sandbox/tests/test scenes/sun and sky/ref/25 - nishita93 - theta 86 - 25000m - ptne.png new file mode 100644 index 0000000000..e609ff7f37 Binary files /dev/null and b/sandbox/tests/test scenes/sun and sky/ref/25 - nishita93 - theta 86 - 25000m - ptne.png differ diff --git a/sandbox/tests/test scenes/sun and sky/ref/26 - nishita93 - theta 86 - 15000000m - ptne.png b/sandbox/tests/test scenes/sun and sky/ref/26 - nishita93 - theta 86 - 15000000m - ptne.png new file mode 100644 index 0000000000..0a9da67b49 Binary files /dev/null and b/sandbox/tests/test scenes/sun and sky/ref/26 - nishita93 - theta 86 - 15000000m - ptne.png differ diff --git a/src/appleseed/CMakeLists.txt b/src/appleseed/CMakeLists.txt index 68b428c1e4..cf49195acf 100644 --- a/src/appleseed/CMakeLists.txt +++ b/src/appleseed/CMakeLists.txt @@ -1806,6 +1806,8 @@ source_group ("renderer\\modeling\\environment" FILES set (renderer_modeling_environmentedf_sources renderer/modeling/environmentedf/ArHosekSkyModelData_CIEXYZ.h + renderer/modeling/environmentedf/atmosphereshell.cpp + renderer/modeling/environmentedf/atmosphereshell.h renderer/modeling/environmentedf/constantenvironmentedf.cpp renderer/modeling/environmentedf/constantenvironmentedf.h renderer/modeling/environmentedf/constanthemisphereenvironmentedf.cpp @@ -1825,8 +1827,14 @@ set (renderer_modeling_environmentedf_sources renderer/modeling/environmentedf/latlongmapenvironmentedf.h renderer/modeling/environmentedf/mirrorballmapenvironmentedf.cpp renderer/modeling/environmentedf/mirrorballmapenvironmentedf.h + renderer/modeling/environmentedf/nishita93environmentedf.cpp + renderer/modeling/environmentedf/nishita93environmentedf.h + renderer/modeling/environmentedf/opticaldepth.cpp + renderer/modeling/environmentedf/opticaldepth.h renderer/modeling/environmentedf/oslenvironmentedf.cpp renderer/modeling/environmentedf/oslenvironmentedf.h + renderer/modeling/environmentedf/physicalsky.cpp + renderer/modeling/environmentedf/physicalsky.h renderer/modeling/environmentedf/preethamenvironmentedf.cpp renderer/modeling/environmentedf/preethamenvironmentedf.h renderer/modeling/environmentedf/sphericalcoordinates.h diff --git a/src/appleseed/renderer/modeling/environmentedf/atmosphereshell.cpp b/src/appleseed/renderer/modeling/environmentedf/atmosphereshell.cpp new file mode 100644 index 0000000000..1ad31b7e85 --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/atmosphereshell.cpp @@ -0,0 +1,171 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// appleseed.foundation headers. +#include "foundation/math/intersection/raysphere.h" +#include "foundation/math/ray.h" + +// Header file. +#include "atmosphereshell.h" + +#include + +using namespace foundation; + +float get_rayleigh_density(float height) +{ + return expf((-height + earth_radius) / rayleigh_scale); +} + +float get_mie_density(float height) +{ + return expf((-height + earth_radius) / mie_scale); +} + +float get_ozone_density(float height) +{ + const float total_height = height - earth_radius; + if (total_height < ozone_start) + return 0.0f; + if (total_height < ozone_peak) + return (total_height - ozone_start) * (1.0f / (ozone_peak - ozone_start)); + return expf(-(total_height - ozone_peak) / ozone_start); +} + +const int shell::n_atmosphere_shells; +shell shell::atmosphere_shells[shell::n_atmosphere_shells + 1]; + +shell::shell() {} + +shell::shell(int i) : index(i) +{ + radius = shell::find_radius(index); + densities(); +} + +shell::shell(int i, float r, float rd, float md, float od) : + index(i), + radius(r), + rayleigh_density(rd), + mie_density(md), + ozone_density(od) {} + +bool shell::ray_in_shell(const Ray3f& ray) const +{ + return (norm(ray.m_org) < radius); +} + +shell::intersection shell::intersection_distance_inside(const Ray3f& ray) const +{ + const float radius_sqr = radius * radius; + float b = -2.0f * dot(ray.m_dir, -ray.m_org); + float c = square_norm(ray.m_org) - radius_sqr; + float distance = (-b + sqrtf(b * b - 4.0f * c)) / 2.0f; + return shell::intersection(distance, this); +} + +size_t shell::intersection_distances_outside(const Ray3f& ray, shell::intersection intersections[2]) const +{ + float distances[2] = { 0.0f }; + size_t n_distances = intersect_sphere_unit_direction(ray, earth_center, radius, distances); + intersections[0].distance = distances[0]; + intersections[1].distance = distances[1]; + intersections[1].involved_shell = this; + return n_distances; +} + +void shell::densities() +{ + const float previous_radius = shell::find_radius(index-1); + const float shell_center_height = (radius + previous_radius) / 2.0f; + rayleigh_density = get_rayleigh_density(shell_center_height); + mie_density = get_mie_density(shell_center_height); + ozone_density = get_ozone_density(shell_center_height); +} + +float shell::find_radius(int shell_index) +{ + if (shell_index < 0) { + return earth_radius; + } + const float scale = rayleigh_scale * 2; + float x = static_cast(shell_index) / static_cast(shell::n_atmosphere_shells - 1); + const float a = expf(-(atmosphere_radius - earth_radius) / scale) - 1.0f; + return earth_radius - scale * logf(a * x + 1.0f); +} + +float shell::find_index(float shell_radius) +{ + for (int i = 0; i < shell::n_atmosphere_shells; i++) { + float radius_s1 = shell::atmosphere_shells[i].radius; + float radius_s2 = shell::atmosphere_shells[i + 1].radius; + float dist_s1 = radius_s1 - shell_radius; + float dist_s2 = radius_s2 - shell_radius; + if (dist_s1 <= 0 && dist_s2 > 0) { + return static_cast(i) + ((shell_radius - radius_s1) / (radius_s2 - radius_s1)); + } + } + return shell::n_atmosphere_shells; +} + +int shell::find_intersections(const Ray3f& ray, shell::intersection intersections[]) { + int intersct_i = 0; + for (int k = 0; k < shell::n_atmosphere_shells; k++) { + shell *kth_shell = &shell::atmosphere_shells[k]; + + if (kth_shell->ray_in_shell(ray)) { + intersections[intersct_i] = kth_shell->intersection_distance_inside(ray); + intersct_i++; + } + else { + shell::intersection ray_intersections[2]; + size_t n_intersections = kth_shell->intersection_distances_outside(ray, ray_intersections); + if (n_intersections >= 1) { + ray_intersections[0].involved_shell = &shell::atmosphere_shells[k + 1]; + intersections[intersct_i] = ray_intersections[0]; + intersct_i++; + } + if (n_intersections == 2) { + intersections[intersct_i] = ray_intersections[1]; + intersct_i++; + } + } + } + + std::sort(intersections, intersections + intersct_i, shell::intersection::sort_by_distance); + return intersct_i; +} + +shell::intersection::intersection() : distance(0) {} + +shell::intersection::intersection(float d, const shell* s) : distance(d), involved_shell(s) {} + +bool shell::intersection::sort_by_distance(const shell::intersection& i, const shell::intersection& j) +{ + return i.distance < j.distance; +} diff --git a/src/appleseed/renderer/modeling/environmentedf/atmosphereshell.h b/src/appleseed/renderer/modeling/environmentedf/atmosphereshell.h new file mode 100644 index 0000000000..00cede0ced --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/atmosphereshell.h @@ -0,0 +1,129 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +#include "foundation/math/vector.h" +#include "foundation/math/ray.h" + +#include + +using namespace foundation; + +// Forward declarations. +struct intersection; + +const float earth_radius = 6378137.0f; // radius of Earth (m) according to nssdc.gsfc.nasa.gov +const float atmosphere_thickness = 60000.0f; // rough height of Mesosphere according to nasa.gov +const float atmosphere_radius = earth_radius + atmosphere_thickness; // radius of atmosphere (m) +const Vector3f earth_center(0.0f, 0.0f, 0.0f); // central point of the earth (m) + +const float rayleigh_scale = 7994.0f; // rayleigh scale height H0 (m) +const float mie_scale = 1200.0f; // mie scale height H0 (m) + +const float ozone_ground = 0.4f; // total amount of ozone on the ground +const float ozone_start = 10000.0f; // height at beginning of ozone layer in atmosphere (m) +const float ozone_peak = 32000.0f; // height at peak density of ozone in atmosphere (m) + +// Density of rayleigh particles at height (m) above the earths surface. +float get_rayleigh_density(float height); + +// Density of mie particles at height (m) above the earths surface. +float get_mie_density(float height); + +// Density of ozone particles (m) above the earths surface. +// Source: https://doi.org/10.1145/584458.584482 +float get_ozone_density(float height); + +// +// Class representing a shell around the earth with constant rayleigh/mie particle density. +// +class shell { + +public: + + static const int n_atmosphere_shells = 64; // number of atmospheric shells around the earth + static shell atmosphere_shells[n_atmosphere_shells + 1]; // precomputed atmospheric shells with constant particle density + + // + // Intersection of ray with a shell after a distance (m). + // + struct intersection { + + float distance; // distance before ray hits shell + const shell* involved_shell; // pointer to shell that was hit + + intersection(); + + intersection(float d, const shell* s); + + // Comparison function used to sort intersections with increasing distance. + static bool sort_by_distance(const intersection& i, const intersection& j); + }; + + int index; // shell index between [0, n_shells) + float radius; // radius (m) of shell around earth + float rayleigh_density; // constant density of aerosol particles withing shell + float mie_density; // constant density of dust particles within shell + float ozone_density; // constant density of ozone particles within shell + + shell(); + + // Instanciate a new shell at index i of n_shells. + shell(int i); + + // Predefine all shell values. + shell(int i, float r, float rd, float md, float od); + + // Determines whether a given light ray origins inside of the shell. + bool ray_in_shell(const Ray3f& ray) const; + + // Determines intersection from ray within a shell. + shell::intersection intersection_distance_inside(const Ray3f& ray) const; + + // Determines 0, 1 or 2 intersections from ray outside of a shell. + size_t intersection_distances_outside(const Ray3f& ray, shell::intersection intersections[2]) const; + + // Determine height (m) of shell. + static float find_radius(int shell_index); + + // Returns index of shell that matches best. A return value of 4.2 suggest that the distance to shell 4 is 20% and shell 5 is 80%. + static float find_index(float shell_radius); + + // Finds the intersection distances between the ray and each atmospheric shell. + // Returns the number N of intersections found, stores intersection objects in the intersections-array from position 0 to position N-1. + static int find_intersections(const Ray3f& ray, shell::intersection intersections[]); + +private: + + // Finds rayleigh and mie density for this shell. + void densities(); + +}; + + diff --git a/src/appleseed/renderer/modeling/environmentedf/environmentedffactoryregistrar.cpp b/src/appleseed/renderer/modeling/environmentedf/environmentedffactoryregistrar.cpp index 1a99c9fb6d..1acb6c08be 100644 --- a/src/appleseed/renderer/modeling/environmentedf/environmentedffactoryregistrar.cpp +++ b/src/appleseed/renderer/modeling/environmentedf/environmentedffactoryregistrar.cpp @@ -41,6 +41,7 @@ #include "renderer/modeling/environmentedf/mirrorballmapenvironmentedf.h" #include "renderer/modeling/environmentedf/oslenvironmentedf.h" #include "renderer/modeling/environmentedf/preethamenvironmentedf.h" +#include "renderer/modeling/environmentedf/nishita93environmentedf.h" // appleseed.foundation headers. #include "foundation/memory/autoreleaseptr.h" @@ -73,6 +74,7 @@ EnvironmentEDFFactoryRegistrar::EnvironmentEDFFactoryRegistrar(const SearchPaths impl->register_factory(auto_release_ptr(new MirrorBallMapEnvironmentEDFFactory())); impl->register_factory(auto_release_ptr(new OSLEnvironmentEDFFactory())); impl->register_factory(auto_release_ptr(new PreethamEnvironmentEDFFactory())); + impl->register_factory(auto_release_ptr(new Nishita93EnvironmentEDFFactory())); } EnvironmentEDFFactoryRegistrar::~EnvironmentEDFFactoryRegistrar() diff --git a/src/appleseed/renderer/modeling/environmentedf/nishita93environmentedf.cpp b/src/appleseed/renderer/modeling/environmentedf/nishita93environmentedf.cpp new file mode 100644 index 0000000000..6bf67e4496 --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/nishita93environmentedf.cpp @@ -0,0 +1,494 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "nishita93environmentedf.h" + +// appleseed.renderer headers. +#include "renderer/modeling/color/colorspace.h" +#include "renderer/modeling/environmentedf/environmentedf.h" + +// appleseed.foundation headers. +#include "foundation/math/ray.h" +#include "foundation/math/sampling/mappings.h" +#include "foundation/utility/api/specializedapiarrays.h" + +// Standard headers. +#include +#include + +#include "physicalsky.h" + +// Forward declarations. +namespace foundation { class IAbortSwitch; } +namespace renderer { class Project; } + +using namespace foundation; + +namespace renderer +{ + + namespace + { + + // + // An environment EDF implementing the Nishita93 day sky model. + // + // Reference: + // + // http://nishitalab.org/user/nis/cdrom/sig93_nis.pdf + // + + const char* Model = "nishita93_environment_edf"; + + class Nishita93EnvironmentEDF + : public EnvironmentEDF + { + public: + Nishita93EnvironmentEDF( + const char* name, + const ParamArray& params) + : EnvironmentEDF(name, params) + { + m_inputs.declare("sun_theta", InputFormat::Float, "45.0"); + m_inputs.declare("sun_phi", InputFormat::Float, "0.0"); + m_inputs.declare("sun_intensity_multiplier", InputFormat::Float, "1.0"); + m_inputs.declare("elevation", InputFormat::Float, "0.0"); + m_inputs.declare("air_molecule_density", InputFormat::Float, "1.0"); + m_inputs.declare("dust_molecule_density", InputFormat::Float, "1.0"); + m_inputs.declare("ozone_molecule_density", InputFormat::Float, "1.0"); + m_inputs.declare("haze", InputFormat::Float, "0.8"); + m_inputs.declare("horizon_shift", InputFormat::Float, "0.0"); + m_inputs.declare("sun_angular_diameter", InputFormat::Float, "0.545"); + } + + void release() override + { + delete this; + } + + const char* get_model() const override + { + return Model; + } + + bool on_frame_begin( + const Project& project, + const BaseGroup* parent, + OnFrameBeginRecorder& recorder, + IAbortSwitch* abort_switch) override + { + if (!EnvironmentEDF::on_frame_begin(project, parent, recorder, abort_switch)) + return false; + + // Evaluate uniform values. + m_inputs.evaluate_uniforms(&m_uniform_values); + + // Set user input values from user interface. + m_sun_theta = deg_to_rad(m_uniform_values.m_sun_theta); + m_sun_phi = deg_to_rad(m_uniform_values.m_sun_phi); + m_sun_intensity_multiplier = m_uniform_values.m_sun_intensity_multiplier; + m_elevation = m_uniform_values.m_elevation; + m_air_molecule_density = m_uniform_values.m_air_molecule_density; + m_dust_molecule_density = m_uniform_values.m_dust_molecule_density; + m_ozone_molecule_density = m_uniform_values.m_ozone_molecule_density; + m_haze = m_uniform_values.m_haze; + m_sun_angular_diameter = deg_to_rad(m_uniform_values.m_sun_angular_diameter); + m_precompute = m_params.get_optional("precompute", true); + + // Compute the sun direction. + sun_dir = Vector3f::make_unit_vector(m_sun_theta, m_sun_phi); + + // Precompute nishitas lookup table for optical depths. + sky_precomputations(); + + return true; + } + + void sample( + const ShadingContext& shading_context, + const Vector2f& s, + Vector3f& outgoing, + Spectrum& value, + float& probability) const override + { + const Vector3f local_outgoing = sample_hemisphere_cosine(s); + + Transformd scratch; + const Transformd& transform = m_transform_sequence.evaluate(0.0f, scratch); + outgoing = transform.vector_to_parent(local_outgoing); + const Vector3f shifted_outgoing = shift(local_outgoing); + + RegularSpectrum31f radiance; + compute_sky_radiance(shading_context, shifted_outgoing, radiance); + + value.set(radiance, g_std_lighting_conditions, Spectrum::Illuminance); + probability = shifted_outgoing.y > 0.0f ? shifted_outgoing.y * RcpPi() : 0.0f; + assert(probability >= 0.0f); + } + + void evaluate( + const ShadingContext& shading_context, + const Vector3f& outgoing, + Spectrum& value) const override + { + assert(is_normalized(outgoing)); + + Transformd scratch; + const Transformd& transform = m_transform_sequence.evaluate(0.0f, scratch); + const Vector3f local_outgoing = transform.vector_to_local(outgoing); + const Vector3f shifted_outgoing = shift(local_outgoing); + + RegularSpectrum31f radiance; + compute_sky_radiance(shading_context, shifted_outgoing, radiance); + + value.set(radiance, g_std_lighting_conditions, Spectrum::Illuminance); + } + + void evaluate( + const ShadingContext& shading_context, + const Vector3f& outgoing, + Spectrum& value, + float& probability) const override + { + assert(is_normalized(outgoing)); + + Transformd scratch; + const Transformd& transform = m_transform_sequence.evaluate(0.0f, scratch); + const Vector3f local_outgoing = transform.vector_to_local(outgoing); + const Vector3f shifted_outgoing = shift(local_outgoing); + + RegularSpectrum31f radiance; + compute_sky_radiance(shading_context, shifted_outgoing, radiance); + + value.set(radiance, g_std_lighting_conditions, Spectrum::Illuminance); + probability = shifted_outgoing.y > 0.0f ? shifted_outgoing.y * RcpPi() : 0.0f; + assert(probability >= 0.0f); + } + + float evaluate_pdf( + const Vector3f& outgoing) const override + { + assert(is_normalized(outgoing)); + + Transformd scratch; + const Transformd& transform = m_transform_sequence.evaluate(0.0f, scratch); + const Vector3f local_outgoing = transform.vector_to_local(outgoing); + const Vector3f shifted_outgoing = shift(local_outgoing); + + const float probability = shifted_outgoing.y > 0.0f ? shifted_outgoing.y * RcpPi() : 0.0f; + assert(probability >= 0.0f); + + return probability; + } + + private: + APPLESEED_DECLARE_INPUT_VALUES(InputValues) + { + float m_sun_theta; // angle (deg) between sun and zenith, 0=zenith + float m_sun_phi; // angle (deg) between sun and north, 0=north + float m_sun_intensity_multiplier; // increases or decreases the returned radiance values + float m_elevation; // elevation of camera above earth surface (m) + float m_air_molecule_density; // multiplier of air molecule density, affecting rayleigh scattering + float m_dust_molecule_density; // multiplier of dust molecule density, affecting mie scattering + float m_ozone_molecule_density; // multiplier of ozone molecule density, affecting ozone scattering + float m_haze; // u parameter for Cornette phase function used for Mie scattering + float m_horizon_shift; + float m_sun_angular_diameter; // rays with angle (rad) +- sun_angular_diameter will return sun radiance directly + }; + + InputValues m_uniform_values; + + float m_sun_theta; // angle (rad) between sun and zenith, 0=zenith + float m_sun_phi; // angle (rad) between sun and north, 0=north + Vector3f sun_dir; // vector pointing into the direction of the sun + + float m_sun_intensity_multiplier; // increases or decreases the returned radiance values + float m_elevation; // elevation of camera above earth surface (m) + float m_air_molecule_density; // multiplier of air molecule density, affecting rayleigh scattering + float m_dust_molecule_density; // multiplier of dust molecule density, affecting mie scattering + float m_ozone_molecule_density; // multiplier of ozone molecule density, affecting ozone scattering + float m_haze; // u parameter for Cornette phase function used for Mie scattering + float m_sun_angular_diameter; // rays with angle (rad) +- sun_angular_diameter will return sun radiance directly + bool m_precompute; // use 2D lookup table to precompute sky optical depths + + // Fills a 3D precomputation table with optical depths value along the sun direction. + void sky_precomputations() { + nishita::precompute_mie_g(m_haze); + nishita::precompute_shells(); + if (m_precompute) + nishita::precompute_optical_depths(sun_dir, m_air_molecule_density, m_dust_molecule_density, m_ozone_molecule_density); + } + + // Compute the sky radiance along a given direction. + void compute_sky_radiance( + const ShadingContext& shading_context, + const Vector3f& outgoing, + RegularSpectrum31f& radiance) const + { + + // Position of the camera at least 1.2 meters above earth radius to avoid numerical errors. + Vector3f camera_position = Vector3f(0.0f, earth_radius + 1.2f + m_elevation, 0.0f); + Ray3f ray = Ray3f(camera_position, outgoing); + + // If the outoing vector points to the sun, return suns spectrum. + float sun_angular_radius = m_sun_angular_diameter / 2.0f; + float is_sun = norm(outgoing - sun_dir) < sun_angular_radius; + if (is_sun) { + const bool sun_hit = nishita::sun_disk( + ray, + m_air_molecule_density, // air molecule density (Rayleigh scattering) + m_dust_molecule_density, // dust molecule density (Mie scattering) + m_ozone_molecule_density, // ozone molecule density (Ozone scattering) + sun_angular_radius, + radiance + ); + if (sun_hit) + return; + } + + // Compute the final sky radiance. + nishita::single_scattering( + ray, + sun_dir, // sun direction + m_air_molecule_density, // air molecule density (Rayleigh scattering) + m_dust_molecule_density, // dust molecule density (Mie scattering) + m_ozone_molecule_density, // ozone molecule density (Ozone scattering) + m_precompute, // use precomputed lookup table + radiance + ); + radiance *= + m_uniform_values.m_sun_intensity_multiplier // multiply sun intensity + * 1.5f; // since nishita93 underestimates radiance by 1/3 according to Bruneton + } + + Vector3f shift(Vector3f v) const + { + v.y -= m_uniform_values.m_horizon_shift; + return normalize(v); + } + }; + } + + + // + // Nishita93EnvironmentEDFFactory class implementation. + // + void Nishita93EnvironmentEDFFactory::release() + { + delete this; + } + + const char* Nishita93EnvironmentEDFFactory::get_model() const + { + return Model; + } + + Dictionary Nishita93EnvironmentEDFFactory::get_model_metadata() const + { + return + Dictionary() + .insert("name", Model) + .insert("label", "Nishita93 Environment EDF") + .insert("help", "Physical sky with single scattering environment"); + } + + DictionaryArray Nishita93EnvironmentEDFFactory::get_input_metadata() const + { + DictionaryArray metadata; + + metadata.push_back( + Dictionary() + .insert("name", "sun_theta") + .insert("label", "Sun Theta Angle") + .insert("type", "numeric") + .insert("min", + Dictionary() + .insert("value", "0.0") + .insert("type", "hard")) + .insert("max", + Dictionary() + .insert("value", "100.0") + .insert("type", "hard")) + .insert("use", "required") + .insert("default", "45.0") + .insert("help", "Sun polar (vertical) angle in degrees")); + + metadata.push_back( + Dictionary() + .insert("name", "sun_phi") + .insert("label", "Sun Phi Angle") + .insert("type", "numeric") + .insert("min", + Dictionary() + .insert("value", "-360.0") + .insert("type", "soft")) + .insert("max", + Dictionary() + .insert("value", "360.0") + .insert("type", "soft")) + .insert("use", "required") + .insert("default", "0.0") + .insert("help", "Sun azimuthal (horizontal) angle in degrees")); + + metadata.push_back( + Dictionary() + .insert("name", "sun_intensity_multiplier") + .insert("label", "Sun intensity multiplier") + .insert("type", "numeric") + .insert("min", + Dictionary() + .insert("value", "0.0") + .insert("type", "hard")) + .insert("max", + Dictionary() + .insert("value", "10.0") + .insert("type", "soft")) + .insert("use", "optional") + .insert("default", "1.0") + .insert("help", "Multiplies sun intensity with constant factor")); + + metadata.push_back( + Dictionary() + .insert("name", "air_molecule_density") + .insert("label", "Air Molecule Density") + .insert("type", "numeric") + .insert("min", + Dictionary() + .insert("value", "0.0") + .insert("type", "hard")) + .insert("max", + Dictionary() + .insert("value", "5.0") + .insert("type", "hard")) + .insert("use", "optional") + .insert("default", "1.0") + .insert("help", "Air molecule density affect Rayleigh scattering (blueness of sky)")); + + metadata.push_back( + Dictionary() + .insert("name", "dust_molecule_density") + .insert("label", "Dust Molecule Density") + .insert("type", "numeric") + .insert("min", + Dictionary() + .insert("value", "0.0") + .insert("type", "hard")) + .insert("max", + Dictionary() + .insert("value", "5.0") + .insert("type", "hard")) + .insert("use", "optional") + .insert("default", "1.0") + .insert("help", "Dust molecule density affect Mie scattering (turbidity)")); + + metadata.push_back( + Dictionary() + .insert("name", "ozone_molecule_density") + .insert("label", "Ozone Molecule Density") + .insert("type", "numeric") + .insert("min", + Dictionary() + .insert("value", "0.0") + .insert("type", "hard")) + .insert("max", + Dictionary() + .insert("value", "5.0") + .insert("type", "hard")) + .insert("use", "optional") + .insert("default", "1.0") + .insert("help", "Ozone molecules effect the sky color at twilight")); + + metadata.push_back( + Dictionary() + .insert("name", "haze") + .insert("label", "Haze in the atmosphere") + .insert("type", "numeric") + .insert("min", + Dictionary() + .insert("value", "0.7") + .insert("type", "hard")) + .insert("max", + Dictionary() + .insert("value", "0.8") + .insert("type", "hard")) + .insert("use", "optional") + .insert("default", "0.8") + .insert("help", "Haze changes effect of dust particles on sunlight")); + + metadata.push_back( + Dictionary() + .insert("name", "elevation") + .insert("label", "Elevation") + .insert("type", "text") + .insert("use", "optional") + .insert("default", "0") + .insert("help", "Elevates camera above earths surface")); + + metadata.push_back( + Dictionary() + .insert("name", "sun_angular_diameter") + .insert("label", "Sun Size") + .insert("type", "text") + .insert("use", "optional") + .insert("default", "0.545") + .insert("help", "Angular diameter of sun disk, make 0 to hide sun")); + + metadata.push_back( + Dictionary() + .insert("name", "horizon_shift") + .insert("label", "Horizon Shift") + .insert("type", "text") + .insert("use", "optional") + .insert("default", "0.0") + .insert("help", "Shift the horizon vertically")); + + metadata.push_back( + Dictionary() + .insert("name", "precompute") + .insert("label", "Precalculate sky") + .insert("type", "boolean") + .insert("use", "optional") + .insert("default", "true") + .insert("help", "If enabled, precalculations make sky fast but slightly less accurate")); + + add_common_input_metadata(metadata); + + return metadata; + } + + auto_release_ptr Nishita93EnvironmentEDFFactory::create( + const char* name, + const ParamArray& params) const + { + return + auto_release_ptr( + new Nishita93EnvironmentEDF(name, params)); + } + +} diff --git a/src/appleseed/renderer/modeling/environmentedf/nishita93environmentedf.h b/src/appleseed/renderer/modeling/environmentedf/nishita93environmentedf.h new file mode 100644 index 0000000000..751b1e01a9 --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/nishita93environmentedf.h @@ -0,0 +1,76 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +// appleseed.renderer headers. +#include "renderer/modeling/environmentedf/ienvironmentedffactory.h" + +// appleseed.foundation headers. +#include "foundation/memory/autoreleaseptr.h" +#include "foundation/platform/compiler.h" + +// appleseed.main headers. +#include "main/dllsymbol.h" + +// Forward declarations. +namespace foundation { class Dictionary; } +namespace foundation { class DictionaryArray; } +namespace renderer { class EnvironmentEDF; } +namespace renderer { class ParamArray; } + +namespace renderer +{ + + // + // An environment EDF implementing the Nishita93 day sky model. + // + + class APPLESEED_DLLSYMBOL Nishita93EnvironmentEDFFactory + : public IEnvironmentEDFFactory + { + public: + // Delete this instance. + void release() override; + + // Return a string identifying this environment EDF model. + const char* get_model() const override; + + // Return metadata for this environment EDF model. + foundation::Dictionary get_model_metadata() const override; + + // Return metadata for the inputs of this environment EDF model. + foundation::DictionaryArray get_input_metadata() const override; + + // Create a new environment EDF instance. + foundation::auto_release_ptr create( + const char* name, + const ParamArray& params) const override; + }; + +} diff --git a/src/appleseed/renderer/modeling/environmentedf/opticaldepth.cpp b/src/appleseed/renderer/modeling/environmentedf/opticaldepth.cpp new file mode 100644 index 0000000000..149e2f88e4 --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/opticaldepth.cpp @@ -0,0 +1,52 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "opticaldepth.h" + +sky::opticaldepth::opticaldepth() { + sky::opticaldepth(0.0f, 0.0f, 0.0f); +} + +sky::opticaldepth::opticaldepth(float r, float m, float o) { + sky::opticaldepth(r, m, o, 1.0f, 1.0f, 1.0f); +} + +sky::opticaldepth::opticaldepth(float r, float m, float o, float ad, float dd, float od) : + rayleigh(r), + mie(m), + ozone(o), + air_particle_density(ad), + dust_particle_density(dd), + ozone_particle_density(od) { } + +void sky::opticaldepth::increase(float segment_length, float rayleigh_density, float mie_density, float ozone_density) { + rayleigh += segment_length * rayleigh_density * air_particle_density; + mie += segment_length * mie_density * dust_particle_density; + ozone += segment_length * ozone_density * ozone_particle_density; +} + diff --git a/src/appleseed/renderer/modeling/environmentedf/opticaldepth.h b/src/appleseed/renderer/modeling/environmentedf/opticaldepth.h new file mode 100644 index 0000000000..c7fc77a50b --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/opticaldepth.h @@ -0,0 +1,65 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +namespace sky { + class opticaldepth { + + public: + float rayleigh; + float mie; + float ozone; + + opticaldepth(); + opticaldepth(float r, float m, float o); + opticaldepth(float r, float m, float o, float ad, float dd, float od); + + void increase(float segment_length, float rayleigh_density, float mie_density, float ozone_density); + + inline sky::opticaldepth operator+(const sky::opticaldepth& rod) { + return sky::opticaldepth(rayleigh + rod.rayleigh, mie + rod.mie, ozone + rod.ozone, air_particle_density, dust_particle_density, ozone_particle_density); + } + + inline sky::opticaldepth operator-(const sky::opticaldepth& rod) { + return sky::opticaldepth(rayleigh - rod.rayleigh, mie - rod.mie, ozone - rod.ozone, air_particle_density, dust_particle_density, ozone_particle_density); + } + + inline sky::opticaldepth operator*(float i) { + return sky::opticaldepth(rayleigh * i, mie * i, ozone * i, air_particle_density, dust_particle_density, ozone_particle_density); + } + + protected: + float air_particle_density; + float dust_particle_density; + float ozone_particle_density; + }; + +}; + + diff --git a/src/appleseed/renderer/modeling/environmentedf/physicalsky.cpp b/src/appleseed/renderer/modeling/environmentedf/physicalsky.cpp new file mode 100644 index 0000000000..4dff1ae269 --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/physicalsky.cpp @@ -0,0 +1,265 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// appleseed.foundation headers. +#include "foundation/math/vector.h" +#include "foundation/image/regularspectrum.h" +#include "foundation/math/ray.h" +#include "foundation/math/intersection/raysphere.h" +#include "physicalsky.h" + +#include + +using namespace foundation; + +float mie_g; // mie assymetricity component g + + +void nishita::precompute_mie_g(float haze) { + mie_g = nishita::mie_assymetricity(haze); +} + +void nishita::precompute_shells() { + for (int i = 0; i < shell::n_atmosphere_shells; i++) { + shell::atmosphere_shells[i] = shell(i); + } + // Outermost "shell" is deep space with infinite radius. + shell::atmosphere_shells[shell::n_atmosphere_shells] = shell(shell::n_atmosphere_shells, INFINITY, 0.0f, 0.0f, 0.0f); +} + +void nishita::precompute_optical_depths(const Vector3f& sun_dir, float air_particle_density, float dust_particle_density, float ozone_particle_density) { + float sqrt3 = sqrtf(3.0f); + Vector3f unit_vector = Vector3f(sqrt3); + Vector3f sun_dir_perpendicular = normalize(cross(sun_dir, unit_vector)); + + for (int n_cylinder = 0; n_cylinder < nishita::n_cylinders; n_cylinder++) { + float cylinder_radius = nishita::cylinder_delta * n_cylinder; + Ray3f cylinder_border = Ray3f(sun_dir_perpendicular * cylinder_radius, sun_dir); + for (int n_shell = 0; n_shell <= shell::n_atmosphere_shells; n_shell++) { + if (shell::atmosphere_shells[n_shell].ray_in_shell(cylinder_border)) { + shell::intersection shell_cylinder_intersection = shell::atmosphere_shells[n_shell].intersection_distance_inside(cylinder_border); + Vector3f intersection_point = cylinder_border.m_org + sun_dir * shell_cylinder_intersection.distance; + Ray3f intersection_ray = Ray3f(intersection_point, sun_dir); + + const sky::opticaldepth optical_depth = nishita::ray_optical_depth(intersection_ray, air_particle_density, dust_particle_density, ozone_particle_density); + nishita::optical_depths_table[n_shell][n_cylinder] = optical_depth; + } + else { + nishita::optical_depths_table[n_shell][n_cylinder] = nishita::optical_depths_table[n_shell][n_cylinder-1]; + } + } + } +} + +float nishita::rayleigh_phase(float angle) +{ + const float angle_squared = angle * angle; + return 3.0f / (16.0f * Pi()) * (1.0f + angle_squared); +} + +float nishita::mie_assymetricity(float u) +{ + const float x = 5.0f / 9.0f * u + 125.0f / 729.0f * powf(u, 3.0f) + + powf(64.0f / 27.0f - 325.0f / 243.0f * (u * u) + 1250.0f / 2187.0f * powf(u, 4), 1.0f / 2.0f); + return 5.0f / 9.0f * u - (4.0f / 3.0f - 25.0f / 81.0f * (u * u)) * powf(x, -1.0f / 3.0f) + powf(x, 1.0f / 3.0f); +} + +float nishita::mie_phase(float angle) +{ + const static float mie_g_sqr = (mie_g * mie_g); + return 3.0f / (8.0f * Pi()) * (1.0f - mie_g_sqr) / (2.0f + mie_g_sqr) * + (1.0f + angle * angle) / powf(1.0f + mie_g_sqr - 2.0f * mie_g * angle, 3.0f / 2.0f); +} + +bool nishita::intersects_earth(const Ray3f& ray) +{ + if (ray.m_dir.y >= 0) + return false; + return intersect_sphere_unit_direction(ray, earth_center, earth_radius); +} + +bool nishita::intersects_earth(const Ray3f& ray, float& distance) +{ + if (ray.m_dir.y >= 0) + return false; + return intersect_sphere_unit_direction(ray, earth_center, earth_radius, distance); +} + +bool nishita::ray_inside_earth(const Ray3f& ray) +{ + return (norm(ray.m_org) < earth_radius); +} + +float nishita::distance_to_atmosphere(const Ray3f& ray) { + const float radius_sqr = earth_radius * earth_radius; + float b = -2.0f * dot(ray.m_dir, -ray.m_org); + float c = square_norm(ray.m_org) - radius_sqr; + return (-b + sqrtf(b * b - 4.0f * c)) / 2.0f; +} + +sky::opticaldepth nishita::ray_optical_depth(const Ray3f& ray, float air_particle_density, float dust_particle_density, float ozone_particle_density) +{ + shell::intersection intersections[shell::n_atmosphere_shells * 2]; + int n_intersections = shell::find_intersections(ray, intersections); + float passed_distance = 0.0f; + + sky::opticaldepth optical_depth(0.0f, 0.0f, 0.0f, air_particle_density, dust_particle_density, ozone_particle_density); + + for (int i = 0; i < n_intersections; i++) { + + shell::intersection ith_intersection = intersections[i]; + float segment_length = ith_intersection.distance - passed_distance; + + optical_depth.increase( + segment_length, + ith_intersection.involved_shell->rayleigh_density, + ith_intersection.involved_shell->mie_density, + ith_intersection.involved_shell->ozone_density + ); + + passed_distance = ith_intersection.distance; + } + return optical_depth; +} + +sky::opticaldepth nishita::lookup_optical_depth(const Ray3f& ray) { + Vector3f sun_dir = ray.m_dir; + float radius = sqrtf(square_distance_point_line(ray.m_org, earth_center, sun_dir)); + + float cylinder_index_raw = radius / nishita::cylinder_delta; + int cylinder_index = static_cast(cylinder_index_raw); + float second_cylinder_dominance = cylinder_index_raw - static_cast(cylinder_index); + float first_cylinder_dominance = 1.0f - second_cylinder_dominance; + + float shell_index_raw = shell::find_index(norm(ray.m_org)); + int shell_index = static_cast(shell_index_raw); + float second_shell_dominance = shell_index_raw - static_cast(shell_index); + float first_shell_dominance = 1.0f - second_shell_dominance; + + sky::opticaldepth avg_cylinder_depths_1 = nishita::optical_depths_table[shell_index][cylinder_index] * first_cylinder_dominance + + nishita::optical_depths_table[shell_index][cylinder_index + 1] * second_cylinder_dominance; + sky::opticaldepth avg_cylinder_depths_2 = nishita::optical_depths_table[shell_index + 1][cylinder_index] * first_cylinder_dominance + + nishita::optical_depths_table[shell_index + 1][cylinder_index + 1] * second_cylinder_dominance; + sky::opticaldepth looked_up_depth = avg_cylinder_depths_1 * first_shell_dominance + avg_cylinder_depths_2 * second_shell_dominance; + return looked_up_depth; +} + +void nishita::single_scattering( + const Ray3f& ray, + const Vector3f& sun_dir, + float air_particle_density, + float dust_particle_density, + float ozone_particle_density, + bool is_precomputed, + RegularSpectrum31f& spectrum) +{ + spectrum.set(0.0f); + + float distance_to_earth_intersection = 0.0f; + bool earth_intersection = intersects_earth(ray, distance_to_earth_intersection); + + shell::intersection intersections[shell::n_atmosphere_shells * 2]; + int n_intersections = shell::find_intersections(ray, intersections); + + sky::opticaldepth optical_depth(0.0f, 0.0f, 0.0f, air_particle_density, dust_particle_density, ozone_particle_density); + + float angle = dot(ray.m_dir, sun_dir); + float rayleigh_phase_function = rayleigh_phase(angle); + float mie_phase_function = mie_phase(angle); + + float passed_distance = 0; + + for (int i = 0; i < n_intersections; i++) { + + shell::intersection ith_intersection = intersections[i]; + float segment_length = ith_intersection.distance - passed_distance; + passed_distance = ith_intersection.distance; + float half_segment_length = segment_length / 2.0f; + + float distance_to_scatterpoint = (ith_intersection.distance - half_segment_length); + + Vector3f segment_middle_point = ray.m_org + (ray.m_dir * distance_to_scatterpoint); + Ray3f scatter_ray = Ray3f(segment_middle_point, sun_dir); + + if (earth_intersection && ((distance_to_scatterpoint > distance_to_earth_intersection) || intersects_earth(scatter_ray))) + break; + + float rayleigh_density = ith_intersection.involved_shell->rayleigh_density; + float mie_density = ith_intersection.involved_shell->mie_density; + float ozone_density = ith_intersection.involved_shell->ozone_density; + + optical_depth.increase(segment_length, rayleigh_density, mie_density, ozone_density); + + sky::opticaldepth ligh_optical_depth; + + if (is_precomputed) + ligh_optical_depth = lookup_optical_depth(scatter_ray); + else + ligh_optical_depth = ray_optical_depth(scatter_ray, air_particle_density, dust_particle_density, ozone_particle_density); + + sky::opticaldepth total_optical_depth = optical_depth + ligh_optical_depth; + const RegularSpectrum31f total_extinction_density = rayleigh_coeff_spectrum * total_optical_depth.rayleigh + + mie_coeff_spectrum * total_optical_depth.mie + + ozone_coeff_spectrum * total_optical_depth.ozone; + + float attenuations[num_wavelengths]; + for (int wl = 0; wl < num_wavelengths; wl++) { attenuations[wl] = expf(-total_extinction_density[wl]); } + const RegularSpectrum31f attenuation = RegularSpectrum31f::from_array(attenuations); + + const RegularSpectrum31f total_reduction = rayleigh_phase_function * rayleigh_density * rayleigh_coeff_spectrum + mie_phase_function * mie_density * mie_coeff_spectrum; + spectrum += attenuation * total_reduction * nishita::sun_radiance_spectrum * segment_length; + } +} + + +bool nishita::sun_disk( + const Ray3f& ray, + float air_particle_density, + float dust_particle_density, + float ozone_particle_density, + float sun_radius, + RegularSpectrum31f& spectrum) +{ + spectrum.set(0.0f); + if (intersects_earth(ray)) { + return false; + } + sky::opticaldepth optical_depth = ray_optical_depth(ray, air_particle_density, dust_particle_density, ozone_particle_density); + float solid_angle = Pi() * (1.0f - cosf(sun_radius)); + + const RegularSpectrum31f rayleigh_transmittance = rayleigh_coeff_spectrum * optical_depth.rayleigh * air_particle_density; + const RegularSpectrum31f mie_transmittance = mie_coeff_spectrum * optical_depth.mie * dust_particle_density; + const RegularSpectrum31f ozone_transmittance = ozone_coeff_spectrum * optical_depth.ozone * ozone_particle_density; + const RegularSpectrum31f total_transmittance = rayleigh_transmittance + mie_transmittance + ozone_transmittance; + + float attenuations[num_wavelengths]; + for (int wl = 0; wl < num_wavelengths; wl++) { attenuations[wl] = expf(-total_transmittance[wl]); } + + spectrum = nishita::sun_radiance_spectrum / solid_angle * RegularSpectrum31f::from_array(attenuations); + return true; +} diff --git a/src/appleseed/renderer/modeling/environmentedf/physicalsky.h b/src/appleseed/renderer/modeling/environmentedf/physicalsky.h new file mode 100644 index 0000000000..7feb3cb908 --- /dev/null +++ b/src/appleseed/renderer/modeling/environmentedf/physicalsky.h @@ -0,0 +1,222 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2020 Joel Barmettler, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +#include "foundation/math/vector.h" +#include "foundation/image/regularspectrum.h" +#include "foundation/math/ray.h" +#include "foundation/math/distance.h" + +#include "atmosphereshell.h" +#include "opticaldepth.h" + +using namespace foundation; + +namespace nishita { + + static const int num_wavelengths = 31; // number of wavelengths per spectrum (400nm to 700nm, delta=10nm) + static const int n_cylinders = 1024; // number of cylinders for optical depth precomputation + + // Lookup table storing optical dephts into the direction of the sun. + static sky::opticaldepth optical_depths_table[shell::n_atmosphere_shells + 1][n_cylinders]; + + // Width of a single cylinder. Sum of all cylinder widths must be marginally smaller than the atmosphere radius. + static const float cylinder_delta = (atmosphere_radius - n_cylinders) / (n_cylinders - 1); + + // Mie scattering coefficient and regular spectrum of mie coefficients. + const float mie_extinction_coeff = 2e-5f; + const RegularSpectrum31f mie_coeff_spectrum = RegularSpectrum31f(mie_extinction_coeff); + + // Sun irradiance (W*m^-2*nm^-1) values at the top of the atmosphere. + // Source: https://www.nrel.gov/grid/solar-resource/spectra.html, Table SMART MODTRAN ETR Spectra. + const float sun_radiance[num_wavelengths] = { + 2.11275f, // 400nm + 2.58882f, // 410nm + 2.58291f, // 420nm + 2.42323f, // 430nm + 2.67605f, // 440nm + 2.96583f, // 450nm + 3.05454f, // 460nm + 3.00575f, // 470nm + 3.06637f, // 480nm + 2.88304f, // 490nm + 2.87121f, // 500nm + 2.78250f, // 510nm + 2.71006f, // 520nm + 2.72336f, // 530nm + 2.63613f, // 540nm + 2.55038f, // 550nm + 2.50602f, // 560nm + 2.53116f, // 570nm + 2.53559f, // 580nm + 2.51342f, // 590nm + 2.46315f, // 600nm + 2.41732f, // 610nm + 2.36853f, // 620nm + 2.32121f, // 630nm + 2.28277f, // 640nm + 2.23398f, // 650nm + 2.19702f, // 650nm + 2.15267f, // 670nm + 2.10979f, // 680nm + 2.07283f, // 690nm + 2.02404f // 700nm + }; + const RegularSpectrum31f sun_radiance_spectrum = RegularSpectrum31f::from_array(sun_radiance); + + // Rayleigh scattering coefficients (m^-1) from Rudolf Penndorf (1957) Table 3. + // Source: https://doi.org/10.1364/JOSA.47.000176 + const float rayleigh_coeff[num_wavelengths] = { + 45.40e-6f, // 400nm + 40.98e-6f, // 410nm + 37.08e-6f, // 420nm + 33.65e-6f, // 430nm + 30.60e-6f, // 440nm + 27.89e-6f, // 450nm + 25.48e-6f, // 460nm + 23.33e-6f, // 470nm + 21.40e-6f, // 480nm + 19.66e-6f, // 490nm + 18.10e-6f, // 500nm + 16.69e-6f, // 510nm + 15.42e-6f, // 520nm + 14.26e-6f, // 530nm + 13.21e-6f, // 540nm + 12.26e-6f, // 550nm + 11.39e-6f, // 560nm + 10.60e-6f, // 570nm + 9.876e-6f, // 580nm + 9.212e-6f, // 590nm + 8.604e-6f, // 600nm + 8.045e-6f, // 610nm + 7.531e-6f, // 620nm + 7.057e-6f, // 630nm + 6.620e-6f, // 640nm + 6.217e-6f, // 650nm + 5.844e-6f, // 660nm + 5.498e-6f, // 670nm + 5.178e-6f, // 680nm + 4.881e-6f, // 690nm + 4.605e-6f, // 700nm + }; + const RegularSpectrum31f rayleigh_coeff_spectrum = RegularSpectrum31f::from_array(rayleigh_coeff); + + // Ozona absorption coefficient (m^-1). + // Source: https://www.iup.uni-bremen.de/gruppen/molspec/databases/referencespectra/o3spectra2011/index.html + const float ozone_coeff[num_wavelengths] = { + 3.804511196879277e-09f, // 400 nm + 6.913786897105462e-09f, // 410 nm + 1.3852765960014552e-08f, // 420 nm + 2.1308603627919998e-08f, // 430 nm + 3.974417614472733e-08f, // 440 nm + 5.779591314894535e-08f, // 450 nm + 9.191587335498181e-08f, // 460 nm + 1.2363721551643633e-07f, // 470 nm + 1.9505027060647285e-07f, // 480 nm + 2.2672051905767247e-07f, // 490 nm + 3.716605995280002e-07f, // 500 nm + 4.0267814468581854e-07f, // 510 nm + 5.364069922247275e-07f, // 520 nm + 6.912136535745463e-07f, // 530 nm + 7.745488102370914e-07f, // 540 nm + 8.772119777709093e-07f, // 550 nm + 1.0680234682312722e-06f, // 560 nm + 1.1695343279723626e-06f, // 570 nm + 1.1011384812494534e-06f, // 580 nm + 1.1759623019832746e-06f, // 590 nm + 1.2552240270210935e-06f, // 600 nm + 1.0772983295309093e-06f, // 610 nm + 9.361428617905462e-07f, // 620 nm + 8.052237676756349e-07f, // 630 nm + 6.675936847221821e-07f, // 640 nm + 5.619235334727269e-07f, // 650 nm + 4.6550674463418176e-07f, // 660 nm + 3.7068568738763686e-07f, // 670 nm + 3.0466838275272715e-07f, // 680 nm + 2.3788813137578206e-07f, // 690 nm + 1.8836707145585476e-07f, // 700 nm + }; + const RegularSpectrum31f ozone_coeff_spectrum = RegularSpectrum31f::from_array(ozone_coeff); + + // Precomputes g parameter determining Mie assymetricity depending on atmospheric haze condition. + void precompute_mie_g(float haze); + + // Precomputes shell values with exponentially decreasing radius. + void precompute_shells(); + + // Precomputes optical dephts using n cylinders. + void precompute_optical_depths(const Vector3f& sun_dir, float air_particle_density, float dust_particle_density, float ozone_particle_density); + + // Mie assymetricity value depending on atmospheric haze condition u, varies from 0.7 tp 0.85. + inline float mie_assymetricity(float u); + + // Rayleigh phase function for a given angle (rad). + float rayleigh_phase(float angle); + + // Mie phase function for a given angle (rad). + float mie_phase(float angle); + + // Determines whether the light ray intersects with the earths surface. + bool intersects_earth(const Ray3f& ray); + bool intersects_earth(const Ray3f& ray, float& distance); + + // Determines whether a ray is below the earths surface. + bool ray_inside_earth(const Ray3f& ray); + + // Determines the distance a ray travels before hitting the outer point of the atmosphere. + float distance_to_atmosphere(const Ray3f& ray); + + // Computes optical depth along a ray considering mie and rayleigh scattering. + sky::opticaldepth ray_optical_depth(const Ray3f& ray, float air_particle_density, float dust_particle_density, float ozone_particle_density); + + // Finds best fitting optical depth from lookup table. + sky::opticaldepth lookup_optical_depth(const Ray3f& ray); + + // Computes the irradiance spectrum of a single ray through the atmosphere, considering rayleigh and mie scattering. + void single_scattering( + const Ray3f& ray, + const Vector3f& sun_dir, + float air_particle_density, + float dust_particle_density, + float ozone_particle_density, + bool is_precomputed, + RegularSpectrum31f& spectrum); + + // Returns the irradiance spectrum of the sun for rays pointing directgly at the sun. + bool sun_disk( + const Ray3f& ray, + float air_particle_density, + float dust_particle_density, + float ozone_particle_density, + float sun_radius, + RegularSpectrum31f& spectrum); + + +} +