diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4aa351..c7a360c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,6 +4,7 @@ option(VERSION_STRING "Project version string" "0.0.0")
project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX)
+
# Find dependency packages
find_package(dr_wav REQUIRED CONFIG)
find_package(stb REQUIRED CONFIG)
diff --git a/src/astro/astro.hpp b/src/astro/astro.hpp
index f8ce409..d030ec4 100644
--- a/src/astro/astro.hpp
+++ b/src/astro/astro.hpp
@@ -24,9 +24,7 @@
namespace astro {}
#include "apparent-size.hpp"
-#include "blackbody.hpp"
#include "coordinates.hpp"
-#include "color-index.hpp"
#include "constants.hpp"
#include "coordinates.hpp"
#include "illuminance.hpp"
diff --git a/src/astro/blackbody.cpp b/src/astro/blackbody.cpp
deleted file mode 100644
index fcc11bd..0000000
--- a/src/astro/blackbody.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2021 Christopher J. Howard
- *
- * This file is part of Antkeeper source code.
- *
- * Antkeeper source code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Antkeeper source code is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Antkeeper source code. If not, see .
- */
-
-#include "blackbody.hpp"
-#include
-
-namespace astro
-{
-
-/// Transforms colors from CIE XYZ to linear RGB
-static constexpr double3x3 xyz_to_rgb =
-{
- 3.2404542, -0.9692660, 0.0556434,
- -1.5371385, 1.8760108, -0.2040259,
- -0.4985314, 0.0415560, 1.0572252
-};
-
-double3 blackbody(double t)
-{
- // Approximate the Planckian locus in CIE 1960 UCS color space (Krystek's algorithm)
- double tt = t * t;
- double u = (0.860117757 + 1.54118254e-4 * t + 1.28641212e-7 * tt) / (1.0 + 8.42420235e-4 * t + 7.08145163e-7 * tt);
- double v = (0.317398726 + 4.22806245e-5 * t + 4.20481691e-8 * tt) / (1.0 - 2.89741816e-5 * t + 1.61456053e-7 * tt);
-
- // CIE 1960 UCS -> CIE xyY, Y = 1
- double2 xyy = double2{3.0 * u, 2.0 * v} / (2.0 * u - 8.0 * v + 4.0);
-
- // CIE xyY -> CIE XYZ
- double3 xyz = {xyy.x / xyy.y, 1.0, (1.0 - xyy.x - xyy.y) / xyy.y};
-
- // CIE XYZ -> linear RGB
- double3 rgb = xyz_to_rgb * xyz;
-
- // Normalize RGB to preserve chromaticity
- return rgb / std::max(rgb.x, std::max(rgb.y, rgb.z));
-}
-
-} // namespace astro
diff --git a/src/astro/blackbody.hpp b/src/astro/blackbody.hpp
deleted file mode 100644
index 2f5d254..0000000
--- a/src/astro/blackbody.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 Christopher J. Howard
- *
- * This file is part of Antkeeper source code.
- *
- * Antkeeper source code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Antkeeper source code is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Antkeeper source code. If not, see .
- */
-
-#ifndef ANTKEEPER_ASTRO_BLACKBODY_HPP
-#define ANTKEEPER_ASTRO_BLACKBODY_HPP
-
-#include "utility/fundamental-types.hpp"
-
-namespace astro
-{
-
-/**
- * Calculates the color of an ideal black-body radiator, given its temperature in Kelvin.
- *
- * @param t Temperature, in Kelvin.
- * @return Correlated color, in linear sRGB.
- *
- * @see https://en.wikipedia.org/wiki/Planckian_locus
- * @see https://en.wikipedia.org/wiki/CIE_1960_color_space
- * @see https://google.github.io/filament/Filament.html
- */
-double3 blackbody(double t);
-
-} // namespace astro
-
-#endif // ANTKEEPER_ASTRO_BLACKBODY_HPP
diff --git a/src/astro/color-index.cpp b/src/astro/color-index.cpp
deleted file mode 100644
index c0e09b3..0000000
--- a/src/astro/color-index.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2021 Christopher J. Howard
- *
- * This file is part of Antkeeper source code.
- *
- * Antkeeper source code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Antkeeper source code is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Antkeeper source code. If not, see .
- */
-
-#include "color-index.hpp"
-
-namespace astro
-{
-
-double bv_to_k(double bv)
-{
- return 4600.0 * (1.0 / (0.92 * bv + 1.7) + 1.0 / (0.92 * bv + 0.62));
-}
-
-} // namespace astro
diff --git a/src/color/acescg.hpp b/src/color/acescg.hpp
index 71c8ec3..1b50f34 100644
--- a/src/color/acescg.hpp
+++ b/src/color/acescg.hpp
@@ -27,6 +27,10 @@ namespace color {
/// Functions which operate in the ACEScg colorspace.
namespace acescg {
+/// CIE chromaticity coordinates of the ACEScg white point (~D60).
+template
+constexpr math::vector2 whitepoint = {0.32168, 0.33767};
+
/**
* Calculates the luminance of an ACEScg color.
*
diff --git a/src/color/cct.hpp b/src/color/cct.hpp
new file mode 100644
index 0000000..c9d676c
--- /dev/null
+++ b/src/color/cct.hpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_COLOR_CCT_HPP
+#define ANTKEEPER_COLOR_CCT_HPP
+
+#include "math/math.hpp"
+#include "ucs.hpp"
+#include "xyy.hpp"
+
+namespace color {
+
+/// Functions which operate on correlated color temperature (CCT).
+namespace cct {
+
+/**
+ * Calculates CIE 1960 UCS colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm.
+ *
+ * @param t Correlated color temperature, in Kelvin.
+ * @return CIE 1960 UCS colorspace chromaticity coordinates.
+ *
+ * @see Krystek, M. (1985), An algorithm to calculate correlated colour temperature. Color Res. Appl., 10: 38-40.
+ */
+template
+math::vector2 to_ucs(T t);
+
+/**
+ * Calculates CIE xyY colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm.
+ *
+ * @param t Correlated color temperature, in Kelvin.
+ * @return CIE xyY color with `Y = 1`.
+ */
+template
+math::vector3 to_xyy(T t);
+
+/**
+ * Calculates CIE XYZ colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm.
+ *
+ * @param t Correlated color temperature, in Kelvin.
+ * @return CIE XYZ color with `Y = 1`.
+ */
+template
+math::vector3 to_xyz(T t);
+
+template
+math::vector2 to_ucs(T t)
+{
+ // Approximate the Planckian locus in CIE 1960 UCS colorspace (Krystek's algorithm)
+ const T tt = t * t;
+ return math::vector2
+ {
+ (T(0.860117757) + T(1.54118254e-4) * t + T(1.28641212e-7) * tt) / (T(1.0) + T(8.42420235e-4) * t + T(7.08145163e-7) * tt),
+ (T(0.317398726) + T(4.22806245e-5) * t + T(4.20481691e-8) * tt) / (T(1.0) - T(2.89741816e-5) * t + T(1.61456053e-7) * tt)
+ };
+}
+
+template
+math::vector3 to_xyy(T t)
+{
+ return ucs::to_xyy(to_ucs(t), T(1.0));
+}
+
+template
+math::vector3 to_xyz(T t)
+{
+ return xyy::to_xyz(to_xyy(t));
+}
+
+} // namespace cct
+} // namespace color
+
+#endif // ANTKEEPER_COLOR_CCT_HPP
diff --git a/src/color/color.hpp b/src/color/color.hpp
index 82f83d6..b5bcf10 100644
--- a/src/color/color.hpp
+++ b/src/color/color.hpp
@@ -24,7 +24,11 @@
namespace color {}
#include "acescg.hpp"
+#include "cct.hpp"
+#include "index.hpp"
#include "srgb.hpp"
+#include "ucs.hpp"
+#include "xyy.hpp"
#include "xyz.hpp"
#endif // ANTKEEPER_COLOR_HPP
diff --git a/src/astro/color-index.hpp b/src/color/index.hpp
similarity index 68%
rename from src/astro/color-index.hpp
rename to src/color/index.hpp
index 0d3dd95..6033035 100644
--- a/src/astro/color-index.hpp
+++ b/src/color/index.hpp
@@ -17,22 +17,32 @@
* along with Antkeeper source code. If not, see .
*/
-#ifndef ANTKEEPER_ASTRO_COLOR_INDEX_HPP
-#define ANTKEEPER_ASTRO_COLOR_INDEX_HPP
+#ifndef ANTKEEPER_COLOR_INDEX_HPP
+#define ANTKEEPER_COLOR_INDEX_HPP
-namespace astro
-{
+namespace color {
+
+/// Functions which operate on color indices.
+namespace index {
/**
* Approximates the temperature of a star, given its B-V index.
*
* @param bv B-V index.
- * @return Temperature, in Kelvin.
+ * @return Correlated color temperature, in Kelvin.
*
* @see Ballesteros, F. J. (2012). "New insights into black bodies". EPL 97 (2012) 34008.
*/
-double bv_to_k(double bv);
+template
+T bv_to_cct(T bv);
+
+template
+T bv_to_cct(T bv)
+{
+ return T(4600.0) * (T(1.0) / (T(0.92) * bv + T(1.7)) + T(1.0) / (T(0.92) * bv + T(0.62)));
+}
-} // namespace astro
+} // namespace index
+} // namespace color
-#endif // ANTKEEPER_ASTRO_COLOR_INDEX_HPP
+#endif // ANTKEEPER_COLOR_INDEX_HPP
diff --git a/src/color/srgb.hpp b/src/color/srgb.hpp
index 98ac149..03561be 100644
--- a/src/color/srgb.hpp
+++ b/src/color/srgb.hpp
@@ -28,6 +28,10 @@ namespace color {
/// Functions which operate in the sRGB colorspace.
namespace srgb {
+/// CIE chromaticity coordinates of the sRGB white point (D65)
+template
+constexpr math::vector2 whitepoint = {0.31271, 0.32902};
+
/**
* Performs the sRGB Electro-Optical Transfer Function (EOTF), also known as the sRGB decoding function.
*
diff --git a/src/color/ucs.hpp b/src/color/ucs.hpp
index 5fbeb5b..a19819f 100644
--- a/src/color/ucs.hpp
+++ b/src/color/ucs.hpp
@@ -28,24 +28,24 @@ namespace color {
namespace ucs {
/**
- * Transforms a CIE 1960 UCS color into the CIE xyY colorspace.
+ * Transforms CIE 1960 UCS chromaticity coordinates into the CIE xyY colorspace.
*
- * @param uv CIE 1960 UCS color coordinates.
+ * @param uv CIE 1960 UCS chromaticity coordinates.
* @param luminance Luminance or `Y` value of the resulting xyY color.
* @return CIE xyY color.
*/
template
-math::vector3 to_xyy(const math::vector2& uv, T luminance);
+math::vector3 to_xyy(const math::vector2& uv, T luminance = T(1.0));
template
-math::vector3 to_xyy(const math::vector2& uv, T luminance);
+math::vector3 to_xyy(const math::vector2& uv, T luminance)
{
- const T inverse_denom = 1.0 / (2.0 * uv[0] - 8.0 * uv[1] + 4.0);
+ const T inverse_denom = T(1.0) / (T(2.0) * uv[0] - T(8.0) * uv[1] + T(4.0));
return math::vector3
{
- (3.0 * uv[0]) * inverse_denom,
- (2.0 * uv[1]) * inverse_denom,
+ (T(3.0) * uv[0]) * inverse_denom,
+ (T(2.0) * uv[1]) * inverse_denom,
luminance
};
}
diff --git a/src/color/xyy.hpp b/src/color/xyy.hpp
index a133df6..f3e9d4d 100644
--- a/src/color/xyy.hpp
+++ b/src/color/xyy.hpp
@@ -63,12 +63,12 @@ inline T luminance(const math::vector3& x)
template
math::vector2 to_ucs(const math::vector3& x)
{
- const T inverse_denom = 1.0 / (-2.0 * x[0] + 12.0 * x[1] + 3.0);
+ const T inverse_denom = T(1.0) / (T(-2.0) * x[0] + T(12.0) * x[1] + T(3.0));
return math::vector2
{
- (4.0 * x[0]) * inverse_denom,
- (6.0 * x[1]) * inverse_denom
+ (T(4.0) * x[0]) * inverse_denom,
+ (T(6.0) * x[1]) * inverse_denom
};
}
@@ -79,7 +79,7 @@ math::vector3 to_xyz(const math::vector3& x)
{
(x[0] * x[2]) / x[1],
x[2],
- ((1.0 - x[0] - x[1]) * x[2]) / x[1]
+ ((T(1.0) - x[0] - x[1]) * x[2]) / x[1]
};
}
diff --git a/src/color/xyz.hpp b/src/color/xyz.hpp
index 6d4350f..976c49d 100644
--- a/src/color/xyz.hpp
+++ b/src/color/xyz.hpp
@@ -63,6 +63,29 @@ math::vector3 to_srgb(const math::vector3& x);
template
math::vector3 to_xyy(const math::vector3& x);
+/// Chromatic Adaptation Transforms (CATs).
+namespace cat {
+
+ /**
+ * Transforms a CIE XYZ color with an ACES (~D60) illuminant to a CIE XYZ color with a D65 illuminant using the Bradford chromatic adaption transform.
+ *
+ * @param x CIE XYZ color with an ACES (~D60) illuminant.
+ * @return CIE XYZ color with a D65 illuminant.
+ */
+ template
+ math::vector3 aces_to_d65(const math::vector3& x);
+
+ /**
+ * Transforms a CIE XYZ color with a D65 illuminant to a CIE XYZ color with an ACES (~D60) illuminant using the Bradford chromatic adaption transform.
+ *
+ * @param x CIE XYZ color with a D65 illuminant.
+ * @return CIE XYZ color with an ACES (~D60) illuminant.
+ */
+ template
+ math::vector3 d65_to_aces(const math::vector3& x);
+
+} // namespace cat
+
template
inline T luminance(const math::vector3& x)
{
@@ -74,9 +97,9 @@ math::vector3 to_acescg(const math::vector3& x)
{
static const math::matrix3 xyz_to_acescg
{{
- { 1.641023379694326 -0.663662858722983 0.011721894328375},
- {-0.324803294184790 1.615331591657338 -0.008284441996237},
- {-0.236424695237612, 0.016756347685530, 0.988394858539022}
+ { 1.641023379694326, -0.663662858722983, 0.011721894328375},
+ {-0.324803294184790, 1.615331591657338, -0.008284441996237},
+ {-0.236424695237612, 0.016756347685530, 0.988394858539022}
}};
return xyz_to_acescg * x;
@@ -108,6 +131,36 @@ math::vector3 to_xyy(const math::vector3& x)
};
}
+namespace cat {
+
+ template
+ math::vector3 aces_to_d65(const math::vector3& x)
+ {
+ static const math::matrix3 cat_aces_to_d65
+ {{
+ { 0.987225397551079, -0.007603864790602, 0.003066041131217},
+ {-0.006114800968213, 1.001874800208719, -0.005084242870792},
+ { 0.015926393295811, 0.005322023893623, 1.081519155692042}
+ }};
+
+ return cat_aces_to_d65 * x;
+ }
+
+ template
+ math::vector3 d65_to_aces(const math::vector3& x)
+ {
+ static const math::matrix3 cat_d65_to_aces
+ {{
+ { 1.013033366661459, 0.007703617525351, -0.002835673220262},
+ { 0.006107049021859, 0.998150224406968, 0.004675010768250},
+ {-0.014947927269674, -0.005025218608126, 0.924644077051100}
+ }};
+
+ return cat_d65_to_aces * x;
+ }
+
+} // namespace cat
+
} // namespace xyz
} // namespace color
diff --git a/src/ecs/systems/astronomy-system.cpp b/src/ecs/systems/astronomy-system.cpp
index 4faff90..d74cb68 100644
--- a/src/ecs/systems/astronomy-system.cpp
+++ b/src/ecs/systems/astronomy-system.cpp
@@ -23,7 +23,6 @@
#include "ecs/components/celestial-body-component.hpp"
#include "ecs/components/transform-component.hpp"
#include "renderer/passes/sky-pass.hpp"
-#include "astro/blackbody.hpp"
#include "color/color.hpp"
#include
@@ -88,9 +87,11 @@ void astronomy_system::update(double t, double dt)
//sun_light->look_at({0, 0, 0}, {0, -1, 0}, {0, 0, 1});
// Set sun color
- float correlated_temperature = 3000.0f + std::sin(spherical.y) * 5000.0f;
- float3 correlated_color = math::type_cast(color::srgb::to_acescg(astro::blackbody(correlated_temperature)));
- sun_light->set_color(correlated_color);
+ float cct = 3000.0f + std::sin(spherical.y) * 5000.0f;
+ float3 color_xyz = color::cct::to_xyz(cct);
+ float3 color_acescg = color::xyz::to_acescg(color_xyz);
+
+ sun_light->set_color(color_acescg);
// Set sun intensity (in lux)
float intensity = std::max(0.0, std::sin(spherical.y) * 108000.0f);
diff --git a/src/game/states/play-state.cpp b/src/game/states/play-state.cpp
index 2c3b7ee..c2c9a19 100644
--- a/src/game/states/play-state.cpp
+++ b/src/game/states/play-state.cpp
@@ -61,7 +61,6 @@
#include "ecs/systems/astronomy-system.hpp"
#include "game/biome.hpp"
#include "utility/fundamental-types.hpp"
-#include "astro/blackbody.hpp"
#include "utility/bit-math.hpp"
#include "genetics/genetics.hpp"
@@ -146,8 +145,8 @@ void play_state_enter(game_context* ctx)
scene::directional_light* sun = new scene::directional_light();
- float3 sun_color = math::type_cast(astro::blackbody(6000.0)); // NOTE: this is linear sRGB, should be ACEScg
- sun->set_color(sun_color);
+ //float3 sun_color = math::type_cast(astro::blackbody(6000.0)); // NOTE: this is linear sRGB, should be ACEScg
+ //sun->set_color(sun_color);
sun->set_intensity(1000.0f);
sun->set_light_texture(resource_manager->load("forest-gobo.tex"));
sun->set_light_texture_scale({2000, 2000});
diff --git a/src/renderer/passes/sky-pass.cpp b/src/renderer/passes/sky-pass.cpp
index b5c4e69..e127084 100644
--- a/src/renderer/passes/sky-pass.cpp
+++ b/src/renderer/passes/sky-pass.cpp
@@ -112,22 +112,22 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe
double3 rectangular = astro::spherical_to_rectangular(spherical);
// Convert color index to color temperature
- double color_temperature = astro::bv_to_k(bv_color);
+ double cct = color::index::bv_to_cct(bv_color);
- // Calculate linear sRGB color from color temperature
- double3 color_srgb = astro::blackbody(color_temperature);
+ // Calculate XYZ color from color temperature
+ double3 color_xyz = color::cct::to_xyz(cct);
- // Transform to ACEScg colorspace
- double3 color_acescg = color::srgb::to_acescg(color_srgb);
+ // Transform XYZ from (assumed) D65 illuminant to ACES illuminant.
+ //color_xyz = color::xyz::cat::d65_to_aces(color_xyz);
- // Calculate color luminance
- double color_luminance = color::acescg::luminance(color_acescg);
+ // Transform XYZ color to ACEScg colorspace
+ double3 color_acescg = color::xyz::to_acescg(color_xyz);
// Convert apparent magnitude to lux
double vmag_lux = astro::vmag_to_lux(vmag);
// Normalized color luminance and scale by apparent magnitude
- double3 scaled_color = color_acescg * ((1.0 / color_luminance) * vmag_lux);
+ double3 scaled_color = color_acescg * vmag_lux;
// Build vertex
*(star_vertex++) = static_cast(rectangular.x);