Add Chromium-only Blender WebEngine parity work
This commit is contained in:
328
blender-5.2.0/intern/sky/source/sky_hosek.cpp
Normal file
328
blender-5.2.0/intern/sky/source/sky_hosek.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/* SPDX-FileCopyrightText: 2012-2013 Lukas Hosek and Alexander Wilkie. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
/* ============================================================================
|
||||
|
||||
This file is part of a sample implementation of the analytical skylight and
|
||||
solar radiance models presented in the SIGGRAPH 2012 paper
|
||||
|
||||
|
||||
"An Analytic Model for Full Spectral Sky-Dome Radiance"
|
||||
|
||||
and the 2013 IEEE CG&A paper
|
||||
|
||||
"Adding a Solar Radiance Function to the Hosek Skylight Model"
|
||||
|
||||
both by
|
||||
|
||||
Lukas Hosek and Alexander Wilkie
|
||||
Charles University in Prague, Czech Republic
|
||||
|
||||
|
||||
Version: 1.4a, February 22nd, 2013
|
||||
|
||||
Version history:
|
||||
|
||||
1.4a February 22nd, 2013
|
||||
Removed unnecessary and counter-intuitive solar radius parameters
|
||||
from the interface of the color-space sky dome initialization functions.
|
||||
|
||||
1.4 February 11th, 2013
|
||||
Fixed a bug which caused the relative brightness of the solar disc
|
||||
and the sky dome to be off by a factor of about 6. The sun was too
|
||||
bright: this affected both normal and alien sun scenarios. The
|
||||
coefficients of the solar radiance function were changed to fix this.
|
||||
|
||||
1.3 January 21st, 2013 (not released to the public)
|
||||
Added support for solar discs that are not exactly the same size as
|
||||
the terrestrial sun. Also added support for suns with a different
|
||||
emission spectrum ("Alien World" functionality).
|
||||
|
||||
1.2a December 18th, 2012
|
||||
Fixed a mistake and some inaccuracies in the solar radiance function
|
||||
explanations found in ArHosekSkyModel.h. The actual source code is
|
||||
unchanged compared to version 1.2.
|
||||
|
||||
1.2 December 17th, 2012
|
||||
Native RGB data and a solar radiance function that matches the turbidity
|
||||
conditions were added.
|
||||
|
||||
1.1 September 2012
|
||||
The coefficients of the spectral model are now scaled so that the output
|
||||
is given in physical units: W / (m^-2 * sr * nm). Also, the output of the
|
||||
XYZ model is now no longer scaled to the range [0...1]. Instead, it is
|
||||
the result of a simple conversion from spectral data via the CIE 2 degree
|
||||
standard observer matching functions. Therefore, after multiplication
|
||||
with 683 lm / W, the Y channel now corresponds to luminance in lm.
|
||||
|
||||
1.0 May 11th, 2012
|
||||
Initial release.
|
||||
|
||||
|
||||
Please visit http://cgg.mff.cuni.cz/projects/SkylightModelling/ to check if
|
||||
an updated version of this code has been published!
|
||||
|
||||
============================================================================ */
|
||||
|
||||
/*
|
||||
|
||||
All instructions on how to use this code are in the accompanying header file.
|
||||
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_sky_modal
|
||||
*/
|
||||
|
||||
#include "sky_hosek.h"
|
||||
#include "sky_hosek_data.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
// Some macro definitions that occur elsewhere in ART, and that have to be
|
||||
// replicated to make this a stand-alone module.
|
||||
|
||||
#ifndef MATH_PI
|
||||
# define MATH_PI 3.141592653589793
|
||||
#endif
|
||||
|
||||
#ifndef MATH_DEG_TO_RAD
|
||||
# define MATH_DEG_TO_RAD (MATH_PI / 180.0)
|
||||
#endif
|
||||
|
||||
#ifndef DEGREES
|
||||
# define DEGREES *MATH_DEG_TO_RAD
|
||||
#endif
|
||||
|
||||
#ifndef TERRESTRIAL_SOLAR_RADIUS
|
||||
# define TERRESTRIAL_SOLAR_RADIUS ((0.51 DEGREES) / 2.0)
|
||||
#endif
|
||||
|
||||
#ifndef ALLOC
|
||||
# define ALLOC(_struct) ((_struct *)malloc(sizeof(_struct)))
|
||||
#endif
|
||||
|
||||
/* Not defined on all platforms (macOS & WIN32). */
|
||||
using uint = unsigned int;
|
||||
|
||||
// internal definitions
|
||||
|
||||
using ArHosekSkyModel_Dataset = const double *;
|
||||
using ArHosekSkyModel_Radiance_Dataset = const double *;
|
||||
|
||||
// internal functions
|
||||
|
||||
static void ArHosekSkyModel_CookConfiguration(ArHosekSkyModel_Dataset dataset,
|
||||
SKY_ArHosekSkyModelConfiguration config,
|
||||
double turbidity,
|
||||
double albedo,
|
||||
double solar_elevation)
|
||||
{
|
||||
const double *elev_matrix;
|
||||
|
||||
int int_turbidity = int(turbidity);
|
||||
double turbidity_rem = turbidity - double(int_turbidity);
|
||||
|
||||
solar_elevation = pow(solar_elevation / (MATH_PI / 2.0), (1.0 / 3.0));
|
||||
|
||||
// alb 0 low turb
|
||||
|
||||
elev_matrix = dataset + (9 * 6 * (int_turbidity - 1));
|
||||
|
||||
for (uint i = 0; i < 9; ++i) {
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
config[i] =
|
||||
(1.0 - albedo) * (1.0 - turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
|
||||
}
|
||||
|
||||
// alb 1 low turb
|
||||
elev_matrix = dataset + (9 * 6 * 10 + 9 * 6 * (int_turbidity - 1));
|
||||
for (uint i = 0; i < 9; ++i) {
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
config[i] +=
|
||||
(albedo) * (1.0 - turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
|
||||
}
|
||||
|
||||
if (int_turbidity == 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
// alb 0 high turb
|
||||
elev_matrix = dataset + (9 * 6 * (int_turbidity));
|
||||
for (uint i = 0; i < 9; ++i) {
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
config[i] +=
|
||||
(1.0 - albedo) * (turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
|
||||
}
|
||||
|
||||
// alb 1 high turb
|
||||
elev_matrix = dataset + (9 * 6 * 10 + 9 * 6 * (int_turbidity));
|
||||
for (uint i = 0; i < 9; ++i) {
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
config[i] +=
|
||||
(albedo) * (turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[i] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[i + 9] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[i + 18] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[i + 27] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[i + 36] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[i + 45]);
|
||||
}
|
||||
}
|
||||
|
||||
static double ArHosekSkyModel_CookRadianceConfiguration(ArHosekSkyModel_Radiance_Dataset dataset,
|
||||
double turbidity,
|
||||
double albedo,
|
||||
double solar_elevation)
|
||||
{
|
||||
const double *elev_matrix;
|
||||
|
||||
int int_turbidity = int(turbidity);
|
||||
double turbidity_rem = turbidity - double(int_turbidity);
|
||||
double res;
|
||||
solar_elevation = pow(solar_elevation / (MATH_PI / 2.0), (1.0 / 3.0));
|
||||
|
||||
// alb 0 low turb
|
||||
elev_matrix = dataset + (6 * (int_turbidity - 1));
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
res = (1.0 - albedo) * (1.0 - turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[5]);
|
||||
|
||||
// alb 1 low turb
|
||||
elev_matrix = dataset + (6 * 10 + 6 * (int_turbidity - 1));
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
res += (albedo) * (1.0 - turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[5]);
|
||||
if (int_turbidity == 10) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// alb 0 high turb
|
||||
elev_matrix = dataset + (6 * (int_turbidity));
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
res += (1.0 - albedo) * (turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[5]);
|
||||
|
||||
// alb 1 high turb
|
||||
elev_matrix = dataset + (6 * 10 + 6 * (int_turbidity));
|
||||
//(1-t).^3* A1 + 3*(1-t).^2.*t * A2 + 3*(1-t) .* t .^ 2 * A3 + t.^3 * A4;
|
||||
res += (albedo) * (turbidity_rem) *
|
||||
(pow(1.0 - solar_elevation, 5.0) * elev_matrix[0] +
|
||||
5.0 * pow(1.0 - solar_elevation, 4.0) * solar_elevation * elev_matrix[1] +
|
||||
10.0 * pow(1.0 - solar_elevation, 3.0) * pow(solar_elevation, 2.0) * elev_matrix[2] +
|
||||
10.0 * pow(1.0 - solar_elevation, 2.0) * pow(solar_elevation, 3.0) * elev_matrix[3] +
|
||||
5.0 * (1.0 - solar_elevation) * pow(solar_elevation, 4.0) * elev_matrix[4] +
|
||||
pow(solar_elevation, 5.0) * elev_matrix[5]);
|
||||
return res;
|
||||
}
|
||||
|
||||
static double ArHosekSkyModel_GetRadianceInternal(
|
||||
const SKY_ArHosekSkyModelConfiguration configuration, const double theta, const double gamma)
|
||||
{
|
||||
const double expM = exp(configuration[4] * gamma);
|
||||
const double rayM = cos(gamma) * cos(gamma);
|
||||
const double mieM =
|
||||
(1.0 + cos(gamma) * cos(gamma)) /
|
||||
pow((1.0 + configuration[8] * configuration[8] - 2.0 * configuration[8] * cos(gamma)), 1.5);
|
||||
const double zenith = sqrt(cos(theta));
|
||||
|
||||
return (1.0 + configuration[0] * exp(configuration[1] / (cos(theta) + 0.01))) *
|
||||
(configuration[2] + configuration[3] * expM + configuration[5] * rayM +
|
||||
configuration[6] * mieM + configuration[7] * zenith);
|
||||
}
|
||||
|
||||
void SKY_arhosekskymodelstate_free(SKY_ArHosekSkyModelState *state)
|
||||
{
|
||||
free(state);
|
||||
}
|
||||
|
||||
double SKY_arhosekskymodel_radiance(SKY_ArHosekSkyModelState *state,
|
||||
double theta,
|
||||
double gamma,
|
||||
double wavelength)
|
||||
{
|
||||
int low_wl = int((wavelength - 320.0) / 40.0);
|
||||
|
||||
if (low_wl < 0 || low_wl >= 11) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double interp = fmod((wavelength - 320.0) / 40.0, 1.0);
|
||||
|
||||
double val_low = ArHosekSkyModel_GetRadianceInternal(state->configs[low_wl], theta, gamma) *
|
||||
state->radiances[low_wl] * state->emission_correction_factor_sky[low_wl];
|
||||
|
||||
if (interp < 1e-6) {
|
||||
return val_low;
|
||||
}
|
||||
|
||||
double result = (1.0 - interp) * val_low;
|
||||
|
||||
if (low_wl + 1 < 11) {
|
||||
result += interp *
|
||||
ArHosekSkyModel_GetRadianceInternal(state->configs[low_wl + 1], theta, gamma) *
|
||||
state->radiances[low_wl + 1] * state->emission_correction_factor_sky[low_wl + 1];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// xyz and rgb versions
|
||||
|
||||
SKY_ArHosekSkyModelState *SKY_arhosek_xyz_skymodelstate_alloc_init(const double turbidity,
|
||||
const double albedo,
|
||||
const double elevation)
|
||||
{
|
||||
SKY_ArHosekSkyModelState *state = ALLOC(SKY_ArHosekSkyModelState);
|
||||
|
||||
state->solar_radius = TERRESTRIAL_SOLAR_RADIUS;
|
||||
state->turbidity = turbidity;
|
||||
state->albedo = albedo;
|
||||
state->elevation = elevation;
|
||||
|
||||
for (uint channel = 0; channel < 3; ++channel) {
|
||||
ArHosekSkyModel_CookConfiguration(
|
||||
datasetsXYZ[channel], state->configs[channel], turbidity, albedo, elevation);
|
||||
|
||||
state->radiances[channel] = ArHosekSkyModel_CookRadianceConfiguration(
|
||||
datasetsXYZRad[channel], turbidity, albedo, elevation);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
3821
blender-5.2.0/intern/sky/source/sky_hosek_data.h
Normal file
3821
blender-5.2.0/intern/sky/source/sky_hosek_data.h
Normal file
File diff suppressed because it is too large
Load Diff
499
blender-5.2.0/intern/sky/source/sky_math.h
Normal file
499
blender-5.2.0/intern/sky/source/sky_math.h
Normal file
@@ -0,0 +1,499 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_sky_modal
|
||||
*/
|
||||
|
||||
#ifndef __SKY_MATH_H__
|
||||
#define __SKY_MATH_H__
|
||||
|
||||
#ifdef WITH_TBB
|
||||
# include <tbb/parallel_for.h>
|
||||
#else
|
||||
# include <algorithm>
|
||||
#endif
|
||||
|
||||
/* Minimal math implementation for sky model. */
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#ifndef M_PI_F
|
||||
# define M_PI_F (3.1415926535897932f) /* pi */
|
||||
#endif
|
||||
#ifndef M_PI_2_F
|
||||
# define M_PI_2_F (1.5707963267948966f) /* pi/2 */
|
||||
#endif
|
||||
#ifndef M_2PI_F
|
||||
# define M_2PI_F (6.2831853071795864f) /* 2*pi */
|
||||
#endif
|
||||
#ifndef M_1_PI_F
|
||||
# define M_1_PI_F (0.3183098861837067f) /* 1/pi */
|
||||
#endif
|
||||
#ifndef M_4PI_F
|
||||
# define M_4PI_F (12.566370614359172f) /* 4*pi */
|
||||
#endif
|
||||
#ifndef M_1_4PI_F
|
||||
# define M_1_4PI_F (0.0795774715459476f) /* 1/(4*pi) */
|
||||
#endif
|
||||
|
||||
/* float2 */
|
||||
struct float2 {
|
||||
float x, y;
|
||||
|
||||
float2() = default;
|
||||
|
||||
float2(const float *ptr) : x{ptr[0]}, y{ptr[1]} {}
|
||||
|
||||
float2(const float (*ptr)[2]) : float2((const float *)ptr) {}
|
||||
|
||||
explicit float2(float value) : x(value), y(value) {}
|
||||
|
||||
explicit float2(int value) : x(value), y(value) {}
|
||||
|
||||
float2(float x, float y) : x{x}, y{y} {}
|
||||
|
||||
operator const float *() const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
operator float *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
friend float2 operator*(const float2 &a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend float2 operator*(float b, const float2 &a)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend float2 operator/(const float2 &a, float b)
|
||||
{
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend float2 operator/(float b, const float2 &a)
|
||||
{
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend float2 operator/(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x / b.x, a.y / b.y};
|
||||
}
|
||||
|
||||
friend float2 operator-(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
friend float2 operator-(const float2 &a)
|
||||
{
|
||||
return {-a.x, -a.y};
|
||||
}
|
||||
|
||||
float length_squared() const
|
||||
{
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
float length() const
|
||||
{
|
||||
return sqrt(length_squared());
|
||||
}
|
||||
|
||||
static float distance(const float2 &a, const float2 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
friend float2 operator+(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
void operator+=(const float2 &b)
|
||||
{
|
||||
this->x += b.x;
|
||||
this->y += b.y;
|
||||
}
|
||||
|
||||
friend float2 operator*(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y};
|
||||
}
|
||||
};
|
||||
|
||||
/* float3 */
|
||||
struct float3 {
|
||||
float x, y, z;
|
||||
|
||||
float3() = default;
|
||||
|
||||
float3(const float *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]} {}
|
||||
|
||||
float3(const float (*ptr)[3]) : float3((const float *)ptr) {}
|
||||
|
||||
explicit float3(float value) : x(value), y(value), z(value) {}
|
||||
|
||||
explicit float3(int value) : x(value), y(value), z(value) {}
|
||||
|
||||
float3(float x, float y, float z) : x{x}, y{y}, z{z} {}
|
||||
|
||||
operator const float *() const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
operator float *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
friend float3 operator*(const float3 &a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
friend float3 operator*(float b, const float3 &a)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
friend float3 operator/(const float3 &a, float b)
|
||||
{
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
friend float3 operator/(float b, const float3 &a)
|
||||
{
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
friend float3 operator-(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
friend float3 operator-(const float3 &a)
|
||||
{
|
||||
return {-a.x, -a.y, -a.z};
|
||||
}
|
||||
|
||||
float length_squared() const
|
||||
{
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
float length() const
|
||||
{
|
||||
return sqrt(length_squared());
|
||||
}
|
||||
|
||||
static float distance(const float3 &a, const float3 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
friend float3 operator+(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
void operator+=(const float3 &b)
|
||||
{
|
||||
this->x += b.x;
|
||||
this->y += b.y;
|
||||
this->z += b.z;
|
||||
}
|
||||
|
||||
friend float3 operator*(const float3 &a, const float3 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z};
|
||||
}
|
||||
};
|
||||
|
||||
/* float4 */
|
||||
struct float4 {
|
||||
float x, y, z, w;
|
||||
|
||||
float4() = default;
|
||||
|
||||
float4(const float *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}, w{ptr[3]} {}
|
||||
|
||||
float4(const float (*ptr)[4]) : float4((const float *)ptr) {}
|
||||
|
||||
explicit float4(float value) : x(value), y(value), z(value), w(value) {}
|
||||
|
||||
explicit float4(int value) : x(value), y(value), z(value), w(value) {}
|
||||
|
||||
float4(float x, float y, float z, float w) : x{x}, y{y}, z{z}, w{w} {}
|
||||
|
||||
operator const float *() const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
operator float *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
friend float4 operator*(const float4 &a, float b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b, a.w * b};
|
||||
}
|
||||
|
||||
friend float4 operator*(float b, const float4 &a)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b, a.w * b};
|
||||
}
|
||||
|
||||
friend float4 operator/(const float4 &a, float b)
|
||||
{
|
||||
return {a.x / b, a.y / b, a.z / b, a.w / b};
|
||||
}
|
||||
|
||||
friend float4 operator/(float b, const float4 &a)
|
||||
{
|
||||
return {a.x / b, a.y / b, a.z / b, a.w / b};
|
||||
}
|
||||
|
||||
friend float4 operator*(const float4 &a, const float4 &b)
|
||||
{
|
||||
return {a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w};
|
||||
}
|
||||
|
||||
friend float4 operator/(const float4 &a, const float4 &b)
|
||||
{
|
||||
return {a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w};
|
||||
}
|
||||
|
||||
friend float4 operator-(const float4 &a, const float4 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w};
|
||||
}
|
||||
|
||||
friend float4 operator-(const float4 &a)
|
||||
{
|
||||
return {-a.x, -a.y, -a.z, -a.w};
|
||||
}
|
||||
|
||||
float length_squared() const
|
||||
{
|
||||
return x * x + y * y + z * z + w * w;
|
||||
}
|
||||
|
||||
float length() const
|
||||
{
|
||||
return sqrt(length_squared());
|
||||
}
|
||||
|
||||
static float distance(const float4 &a, const float4 &b)
|
||||
{
|
||||
return (a - b).length();
|
||||
}
|
||||
|
||||
friend float4 operator+(const float4 &a, const float4 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w};
|
||||
}
|
||||
|
||||
void operator+=(const float4 &b)
|
||||
{
|
||||
this->x += b.x;
|
||||
this->y += b.y;
|
||||
this->z += b.z;
|
||||
this->w += b.w;
|
||||
}
|
||||
|
||||
void operator*=(const float4 &b)
|
||||
{
|
||||
this->x *= b.x;
|
||||
this->y *= b.y;
|
||||
this->z *= b.z;
|
||||
this->w *= b.w;
|
||||
}
|
||||
};
|
||||
|
||||
inline float sqr(float a)
|
||||
{
|
||||
return a * a;
|
||||
}
|
||||
|
||||
inline float safe_sqrtf(const float f)
|
||||
{
|
||||
return sqrt(fmax(f, 0.0f));
|
||||
}
|
||||
|
||||
inline float2 make_float2(float x, float y)
|
||||
{
|
||||
return float2(x, y);
|
||||
}
|
||||
|
||||
inline float dot(const float2 &a, const float2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
inline float distance(const float2 &a, const float2 &b)
|
||||
{
|
||||
return float2::distance(a, b);
|
||||
}
|
||||
|
||||
inline float len_squared(float2 f)
|
||||
{
|
||||
return f.length_squared();
|
||||
}
|
||||
|
||||
inline float len(float2 f)
|
||||
{
|
||||
return f.length();
|
||||
}
|
||||
|
||||
inline float reduce_add(float2 f)
|
||||
{
|
||||
return f.x + f.y;
|
||||
}
|
||||
|
||||
inline float3 make_float3(float x, float y, float z)
|
||||
{
|
||||
return float3(x, y, z);
|
||||
}
|
||||
|
||||
inline float dot(const float3 &a, const float3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline float distance(const float3 &a, const float3 &b)
|
||||
{
|
||||
return float3::distance(a, b);
|
||||
}
|
||||
|
||||
inline float len_squared(float3 f)
|
||||
{
|
||||
return f.length_squared();
|
||||
}
|
||||
|
||||
inline float len(float3 f)
|
||||
{
|
||||
return f.length();
|
||||
}
|
||||
|
||||
inline float reduce_add(float3 f)
|
||||
{
|
||||
return f.x + f.y + f.z;
|
||||
}
|
||||
|
||||
inline float4 make_float4(float x, float y, float z, float w)
|
||||
{
|
||||
return float4(x, y, z, w);
|
||||
}
|
||||
|
||||
inline float dot(const float4 a, const float4 b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
}
|
||||
|
||||
inline float distance(const float4 &a, const float4 &b)
|
||||
{
|
||||
return float4::distance(a, b);
|
||||
}
|
||||
|
||||
inline float len_squared(float4 f)
|
||||
{
|
||||
return f.length_squared();
|
||||
}
|
||||
|
||||
inline float len(float4 f)
|
||||
{
|
||||
return f.length();
|
||||
}
|
||||
|
||||
inline float reduce_add(float4 f)
|
||||
{
|
||||
return f.x + f.y + f.z + f.w;
|
||||
}
|
||||
|
||||
inline float4 exp(float4 a)
|
||||
{
|
||||
return make_float4(expf(a.x), expf(a.y), expf(a.z), expf(a.w));
|
||||
}
|
||||
|
||||
inline float4 max(float4 a, float b)
|
||||
{
|
||||
return make_float4(fmax(a.x, b), fmax(a.y, b), fmax(a.z, b), fmax(a.w, b));
|
||||
}
|
||||
|
||||
inline float clamp(float x, float min, float max)
|
||||
{
|
||||
if (x < min) {
|
||||
return min;
|
||||
}
|
||||
if (x > max) {
|
||||
return max;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
inline float saturate(const float a)
|
||||
{
|
||||
return clamp(a, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
template<typename T> inline T mix(T x, T y, float a)
|
||||
{
|
||||
return x + a * (y - x);
|
||||
}
|
||||
|
||||
inline float3 sun_direction(float sun_cos_theta)
|
||||
{
|
||||
return make_float3(-sqrtf(1.0f - sun_cos_theta * sun_cos_theta), 0.0f, sun_cos_theta);
|
||||
}
|
||||
|
||||
inline float ray_sphere_intersection(float3 pos, float3 dir, float radius)
|
||||
{
|
||||
float b = dot(pos, dir);
|
||||
float c = dot(pos, pos) - radius * radius;
|
||||
if (c > 0.0f && b > 0.0f) {
|
||||
return -1.0f;
|
||||
}
|
||||
float d = b * b - c;
|
||||
if (d < 0) {
|
||||
return -1.0f;
|
||||
}
|
||||
if (d >= b * b) {
|
||||
return -b + sqrtf(d);
|
||||
}
|
||||
return -b - sqrtf(d);
|
||||
}
|
||||
|
||||
/* Minimal parallel for implementation. */
|
||||
|
||||
template<typename Function>
|
||||
inline void SKY_parallel_for(const size_t begin,
|
||||
const size_t end,
|
||||
const size_t grainsize,
|
||||
const Function &function)
|
||||
{
|
||||
#ifdef WITH_TBB
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(begin, end, grainsize),
|
||||
[function](const tbb::blocked_range<size_t> &r) { function(r.begin(), r.end()); });
|
||||
#else
|
||||
for (size_t i = begin; i < end; i += grainsize) {
|
||||
function(i, std::min(i + grainsize, end));
|
||||
}
|
||||
(void)grainsize;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* __SKY_MATH_H__ */
|
||||
401
blender-5.2.0/intern/sky/source/sky_multiple_scattering.cpp
Normal file
401
blender-5.2.0/intern/sky/source/sky_multiple_scattering.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Fernando García Liñán
|
||||
* SPDX-FileCopyrightText: 2011-2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT */
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_sky_modal
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is a converted version of the ShaderToy written by Fernando García Liñán.
|
||||
*
|
||||
* This shader is the final result of my Master's Thesis.
|
||||
* The main contributions are:
|
||||
*
|
||||
* 1. A spectral rendering technique that only requires 4 wavelength samples to
|
||||
* get accurate results.
|
||||
* 2. A multiple scattering approximation.
|
||||
*
|
||||
* Both of these approximations rely on an analytical fit, so they only work for
|
||||
* Earth's atmosphere. We make up for it by using a very flexible atmosphere
|
||||
* model that is able to represent a wide variety of atmospheric conditions.
|
||||
*
|
||||
* A brief description of this spectral rendering technique can be found in the
|
||||
* following article:
|
||||
* https://fgarlin.com/posts/2024-12-06-spectral_sky/
|
||||
*
|
||||
* The path tracer that has been used as a ground truth can be found at:
|
||||
* https://github.com/fgarlin/skytracer
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "sky_math.h"
|
||||
#include "sky_nishita.h"
|
||||
|
||||
using std::min;
|
||||
|
||||
/* Earth's atmosphere parameters. */
|
||||
/* Ground reflectance. */
|
||||
static const float4 GROUND_ALBEDO = make_float4(0.3f, 0.3f, 0.3f, 0.3f);
|
||||
static const float PHASE_ISOTROPIC = M_1_4PI_F;
|
||||
static const float RAYLEIGH_PHASE_SCALE = (3.0f / 16.0f) * M_1_PI_F;
|
||||
/* Aerosols anisotropy. */
|
||||
static const float G = 0.8f;
|
||||
static const float SQR_G = G * G;
|
||||
/* Earth radius (km). */
|
||||
static const float EARTH_RADIUS = 6371.0f;
|
||||
/* Atmosphere thickness (km). */
|
||||
static const float ATMOSPHERE_THICKNESS = 100.0f;
|
||||
static const float ATMOSPHERE_RADIUS = EARTH_RADIUS + ATMOSPHERE_THICKNESS;
|
||||
/* Ray marching steps. Higher steps means increased accuracy but worse performance. */
|
||||
static const int TRANSMITTANCE_STEPS = 64;
|
||||
static const int IN_SCATTERING_STEPS = 64;
|
||||
|
||||
/* LUTs. */
|
||||
static const int TRANSMITTANCE_RES_X = 256;
|
||||
static const int TRANSMITTANCE_RES_Y = 64;
|
||||
|
||||
/* Spectral data sampled at 630, 560, 490, 430 nm for urban area. */
|
||||
static const float4 SUN_SPECTRAL_IRRADIANCE = make_float4(1.679f, 1.828f, 1.986f, 1.307f);
|
||||
static const float4 MOLECULAR_SCATTERING_COEFFICIENT_BASE = make_float4(
|
||||
6.605e-3f, 1.067e-2f, 1.842e-2f, 3.156e-2f);
|
||||
static const float4 OZONE_ABSORPTION_CROSS_SECTION = make_float4(
|
||||
3.472e-25f, 3.914e-25f, 1.349e-25f, 11.03e-27f);
|
||||
/* Average ozone dobson of monthly mean values. */
|
||||
static const float OZONE_MEAN_DOBSON = 334.5f;
|
||||
static const float4 AEROSOL_ABSORPTION_CROSS_SECTION = make_float4(
|
||||
2.8722e-24f, 4.6168e-24f, 7.9706e-24f, 1.3578e-23f);
|
||||
static const float4 AEROSOL_SCATTERING_CROSS_SECTION = make_float4(
|
||||
1.5908e-22f, 1.7711e-22f, 2.0942e-22f, 2.4033e-22f);
|
||||
static const float AEROSOL_BASE_DENSITY = 1.3681e20f;
|
||||
static const float AEROSOL_BACKGROUND_DENSITY = 2e6f;
|
||||
static const float AEROSOL_HEIGHT_SCALE = 0.73f;
|
||||
/* Spectral to XYZ space conversion matrix. */
|
||||
static const float3 SPECTRAL_XYZ[4] = {
|
||||
make_float3(53.386917738564668023f, 22.981337506691024754f, 0.0f),
|
||||
make_float3(43.904844466369358263f, 71.347795700053393866f, 0.102506867965741307f),
|
||||
make_float3(1.6137278251608962005f, 18.422960591455485011f, 31.742921188390805758f),
|
||||
make_float3(20.762668673810577145f, 2.3614213523314368527f, 110.48009643252140334f),
|
||||
};
|
||||
|
||||
inline float molecular_phase_function(const float cos_theta)
|
||||
{
|
||||
return RAYLEIGH_PHASE_SCALE * (1.0f + sqr(cos_theta));
|
||||
}
|
||||
|
||||
inline float aerosol_phase_function(const float cos_theta)
|
||||
{
|
||||
float den = 1.0f + SQR_G + 2.0f * G * cos_theta;
|
||||
return M_1_4PI_F * (1.0f - SQR_G) / (den * sqrtf(den));
|
||||
}
|
||||
|
||||
inline float4 get_molecular_scattering_coefficient(const float h)
|
||||
{
|
||||
return MOLECULAR_SCATTERING_COEFFICIENT_BASE * expf(-0.07771971f * powf(h, 1.16364243f));
|
||||
}
|
||||
|
||||
inline float4 get_molecular_absorption_coefficient(const float h)
|
||||
{
|
||||
const float log_h = logf(fmaxf(h, 1e-4f));
|
||||
float density = 3.78547397e20f * expf(-sqr(log_h - 3.22261f) * 5.55555555f - log_h);
|
||||
return OZONE_ABSORPTION_CROSS_SECTION * OZONE_MEAN_DOBSON * density;
|
||||
}
|
||||
|
||||
inline float get_aerosol_density(const float h)
|
||||
{
|
||||
float division = AEROSOL_BACKGROUND_DENSITY / AEROSOL_BASE_DENSITY;
|
||||
return AEROSOL_BASE_DENSITY * (expf(-h / AEROSOL_HEIGHT_SCALE) + division);
|
||||
}
|
||||
|
||||
inline float3 spectral_to_xyz(const float4 L)
|
||||
{
|
||||
float3 xyz = make_float3(0.0f, 0.0f, 0.0f);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
xyz += SPECTRAL_XYZ[i] * L[i];
|
||||
}
|
||||
return xyz;
|
||||
}
|
||||
|
||||
/* Precomputed data. */
|
||||
class SkyMultipleScattering {
|
||||
public:
|
||||
SkyMultipleScattering(const float air_density,
|
||||
const float aerosol_density,
|
||||
const float ozone_density)
|
||||
: air_density(air_density), aerosol_density(aerosol_density), ozone_density(ozone_density)
|
||||
{
|
||||
}
|
||||
|
||||
/* Compute atmosphere's transmittance from the given altitude to the sun. */
|
||||
float4 get_transmittance(const float cos_theta, const float normalized_altitude) const
|
||||
{
|
||||
const float3 sun_dir = sun_direction(cos_theta);
|
||||
const float distance_to_earth_center = mix(
|
||||
EARTH_RADIUS, ATMOSPHERE_RADIUS, normalized_altitude);
|
||||
const float3 ray_origin = make_float3(0.0f, 0.0f, distance_to_earth_center);
|
||||
const float t_d = ray_sphere_intersection(ray_origin, sun_dir, ATMOSPHERE_RADIUS);
|
||||
const float t_step = t_d / TRANSMITTANCE_STEPS;
|
||||
|
||||
float4 result = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
for (int step = 0; step < TRANSMITTANCE_STEPS; step++) {
|
||||
const float t = (step + 0.5f) * t_step;
|
||||
const float3 x_t = ray_origin + sun_dir * t;
|
||||
const float altitude = fmaxf(x_t.length() - EARTH_RADIUS, 0.0f);
|
||||
float4 aerosol_absorption, aerosol_scattering, molecular_absorption, molecular_scattering;
|
||||
get_atmosphere_collision_coefficients(altitude,
|
||||
aerosol_absorption,
|
||||
aerosol_scattering,
|
||||
molecular_absorption,
|
||||
molecular_scattering);
|
||||
const float4 extinction = aerosol_absorption + aerosol_scattering + molecular_absorption +
|
||||
molecular_scattering;
|
||||
result += extinction * t_step;
|
||||
}
|
||||
|
||||
return exp(-result);
|
||||
}
|
||||
|
||||
/* Compute in-scattered radiance for the given ray. */
|
||||
float4 get_inscattering(const float3 sun_dir,
|
||||
const float3 ray_origin,
|
||||
const float3 ray_dir,
|
||||
const float t_d) const
|
||||
{
|
||||
const float cos_theta = dot(-ray_dir, sun_dir);
|
||||
const float molecular_phase = molecular_phase_function(cos_theta);
|
||||
const float aerosol_phase = aerosol_phase_function(cos_theta);
|
||||
const float dt = t_d / IN_SCATTERING_STEPS;
|
||||
float4 L_inscattering = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
float4 transmittance = make_float4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
for (int i = 0; i < IN_SCATTERING_STEPS; i++) {
|
||||
const float t = (i + 0.5f) * dt;
|
||||
const float3 x_t = ray_origin + ray_dir * t;
|
||||
const float distance_to_earth_center = x_t.length();
|
||||
const float3 zenith_dir = x_t / distance_to_earth_center;
|
||||
const float altitude = fmaxf(distance_to_earth_center - EARTH_RADIUS, 0.0f);
|
||||
const float normalized_altitude = altitude / ATMOSPHERE_THICKNESS;
|
||||
const float sample_cos_theta = dot(zenith_dir, sun_dir);
|
||||
float4 aerosol_absorption, aerosol_scattering, molecular_absorption, molecular_scattering;
|
||||
get_atmosphere_collision_coefficients(altitude,
|
||||
aerosol_absorption,
|
||||
aerosol_scattering,
|
||||
molecular_absorption,
|
||||
molecular_scattering);
|
||||
const float4 extinction = aerosol_absorption + aerosol_scattering + molecular_absorption +
|
||||
molecular_scattering;
|
||||
const float4 transmittance_to_sun = lookup_transmittance(sample_cos_theta,
|
||||
normalized_altitude);
|
||||
const float4 ms = lookup_multiscattering(
|
||||
sample_cos_theta, normalized_altitude, distance_to_earth_center);
|
||||
const float4 S = SUN_SPECTRAL_IRRADIANCE *
|
||||
(molecular_scattering * (molecular_phase * transmittance_to_sun + ms) +
|
||||
aerosol_scattering * (aerosol_phase * transmittance_to_sun + ms));
|
||||
const float4 step_transmittance = exp(-dt * extinction);
|
||||
/* Energy-conserving analytical integration "Physically Based Sky, Atmosphere and Cloud
|
||||
* Rendering in Frostbite" by Sébastien Hillaire. */
|
||||
const float4 cut_ext = max(extinction, 1e-7f);
|
||||
const float4 S_int = (S - S * step_transmittance) / cut_ext;
|
||||
L_inscattering = L_inscattering + transmittance * S_int;
|
||||
transmittance *= step_transmittance;
|
||||
}
|
||||
|
||||
return L_inscattering;
|
||||
}
|
||||
|
||||
/* Precompute the transmittance LUT. Must be called before get_inscattering(). */
|
||||
void precompute_lut()
|
||||
{
|
||||
SKY_parallel_for(0, TRANSMITTANCE_RES_Y, 4, [&](const size_t begin, const size_t end) {
|
||||
for (int y = begin; y < end; y++) {
|
||||
for (int x = 0; x < TRANSMITTANCE_RES_X; x++) {
|
||||
const float2 uv = make_float2(x / float(TRANSMITTANCE_RES_X - 1),
|
||||
y / float(TRANSMITTANCE_RES_Y - 1));
|
||||
transmittance_lut[y][x] = get_transmittance(uv.x * 2.0f - 1.0f, uv.y);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
float4 transmittance_lut[TRANSMITTANCE_RES_Y][TRANSMITTANCE_RES_X];
|
||||
float air_density;
|
||||
float aerosol_density;
|
||||
float ozone_density;
|
||||
|
||||
/* Compute absorption/scattering coeffients at the given altitude. */
|
||||
void get_atmosphere_collision_coefficients(const float altitude,
|
||||
float4 &aerosol_absorption,
|
||||
float4 &aerosol_scattering,
|
||||
float4 &molecular_absorption,
|
||||
float4 &molecular_scattering) const
|
||||
{
|
||||
const float local_aerosol_density = get_aerosol_density(altitude) * aerosol_density;
|
||||
aerosol_absorption = AEROSOL_ABSORPTION_CROSS_SECTION * local_aerosol_density;
|
||||
aerosol_scattering = AEROSOL_SCATTERING_CROSS_SECTION * local_aerosol_density;
|
||||
molecular_absorption = get_molecular_absorption_coefficient(altitude) * ozone_density;
|
||||
molecular_scattering = get_molecular_scattering_coefficient(altitude) * air_density;
|
||||
}
|
||||
|
||||
float4 lookup_multiscattering(float cos_theta, float normalized_height, float d) const
|
||||
{
|
||||
/* Solid angle subtended by the planet from a point at d distance from the planet center. */
|
||||
const float omega = M_2PI_F * (1.0f - safe_sqrtf(1.0f - sqr(EARTH_RADIUS / d)));
|
||||
const float4 T_to_ground = lookup_transmittance_at_ground(cos_theta);
|
||||
/* We can split the path into Ground <-> Sample <-> Sun.
|
||||
* The LUT gives us both T(Sample,Sun) and T(Ground,Sun) = T(Ground,Sample)*T(Sample,Sun),
|
||||
* so we can easily compute T(Ground,Sample) from those two. */
|
||||
const float4 T_ground_to_sample = lookup_transmittance_to_sun(0.0f) /
|
||||
lookup_transmittance_to_sun(normalized_height);
|
||||
/* 2nd order scattering from the ground. */
|
||||
const float4 L_ground = PHASE_ISOTROPIC * omega * (GROUND_ALBEDO * M_1_PI_F) * T_to_ground *
|
||||
T_ground_to_sample * cos_theta;
|
||||
/* Fit of Earth's multiple scattering coming from other points in the atmosphere. */
|
||||
const float4 L_ms = 0.02f * make_float4(0.217f, 0.347f, 0.594f, 1.0f) *
|
||||
(1.0f / (1.0f + 5.0f * expf(-17.92f * cos_theta)));
|
||||
return L_ms + L_ground;
|
||||
}
|
||||
|
||||
/* Look up a transmittance from the precomputed LUT. */
|
||||
float4 lookup_transmittance(const float cos_theta, const float normalized_altitude) const
|
||||
{
|
||||
const float u = saturate(cos_theta * 0.5f + 0.5f);
|
||||
const float v = saturate(normalized_altitude);
|
||||
const float x = float(TRANSMITTANCE_RES_X - 1) * u;
|
||||
const float y = float(TRANSMITTANCE_RES_Y - 1) * v;
|
||||
const int x1 = int(x);
|
||||
const int y1 = int(y);
|
||||
const int x2 = min(x1 + 1, TRANSMITTANCE_RES_X - 1);
|
||||
const int y2 = min(y1 + 1, TRANSMITTANCE_RES_Y - 1);
|
||||
const float fx = x - x1;
|
||||
const float fy = y - y1;
|
||||
const float4 bottom = mix(transmittance_lut[y1][x1], transmittance_lut[y1][x2], fx);
|
||||
const float4 top = mix(transmittance_lut[y2][x1], transmittance_lut[y2][x2], fx);
|
||||
return mix(bottom, top, fy);
|
||||
}
|
||||
|
||||
/* Specialized versions of lookup_transmittance that skip one interpolation. */
|
||||
float4 lookup_transmittance_at_ground(const float cos_theta) const
|
||||
{
|
||||
const float u = saturate(cos_theta * 0.5f + 0.5f);
|
||||
const float x = float(TRANSMITTANCE_RES_X - 1) * u;
|
||||
const int x1 = int(x);
|
||||
const int x2 = min(x1 + 1, TRANSMITTANCE_RES_X - 1);
|
||||
const int y = 0;
|
||||
const float fx = x - x1;
|
||||
return mix(transmittance_lut[y][x1], transmittance_lut[y][x2], fx);
|
||||
}
|
||||
|
||||
float4 lookup_transmittance_to_sun(const float normalized_altitude) const
|
||||
{
|
||||
const float v = saturate(normalized_altitude);
|
||||
const float y = float(TRANSMITTANCE_RES_Y - 1) * v;
|
||||
const int x = TRANSMITTANCE_RES_X - 1;
|
||||
const int y1 = int(y);
|
||||
const int y2 = min(y1 + 1, TRANSMITTANCE_RES_Y - 1);
|
||||
const float fy = y - y1;
|
||||
return mix(transmittance_lut[y1][x], transmittance_lut[y2][x], fy);
|
||||
}
|
||||
};
|
||||
|
||||
void SKY_multiple_scattering_precompute_texture(float *pixels,
|
||||
int stride,
|
||||
int width,
|
||||
int height,
|
||||
float sun_elevation,
|
||||
float altitude,
|
||||
float air_density,
|
||||
float aerosol_density,
|
||||
float ozone_density)
|
||||
{
|
||||
SkyMultipleScattering sms(air_density, aerosol_density, ozone_density);
|
||||
sms.precompute_lut();
|
||||
|
||||
/* Clamp altitude to avoid numerical issues. */
|
||||
altitude = clamp(altitude, 1.0f, 99999.0f) / 1000.0f;
|
||||
const int half_width = width / 2;
|
||||
const float sun_zenith_cos_angle = cosf(M_PI_2_F - sun_elevation);
|
||||
const float3 sun_dir = sun_direction(sun_zenith_cos_angle);
|
||||
const int rows_per_task = std::max(1024 / width, 1);
|
||||
|
||||
SKY_parallel_for(0, height, rows_per_task, [&](const size_t begin, const size_t end) {
|
||||
for (int y = begin; y < end; y++) {
|
||||
float *pixel_row = pixels + (y * width * stride);
|
||||
for (int x = 0; x < half_width; x++) {
|
||||
float2 uv = make_float2((x + 0.5f) / width, (y + 0.5f) / height);
|
||||
|
||||
const float azimuth = M_2PI_F * uv.x;
|
||||
/* Apply a non-linear transformation to the elevation to dedicate more texels to the
|
||||
* horizon, where having more detail matters. */
|
||||
const float l = uv.y * 2.0f - 1.0f;
|
||||
/* [-pi/2, pi/2]. */
|
||||
const float elev = copysignf(sqr(l), l) * M_PI_2_F;
|
||||
const float3 ray_dir = make_float3(
|
||||
cosf(elev) * cosf(azimuth), cosf(elev) * sinf(azimuth), sinf(elev));
|
||||
const float3 ray_origin = make_float3(0.0f, 0.0f, EARTH_RADIUS + altitude);
|
||||
const float atmos_dist = ray_sphere_intersection(ray_origin, ray_dir, ATMOSPHERE_RADIUS);
|
||||
const float ground_dist = ray_sphere_intersection(ray_origin, ray_dir, EARTH_RADIUS);
|
||||
/* If no ground collision then use the distance to the outer atmosphere, else we have a
|
||||
* collision with the ground so we use the distance to it. */
|
||||
const float t_d = (ground_dist < 0.0f) ? atmos_dist : ground_dist;
|
||||
const float4 L = sms.get_inscattering(sun_dir, ray_origin, ray_dir, t_d);
|
||||
const float3 sky = spectral_to_xyz(L);
|
||||
|
||||
/* Store pixels. */
|
||||
const int pos_x = x * stride;
|
||||
pixel_row[pos_x] = sky.x;
|
||||
pixel_row[pos_x + 1] = sky.y;
|
||||
pixel_row[pos_x + 2] = sky.z;
|
||||
/* Mirror pixels. */
|
||||
const int mirror_x = (width - x - 1) * stride;
|
||||
pixel_row[mirror_x] = sky.x;
|
||||
pixel_row[mirror_x + 1] = sky.y;
|
||||
pixel_row[mirror_x + 2] = sky.z;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SKY_multiple_scattering_precompute_sun(float sun_elevation,
|
||||
float angular_diameter,
|
||||
float altitude,
|
||||
float air_density,
|
||||
float aerosol_density,
|
||||
float ozone_density,
|
||||
float r_pixel_bottom[3],
|
||||
float r_pixel_top[3])
|
||||
{
|
||||
const SkyMultipleScattering sms(air_density, aerosol_density, ozone_density);
|
||||
|
||||
/* Clamp altitude to avoid numerical issues. */
|
||||
altitude = clamp(altitude, 1.0f, 99999.0f) / 1000.0f;
|
||||
const float half_angular = angular_diameter / 2.0f;
|
||||
const float solid_angle = M_2PI_F * (1.0f - cosf(half_angular));
|
||||
const float normalized_altitude = altitude / ATMOSPHERE_THICKNESS;
|
||||
|
||||
/* Compute 2 pixels for Sun disc: one is the lowest point of the disc, one is the highest. */
|
||||
auto get_sun_xyz = [&](const float elevation) {
|
||||
const float sun_zenith_cos_angle = cosf(M_PI_2_F - elevation);
|
||||
const float4 transmittance_to_sun = sms.get_transmittance(sun_zenith_cos_angle,
|
||||
normalized_altitude);
|
||||
const float4 spectrum = SUN_SPECTRAL_IRRADIANCE * transmittance_to_sun / solid_angle;
|
||||
return spectral_to_xyz(spectrum);
|
||||
};
|
||||
const float3 bottom = get_sun_xyz(sun_elevation - half_angular);
|
||||
const float3 top = get_sun_xyz(sun_elevation + half_angular);
|
||||
|
||||
/* Store pixels */
|
||||
r_pixel_bottom[0] = bottom.x;
|
||||
r_pixel_bottom[1] = bottom.y;
|
||||
r_pixel_bottom[2] = bottom.z;
|
||||
r_pixel_top[0] = top.x;
|
||||
r_pixel_top[1] = top.y;
|
||||
r_pixel_top[2] = top.z;
|
||||
}
|
||||
|
||||
float SKY_earth_intersection_angle(float altitude)
|
||||
{
|
||||
/* Calculate intersection angle between line passing through viewpoint and Earth surface. */
|
||||
return M_PI_2_F - asinf(EARTH_RADIUS / (EARTH_RADIUS + altitude / 1000.0f));
|
||||
}
|
||||
423
blender-5.2.0/intern/sky/source/sky_single_scattering.cpp
Normal file
423
blender-5.2.0/intern/sky/source/sky_single_scattering.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_sky_modal
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "sky_math.h"
|
||||
#include "sky_nishita.h"
|
||||
|
||||
/* Constants. */
|
||||
static const float RAYLEIGH_SCALE = 8e3f; /* Rayleigh scale height (m). */
|
||||
static const float MIE_SCALE = 1.2e3f; /* Mie scale height (m). */
|
||||
static const float MIE_COEFF = 2e-5f; /* Mie scattering coefficient (m^-1). */
|
||||
static const float MIE_G = 0.76f; /* Aerosols anisotropy. */
|
||||
static const float SQR_G = MIE_G * MIE_G; /* Squared aerosols anisotropy. */
|
||||
static const float EARTH_RADIUS = 6360e3f; /* Radius of Earth (m). */
|
||||
static const float ATMOSPHERE_RADIUS = 6420e3f; /* Radius of atmosphere (m). */
|
||||
static const int STEPS = 32; /* Segments of primary ray. */
|
||||
static const int NUM_WAVELENGTHS = 21; /* Number of wavelengths. */
|
||||
static const int MIN_WAVELENGTH = 380; /* Lowest sampled wavelength (nm). */
|
||||
static const int MAX_WAVELENGTH = 780; /* Highest sampled wavelength (nm). */
|
||||
/* Step between each sampled wavelength (nm). */
|
||||
static const float STEP_LAMBDA = (MAX_WAVELENGTH - MIN_WAVELENGTH) / (NUM_WAVELENGTHS - 1);
|
||||
/* Sun irradiance on top of the atmosphere (W*m^-2*nm^-1). */
|
||||
static const float IRRADIANCE[] = {
|
||||
1.45756829855592995315f, 1.56596305559738380175f, 1.65148449067670455293f,
|
||||
1.71496242737209314555f, 1.75797983805020541226f, 1.78256407885924539336f,
|
||||
1.79095108475838560302f, 1.78541550133410664714f, 1.76815554864306845317f,
|
||||
1.74122069647250410362f, 1.70647127164943679389f, 1.66556087452739887134f,
|
||||
1.61993437242451854274f, 1.57083597368892080581f, 1.51932335059305478886f,
|
||||
1.46628494965214395407f, 1.41245852740172450623f, 1.35844961970384092709f,
|
||||
1.30474913844739281998f, 1.25174963272610817455f, 1.19975998755420620867f};
|
||||
/* Rayleigh scattering coefficient (m^-1). */
|
||||
static const float RAYLEIGH_COEFF[] = {
|
||||
0.00005424820087636473f, 0.00004418549866505454f, 0.00003635151910165377f,
|
||||
0.00003017929012024763f, 0.00002526320226989157f, 0.00002130859310621843f,
|
||||
0.00001809838025320633f, 0.00001547057129129042f, 0.00001330284977336850f,
|
||||
0.00001150184784075764f, 0.00000999557429990163f, 0.00000872799973630707f,
|
||||
0.00000765513700977967f, 0.00000674217203751443f, 0.00000596134125832052f,
|
||||
0.00000529034598065810f, 0.00000471115687557433f, 0.00000420910481110487f,
|
||||
0.00000377218381260133f, 0.00000339051255477280f, 0.00000305591531679811f};
|
||||
/* Ozone absorption coefficient (m^-1). */
|
||||
static const float OZONE_COEFF[] = {
|
||||
0.00000000325126849861f, 0.00000000585395365047f, 0.00000001977191155085f,
|
||||
0.00000007309568762914f, 0.00000020084561514287f, 0.00000040383958096161f,
|
||||
0.00000063551335912363f, 0.00000096707041180970f, 0.00000154797400424410f,
|
||||
0.00000209038647223331f, 0.00000246128056164565f, 0.00000273551299461512f,
|
||||
0.00000215125863128643f, 0.00000159051840791988f, 0.00000112356197979857f,
|
||||
0.00000073527551487574f, 0.00000046450130357806f, 0.00000033096079921048f,
|
||||
0.00000022512612292678f, 0.00000014879129266490f, 0.00000016828623364192f};
|
||||
/* CIE XYZ color matching functions. */
|
||||
static const float CMF_XYZ[][3] = {{0.00136800000f, 0.00003900000f, 0.00645000100f},
|
||||
{0.01431000000f, 0.00039600000f, 0.06785001000f},
|
||||
{0.13438000000f, 0.00400000000f, 0.64560000000f},
|
||||
{0.34828000000f, 0.02300000000f, 1.74706000000f},
|
||||
{0.29080000000f, 0.06000000000f, 1.66920000000f},
|
||||
{0.09564000000f, 0.13902000000f, 0.81295010000f},
|
||||
{0.00490000000f, 0.32300000000f, 0.27200000000f},
|
||||
{0.06327000000f, 0.71000000000f, 0.07824999000f},
|
||||
{0.29040000000f, 0.95400000000f, 0.02030000000f},
|
||||
{0.59450000000f, 0.99500000000f, 0.00390000000f},
|
||||
{0.91630000000f, 0.87000000000f, 0.00165000100f},
|
||||
{1.06220000000f, 0.63100000000f, 0.00080000000f},
|
||||
{0.85444990000f, 0.38100000000f, 0.00019000000f},
|
||||
{0.44790000000f, 0.17500000000f, 0.00002000000f},
|
||||
{0.16490000000f, 0.06100000000f, 0.00000000000f},
|
||||
{0.04677000000f, 0.01700000000f, 0.00000000000f},
|
||||
{0.01135916000f, 0.00410200000f, 0.00000000000f},
|
||||
{0.00289932700f, 0.00104700000f, 0.00000000000f},
|
||||
{0.00069007860f, 0.00024920000f, 0.00000000000f},
|
||||
{0.00016615050f, 0.00006000000f, 0.00000000000f},
|
||||
{0.00004150994f, 0.00001499000f, 0.00000000000f}};
|
||||
|
||||
/* Parameters for optical depth quadrature.
|
||||
* See the comment in ray_optical_depth for more detail.
|
||||
* Computed using sympy and following Python code:
|
||||
* # from sympy.integrals.quadrature import gauss_laguerre
|
||||
* # from sympy import exp
|
||||
* # x, w = gauss_laguerre(8, 50)
|
||||
* # xend = 25
|
||||
* # print([(xi / xend).evalf(10) for xi in x])
|
||||
* # print([(wi * exp(xi) / xend).evalf(10) for xi, wi in zip(x, w)])
|
||||
*/
|
||||
static const int QUADRATURE_STEPS = 8;
|
||||
static const float QUADRATURE_NODES[] = {0.006811185292f,
|
||||
0.03614807107f,
|
||||
0.09004346519f,
|
||||
0.1706680068f,
|
||||
0.2818362161f,
|
||||
0.4303406404f,
|
||||
0.6296271457f,
|
||||
0.9145252695f};
|
||||
static const float QUADRATURE_WEIGHTS[] = {0.01750893642f,
|
||||
0.04135477391f,
|
||||
0.06678839063f,
|
||||
0.09507698807f,
|
||||
0.1283416365f,
|
||||
0.1707430204f,
|
||||
0.2327233347f,
|
||||
0.3562490486f};
|
||||
|
||||
static float3 geographical_to_direction(float lat, float lon)
|
||||
{
|
||||
return make_float3(cosf(lat) * cosf(lon), cosf(lat) * sinf(lon), sinf(lat));
|
||||
}
|
||||
|
||||
static float3 spec_to_xyz(const float *spectrum)
|
||||
{
|
||||
float3 xyz = make_float3(0.0f, 0.0f, 0.0f);
|
||||
for (int i = 0; i < NUM_WAVELENGTHS; i++) {
|
||||
xyz.x += CMF_XYZ[i][0] * spectrum[i];
|
||||
xyz.y += CMF_XYZ[i][1] * spectrum[i];
|
||||
xyz.z += CMF_XYZ[i][2] * spectrum[i];
|
||||
}
|
||||
return xyz * STEP_LAMBDA;
|
||||
}
|
||||
|
||||
/* Atmosphere volume models */
|
||||
static float density_rayleigh(float height)
|
||||
{
|
||||
return expf(-height / RAYLEIGH_SCALE);
|
||||
}
|
||||
|
||||
static float density_mie(float height)
|
||||
{
|
||||
return expf(-height / MIE_SCALE);
|
||||
}
|
||||
|
||||
static float density_ozone(float height)
|
||||
{
|
||||
return fmax(0.0, 1.0 - (fabs(height - 25000.0) / 15000.0));
|
||||
}
|
||||
|
||||
static float phase_rayleigh(float mu)
|
||||
{
|
||||
return (0.1875f * M_1_PI_F) * (1.0f + sqr(mu));
|
||||
}
|
||||
|
||||
static float phase_mie(float mu)
|
||||
{
|
||||
return (3.0f * (1.0f - SQR_G) * (1.0f + sqr(mu))) /
|
||||
(8.0f * M_PI_F * (2.0f + SQR_G) * powf((1.0f + SQR_G - 2.0f * MIE_G * mu), 1.5));
|
||||
}
|
||||
|
||||
/* Intersection helpers. */
|
||||
static bool surface_intersection(float3 pos, float3 dir)
|
||||
{
|
||||
if (dir.z >= 0) {
|
||||
return false;
|
||||
}
|
||||
float b = -2.0f * dot(dir, -pos);
|
||||
float c = len_squared(pos) - sqr(EARTH_RADIUS);
|
||||
float t = b * b - 4.0f * c;
|
||||
if (t >= 0.0f) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static float3 atmosphere_intersection(float3 pos, float3 dir)
|
||||
{
|
||||
float b = -2.0f * dot(dir, -pos);
|
||||
float c = len_squared(pos) - sqr(ATMOSPHERE_RADIUS);
|
||||
float t = (-b + sqrtf(b * b - 4.0f * c)) / 2.0f;
|
||||
return make_float3(pos.x + dir.x * t, pos.y + dir.y * t, pos.z + dir.z * t);
|
||||
}
|
||||
|
||||
static float3 ray_optical_depth(float3 ray_origin, float3 ray_dir)
|
||||
{
|
||||
/* This function computes the optical depth along a ray.
|
||||
* Instead of using classic ray marching, the code is based on Gauss-Laguerre quadrature,
|
||||
* which is designed to compute the integral of f(x)*exp(-x) from 0 to infinity.
|
||||
* This works well here, since the optical depth along the ray tends to decrease exponentially.
|
||||
* By setting f(x) = g(x) exp(x), the exponentials cancel out and we get the integral of g(x).
|
||||
* The nodes and weights used here are the standard n=6 Gauss-Laguerre values, except that
|
||||
* the exp(x) scaling factor is already included in the weights.
|
||||
* The parametrization along the ray is scaled so that the last quadrature node is still within
|
||||
* the atmosphere. */
|
||||
float3 ray_end = atmosphere_intersection(ray_origin, ray_dir);
|
||||
float ray_length = distance(ray_origin, ray_end);
|
||||
|
||||
float3 segment = ray_length * ray_dir;
|
||||
|
||||
/* Instead of tracking the transmission spectrum across all wavelengths directly,
|
||||
* we use the fact that the density always has the same spectrum for each type of
|
||||
* scattering, so we split the density into a constant spectrum and a factor and
|
||||
* only track the factors. */
|
||||
float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
for (int i = 0; i < QUADRATURE_STEPS; i++) {
|
||||
float3 P = ray_origin + QUADRATURE_NODES[i] * segment;
|
||||
|
||||
/* Height above sea level. */
|
||||
float height = len(P) - EARTH_RADIUS;
|
||||
|
||||
float3 density = make_float3(
|
||||
density_rayleigh(height), density_mie(height), density_ozone(height));
|
||||
optical_depth += density * QUADRATURE_WEIGHTS[i];
|
||||
}
|
||||
|
||||
return optical_depth * ray_length;
|
||||
}
|
||||
|
||||
static void single_scattering(float3 ray_dir,
|
||||
float3 sun_dir,
|
||||
float3 ray_origin,
|
||||
float air_density,
|
||||
float aerosol_density,
|
||||
float ozone_density,
|
||||
float *r_spectrum)
|
||||
{
|
||||
/* This code computes single-inscattering along a ray through the atmosphere. */
|
||||
float3 ray_end = atmosphere_intersection(ray_origin, ray_dir);
|
||||
float ray_length = distance(ray_origin, ray_end);
|
||||
|
||||
/* To compute the inscattering, we step along the ray in segments and accumulate
|
||||
* the inscattering as well as the optical depth along each segment. */
|
||||
float segment_length = ray_length / STEPS;
|
||||
float3 segment = segment_length * ray_dir;
|
||||
|
||||
/* Instead of tracking the transmission spectrum across all wavelengths directly,
|
||||
* we use the fact that the density always has the same spectrum for each type of
|
||||
* scattering, so we split the density into a constant spectrum and a factor and
|
||||
* only track the factors. */
|
||||
float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
/* Zero out light accumulation. */
|
||||
for (int wl = 0; wl < NUM_WAVELENGTHS; wl++) {
|
||||
r_spectrum[wl] = 0.0f;
|
||||
}
|
||||
|
||||
/* Phase function for scattering and the density scale factor. */
|
||||
float mu = dot(ray_dir, sun_dir);
|
||||
float3 phase_function = make_float3(phase_rayleigh(mu), phase_mie(mu), 0.0f);
|
||||
float3 density_scale = make_float3(air_density, aerosol_density, ozone_density);
|
||||
|
||||
/* The density and in-scattering of each segment is evaluated at its middle. */
|
||||
float3 P = ray_origin + 0.5f * segment;
|
||||
|
||||
for (int i = 0; i < STEPS; i++) {
|
||||
/* Height above sea level. */
|
||||
float height = len(P) - EARTH_RADIUS;
|
||||
|
||||
/* Evaluate and accumulate optical depth along the ray. */
|
||||
float3 density = density_scale * make_float3(density_rayleigh(height),
|
||||
density_mie(height),
|
||||
density_ozone(height));
|
||||
optical_depth += segment_length * density;
|
||||
|
||||
/* If the Earth isn't in the way, evaluate inscattering from the Sun. */
|
||||
if (!surface_intersection(P, sun_dir)) {
|
||||
float3 light_optical_depth = density_scale * ray_optical_depth(P, sun_dir);
|
||||
float3 total_optical_depth = optical_depth + light_optical_depth;
|
||||
|
||||
/* Attenuation of light. */
|
||||
for (int wl = 0; wl < NUM_WAVELENGTHS; wl++) {
|
||||
float3 extinction_density = total_optical_depth * make_float3(RAYLEIGH_COEFF[wl],
|
||||
1.11f * MIE_COEFF,
|
||||
OZONE_COEFF[wl]);
|
||||
float attenuation = expf(-reduce_add(extinction_density));
|
||||
|
||||
float3 scattering_density = density * make_float3(RAYLEIGH_COEFF[wl], MIE_COEFF, 0.0f);
|
||||
|
||||
/* The total inscattered radiance from one segment is:
|
||||
* Tr(A<->B) * Tr(B<->C) * sigma_s * phase * L * segment_length
|
||||
*
|
||||
* These terms are:
|
||||
* Tr(A<->B): Transmission from start to scattering position (tracked in optical_depth)
|
||||
* Tr(B<->C): Transmission from scattering position to light (computed in
|
||||
* ray_optical_depth) sigma_s: Scattering density phase: Phase function of the scattering
|
||||
* type (Rayleigh or Mie) L: Radiance coming from the light source segment_length: The
|
||||
* length of the segment
|
||||
*
|
||||
* The code here is just that, with a bit of additional optimization to not store full
|
||||
* spectra for the optical depth
|
||||
*/
|
||||
r_spectrum[wl] += attenuation * reduce_add(phase_function * scattering_density) *
|
||||
IRRADIANCE[wl] * segment_length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance along ray. */
|
||||
P += segment;
|
||||
}
|
||||
}
|
||||
|
||||
void SKY_single_scattering_precompute_texture(float *pixels,
|
||||
int stride,
|
||||
int width,
|
||||
int height,
|
||||
float sun_elevation,
|
||||
float altitude,
|
||||
float air_density,
|
||||
float aerosol_density,
|
||||
float ozone_density)
|
||||
{
|
||||
/* Clamp altitude to avoid numerical issues. */
|
||||
altitude = clamp(altitude, 1.0f, 59999.0f);
|
||||
/* Calculate texture pixels. */
|
||||
const int half_width = width / 2;
|
||||
const int half_height = height / 2;
|
||||
const float3 cam_pos = make_float3(0, 0, EARTH_RADIUS + altitude);
|
||||
const float3 sun_dir = geographical_to_direction(sun_elevation, 0.0f);
|
||||
const float longitude_step = M_2PI_F / width;
|
||||
const int rows_per_task = std::max(1024 / width, 1);
|
||||
|
||||
/* Compute Sky in the upper hemisphere. */
|
||||
SKY_parallel_for(half_height, height, rows_per_task, [=](const size_t begin, const size_t end) {
|
||||
for (int y = begin; y < end; y++) {
|
||||
/* Sample more pixels toward the horizon. */
|
||||
float latitude = M_PI_2_F * sqr(float(y) / half_height - 1.0f);
|
||||
float *pixel_row = pixels + (y * width * stride);
|
||||
|
||||
for (int x = 0; x < half_width; x++) {
|
||||
float longitude = longitude_step * x - M_PI_F;
|
||||
float3 dir = geographical_to_direction(latitude, longitude);
|
||||
float spectrum[NUM_WAVELENGTHS];
|
||||
single_scattering(
|
||||
dir, sun_dir, cam_pos, air_density, aerosol_density, ozone_density, spectrum);
|
||||
const float3 xyz = spec_to_xyz(spectrum);
|
||||
|
||||
/* Store pixels. */
|
||||
int pos_x = x * stride;
|
||||
pixel_row[pos_x] = xyz.x;
|
||||
pixel_row[pos_x + 1] = xyz.y;
|
||||
pixel_row[pos_x + 2] = xyz.z;
|
||||
/* Mirror sky. */
|
||||
int mirror_x = (width - x - 1) * stride;
|
||||
pixel_row[mirror_x] = xyz.x;
|
||||
pixel_row[mirror_x + 1] = xyz.y;
|
||||
pixel_row[mirror_x + 2] = xyz.z;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Fill in the lower hemisphere by fading out the horizon. */
|
||||
for (int y = 0; y < half_height; y++) {
|
||||
/* Sample more pixels toward the horizon. */
|
||||
float latitude = M_PI_2_F * sqr(float(y) / half_height - 1.0f);
|
||||
float3 dir = geographical_to_direction(latitude, 0.0f);
|
||||
float fade = 0.0f;
|
||||
if (dir.z < 0.4f) {
|
||||
fade = 1.0f - dir.z * 2.5f;
|
||||
fade = sqr(fade) * fade;
|
||||
}
|
||||
float *pixel_row = pixels + (y * width * stride);
|
||||
float *horizon_row = pixels + (half_height * width * stride);
|
||||
|
||||
for (int x = 0, offset = 0; x < width; x++, offset += stride) {
|
||||
pixel_row[offset + 0] = horizon_row[offset + 0] * fade;
|
||||
pixel_row[offset + 1] = horizon_row[offset + 1] * fade;
|
||||
pixel_row[offset + 2] = horizon_row[offset + 2] * fade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********** Sun ***********/
|
||||
static void sun_radiation(float3 cam_dir,
|
||||
float altitude,
|
||||
float air_density,
|
||||
float aerosol_density,
|
||||
float solid_angle,
|
||||
float *r_spectrum)
|
||||
{
|
||||
float3 cam_pos = make_float3(0, 0, EARTH_RADIUS + altitude);
|
||||
float3 optical_depth = ray_optical_depth(cam_pos, cam_dir);
|
||||
|
||||
/* Compute final spectrum. */
|
||||
for (int i = 0; i < NUM_WAVELENGTHS; i++) {
|
||||
/* Combine spectra and the optical depth into transmittance. */
|
||||
float transmittance = RAYLEIGH_COEFF[i] * optical_depth.x * air_density +
|
||||
1.11f * MIE_COEFF * optical_depth.y * aerosol_density;
|
||||
r_spectrum[i] = IRRADIANCE[i] * expf(-transmittance) / solid_angle;
|
||||
}
|
||||
}
|
||||
|
||||
void SKY_single_scattering_precompute_sun(float sun_elevation,
|
||||
float angular_diameter,
|
||||
float altitude,
|
||||
float air_density,
|
||||
float aerosol_density,
|
||||
float r_pixel_bottom[3],
|
||||
float r_pixel_top[3])
|
||||
{
|
||||
/* Clamp altitude to avoid numerical issues. */
|
||||
altitude = clamp(altitude, 1.0f, 59999.0f);
|
||||
float half_angular = angular_diameter / 2.0f;
|
||||
float solid_angle = M_2PI_F * (1.0f - cosf(half_angular));
|
||||
float spectrum[NUM_WAVELENGTHS];
|
||||
float bottom = sun_elevation - half_angular;
|
||||
float top = sun_elevation + half_angular;
|
||||
float elevation_bottom, elevation_top;
|
||||
float3 pix_bottom, pix_top, sun_dir;
|
||||
|
||||
/* Compute 2 pixels for Sun disc: one is the lowest point of the disc, one is the highest.
|
||||
* Return black pixels if Sun is below horizon. */
|
||||
elevation_bottom = (bottom > 0.0f) ? bottom : 0.0f;
|
||||
elevation_top = (top > 0.0f) ? top : 0.0f;
|
||||
if (elevation_top > 0.0f) {
|
||||
sun_dir = geographical_to_direction(elevation_bottom, 0.0f);
|
||||
sun_radiation(sun_dir, altitude, air_density, aerosol_density, solid_angle, spectrum);
|
||||
pix_bottom = spec_to_xyz(spectrum);
|
||||
sun_dir = geographical_to_direction(elevation_top, 0.0f);
|
||||
sun_radiation(sun_dir, altitude, air_density, aerosol_density, solid_angle, spectrum);
|
||||
pix_top = spec_to_xyz(spectrum);
|
||||
}
|
||||
else {
|
||||
pix_bottom = make_float3(0.0f, 0.0f, 0.0f);
|
||||
pix_top = make_float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/* Store pixels. */
|
||||
r_pixel_bottom[0] = pix_bottom.x;
|
||||
r_pixel_bottom[1] = pix_bottom.y;
|
||||
r_pixel_bottom[2] = pix_bottom.z;
|
||||
r_pixel_top[0] = pix_top.x;
|
||||
r_pixel_top[1] = pix_top.y;
|
||||
r_pixel_top[2] = pix_top.z;
|
||||
}
|
||||
Reference in New Issue
Block a user