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);