diff --git a/CMakeLists.txt b/CMakeLists.txt index 73e1a79..21ca183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.25) - option(APPLICATION_NAME "Application name" "Antkeeper") option(APPLICATION_VERSION "Application version string" "0.0.0") option(APPLICATION_AUTHOR "Application author" "C. J. Howard") diff --git a/src/engine/color/aces.hpp b/src/engine/color/aces.hpp index 2981f4d..6c7692d 100644 --- a/src/engine/color/aces.hpp +++ b/src/engine/color/aces.hpp @@ -26,33 +26,33 @@ namespace color { -/// ACES color spaces. -namespace aces { +/// @name ACES color spaces +/// @{ /// CIE xy chromaticity coordinates of the ACES white point (~D60). template -constexpr math::vec2 white_point = {T{0.32168}, T{0.33767}}; +constexpr math::vec2 aces_white_point = {T{0.32168}, T{0.33767}}; /// ACES AP0 color space. template -constexpr rgb::color_space ap0 +constexpr rgb_color_space aces_ap0 ( {T{0.7347}, T{ 0.2653}}, {T{0.0000}, T{ 1.0000}}, {T{0.0001}, T{-0.0770}}, - aces::white_point, + aces_white_point, nullptr, nullptr ); /// ACES AP1 color space. template -constexpr rgb::color_space ap1 +constexpr rgb_color_space aces_ap1 ( {T{0.713}, T{0.293}}, {T{0.165}, T{0.830}}, {T{0.128}, T{0.044}}, - aces::white_point, + aces_white_point, nullptr, nullptr ); @@ -66,7 +66,7 @@ constexpr rgb::color_space ap1 * @return Saturation adjustment matrix. */ template -[[nodiscard]] constexpr math::mat3 adjust_saturation(T s, const math::vec3& to_y) noexcept +[[nodiscard]] constexpr math::mat3 aces_adjust_saturation(T s, const math::vec3& to_y) noexcept { const math::vec3 v = to_y * (T{1} - s); return math::mat3 @@ -79,13 +79,13 @@ template /// ACES AP1 RRT saturation adjustment matrix. template -constexpr math::mat3 ap1_rrt_sat = aces::adjust_saturation(T{0.96}, ap1.to_y); +constexpr math::mat3 aces_ap1_rrt_sat = aces_adjust_saturation(T{0.96}, ap1.to_y); /// ACES AP1 ODT saturation adjustment matrix. template -constexpr math::mat3 ap1_odt_sat = aces::adjust_saturation(T{0.93}, ap1.to_y); +constexpr math::mat3 aces_ap1_odt_sat = aces_adjust_saturation(T{0.93}, ap1.to_y); -} // namespace aces +/// @} } // namespace color diff --git a/src/engine/color/cat.hpp b/src/engine/color/cat.hpp index 07d5435..9201873 100644 --- a/src/engine/color/cat.hpp +++ b/src/engine/color/cat.hpp @@ -25,8 +25,8 @@ namespace color { -/// Chromatic adaption transforms (CAT). -namespace cat { +/// @name Chromatic adaption transforms (CAT) +/// @{ /** * Bradford cone response matrix. @@ -35,7 +35,7 @@ namespace cat { * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html */ template -constexpr math::mat3 bradford = +constexpr math::mat3 bradford_cone_response = { T{ 0.8951}, T{-0.7502}, T{ 0.0389}, T{ 0.2664}, T{ 1.7135}, T{-0.0685}, @@ -48,7 +48,7 @@ constexpr math::mat3 bradford = * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html */ template -constexpr math::mat3 von_kries = +constexpr math::mat3 von_kries_cone_response = { T{ 0.40024}, T{-0.22630}, T{0.00000}, T{ 0.70760}, T{ 1.16532}, T{0.00000}, @@ -61,12 +61,7 @@ constexpr math::mat3 von_kries = * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html */ template -constexpr math::mat3 xyz_scaling = -{ - T{1}, T{0}, T{0}, - T{0}, T{1}, T{0}, - T{0}, T{0}, T{1} -}; +constexpr math::mat3 xyz_scaling_cone_response = math::mat3::identity(); /** * Constructs a chromatic adaptation transform (CAT) matrix. @@ -81,7 +76,7 @@ constexpr math::mat3 xyz_scaling = * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html */ template -[[nodiscard]] constexpr math::mat3 matrix(const math::vec2& w0, const math::vec2& w1, const math::mat3& cone_response = bradford) noexcept +[[nodiscard]] constexpr math::mat3 cat_matrix(const math::vec2& w0, const math::vec2& w1, const math::mat3& cone_response = bradford_cone_response) noexcept { // Convert CIE xy chromaticity coordinates to CIE XYZ colors const math::vec3 w0_xyz = {w0[0] / w0[1], T{1}, (T{1} - w0[0] - w0[1]) / w0[1]}; @@ -101,7 +96,8 @@ template return math::inverse(cone_response) * scale * cone_response; } -} // namespace cat +/// @} + } // namespace color #endif // ANTKEEPER_COLOR_CAT_HPP diff --git a/src/engine/color/cct.hpp b/src/engine/color/cct.hpp index 7bcc410..cd3a86a 100644 --- a/src/engine/color/cct.hpp +++ b/src/engine/color/cct.hpp @@ -26,8 +26,8 @@ namespace color { -/// Correlated color temperature (CCT). -namespace cct { +/// @name Correlated color temperature (CCT) +/// @{ /** * Calculates CIE 1960 UCS colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. @@ -38,7 +38,7 @@ namespace cct { * @see Krystek, M. (1985), An algorithm to calculate correlated colour temperature. Color Res. Appl., 10: 38-40. */ template -[[nodiscard]] math::vec2 to_ucs(T t) noexcept +[[nodiscard]] math::vec2 cct_to_ucs(T t) noexcept { const T tt = t * t; return math::vec2 @@ -55,9 +55,9 @@ template * @return CIE xyY color with `Y = 1`. */ template -math::vec3 to_xyy(T t) +math::vec3 cct_to_xyy(T t) { - return ucs::to_xyy(to_ucs(t), T{1}); + return ucs_to_xyy(cct_to_ucs(t), T{1}); } /** @@ -67,12 +67,11 @@ math::vec3 to_xyy(T t) * @return CIE XYZ color with `Y = 1`. */ template -math::vec3 to_xyz(T t) +math::vec3 cct_to_xyz(T t) { - return xyy::to_xyz(to_xyy(t)); + return xyy_to_xyz(cct_to_xyy(t)); } -} // namespace cct } // namespace color #endif // ANTKEEPER_COLOR_CCT_HPP diff --git a/src/engine/color/color.hpp b/src/engine/color/color.hpp index 8bb3283..c3ea4e6 100644 --- a/src/engine/color/color.hpp +++ b/src/engine/color/color.hpp @@ -20,13 +20,13 @@ #ifndef ANTKEEPER_COLOR_HPP #define ANTKEEPER_COLOR_HPP -/// Color manipulation. +/// Color science. namespace color {} #include #include #include -#include +#include #include #include #include diff --git a/src/engine/color/illuminant.hpp b/src/engine/color/illuminant.hpp deleted file mode 100644 index 633b424..0000000 --- a/src/engine/color/illuminant.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2023 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_ILLUMINANT_HPP -#define ANTKEEPER_COLOR_ILLUMINANT_HPP - -#include - -namespace color { - -/** - * CIE standard illuminants. - * - * @see https://en.wikipedia.org/wiki/Standard_illuminant - */ -namespace illuminant { - -/// CIE 1931 2 Degree Standard Observer illuminants. -namespace deg2 { - - template - constexpr math::vec2 a = {T{0.44757}, T{0.40745}}; - template - constexpr math::vec2 b = {T{0.34842}, T{0.35161}}; - template - constexpr math::vec2 c = {T{0.31006}, T{0.31616}}; - template - constexpr math::vec2 d50 = {T{0.34567}, T{0.35850}}; - template - constexpr math::vec2 d55 = {T{0.33242}, T{0.34743}}; - template - constexpr math::vec2 d65 = {T{0.31271}, T{0.32902}}; - template - constexpr math::vec2 d75 = {T{0.29902}, T{0.31485}}; - template - constexpr math::vec2 d93 = {T{0.28315}, T{0.29711}}; - template - constexpr math::vec2 e = {T{0.33333}, T{0.33333}}; - template - constexpr math::vec2 f1 = {T{0.31310}, T{0.33727}}; - template - constexpr math::vec2 f2 = {T{0.37208}, T{0.37529}}; - template - constexpr math::vec2 f3 = {T{0.40910}, T{0.39430}}; - template - constexpr math::vec2 f4 = {T{0.44018}, T{0.40329}}; - template - constexpr math::vec2 f5 = {T{0.31379}, T{0.34531}}; - template - constexpr math::vec2 f6 = {T{0.37790}, T{0.38835}}; - template - constexpr math::vec2 f7 = {T{0.31292}, T{0.32933}}; - template - constexpr math::vec2 f8 = {T{0.34588}, T{0.35875}}; - template - constexpr math::vec2 f9 = {T{0.37417}, T{0.37281}}; - template - constexpr math::vec2 f10 = {T{0.34609}, T{0.35986}}; - template - constexpr math::vec2 f11 = {T{0.38052}, T{0.37713}}; - template - constexpr math::vec2 f12 = {T{0.43695}, T{0.40441}}; - template - constexpr math::vec2 led_b1 = {T{0.4560}, T{0.4078}}; - template - constexpr math::vec2 led_b2 = {T{0.4357}, T{0.4012}}; - template - constexpr math::vec2 led_b3 = {T{0.3756}, T{0.3723}}; - template - constexpr math::vec2 led_b4 = {T{0.3422}, T{0.3502}}; - template - constexpr math::vec2 led_b5 = {T{0.3118}, T{0.3236}}; - template - constexpr math::vec2 led_bh1 = {T{0.4474}, T{0.4066}}; - template - constexpr math::vec2 led_rgb1 = {T{0.4557}, T{0.4211}}; - template - constexpr math::vec2 led_v1 = {T{0.4560}, T{0.4548}}; - template - constexpr math::vec2 led_v2 = {T{0.3781}, T{0.3775}}; - -} // deg2 - -/// CIE 1964 10 Degree Standard Observer illuminants. -namespace deg10 { - - template - constexpr math::vec2 a = {T{0.45117}, T{0.40594}}; - template - constexpr math::vec2 b = {T{0.34980}, T{0.35270}}; - template - constexpr math::vec2 c = {T{0.31039}, T{0.31905}}; - template - constexpr math::vec2 d50 = {T{0.34773}, T{0.35952}}; - template - constexpr math::vec2 d55 = {T{0.33411}, T{0.34877}}; - template - constexpr math::vec2 d65 = {T{0.31382}, T{0.33100}}; - template - constexpr math::vec2 d75 = {T{0.29968}, T{0.31740}}; - template - constexpr math::vec2 d93 = {T{0.28327}, T{0.30043}}; - template - constexpr math::vec2 e = {T{0.33333}, T{0.33333}}; - template - constexpr math::vec2 f1 = {T{0.31811}, T{0.33559}}; - template - constexpr math::vec2 f2 = {T{0.37925}, T{0.36733}}; - template - constexpr math::vec2 f3 = {T{0.41761}, T{0.38324}}; - template - constexpr math::vec2 f4 = {T{0.44920}, T{0.39074}}; - template - constexpr math::vec2 f5 = {T{0.31975}, T{0.34246}}; - template - constexpr math::vec2 f6 = {T{0.38660}, T{0.37847}}; - template - constexpr math::vec2 f7 = {T{0.31569}, T{0.32960}}; - template - constexpr math::vec2 f8 = {T{0.34902}, T{0.35939}}; - template - constexpr math::vec2 f9 = {T{0.37829}, T{0.37045}}; - template - constexpr math::vec2 f10 = {T{0.35090}, T{0.35444}}; - template - constexpr math::vec2 f11 = {T{0.38541}, T{0.37123}}; - template - constexpr math::vec2 f12 = {T{0.44256}, T{0.39717}}; - -} // namespace deg10 - -} // namespace illuminant - -} // namespace color - -#endif // ANTKEEPER_COLOR_ILLUMINANT_HPP diff --git a/src/engine/color/illuminants.hpp b/src/engine/color/illuminants.hpp new file mode 100644 index 0000000..cdd7c80 --- /dev/null +++ b/src/engine/color/illuminants.hpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2023 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_ILLUMINANTS_HPP +#define ANTKEEPER_COLOR_ILLUMINANTS_HPP + +#include + +namespace color { + +/// @name CIE standard illuminants +/// @{ + +/// @name CIE 1931 2 Degree Standard Observer illuminants +/// @{ + +template +inline constexpr math::vec2 deg2_a = {T{0.44757}, T{0.40745}}; +template +inline constexpr math::vec2 deg2_b = {T{0.34842}, T{0.35161}}; +template +inline constexpr math::vec2 deg2_c = {T{0.31006}, T{0.31616}}; +template +inline constexpr math::vec2 deg2_d50 = {T{0.34567}, T{0.35850}}; +template +inline constexpr math::vec2 deg2_d55 = {T{0.33242}, T{0.34743}}; +template +inline constexpr math::vec2 deg2_d65 = {T{0.31271}, T{0.32902}}; +template +inline constexpr math::vec2 deg2_d75 = {T{0.29902}, T{0.31485}}; +template +inline constexpr math::vec2 deg2_d93 = {T{0.28315}, T{0.29711}}; +template +inline constexpr math::vec2 deg2_e = {T{0.33333}, T{0.33333}}; +template +inline constexpr math::vec2 deg2_f1 = {T{0.31310}, T{0.33727}}; +template +inline constexpr math::vec2 deg2_f2 = {T{0.37208}, T{0.37529}}; +template +inline constexpr math::vec2 deg2_f3 = {T{0.40910}, T{0.39430}}; +template +inline constexpr math::vec2 deg2_f4 = {T{0.44018}, T{0.40329}}; +template +inline constexpr math::vec2 deg2_f5 = {T{0.31379}, T{0.34531}}; +template +inline constexpr math::vec2 deg2_f6 = {T{0.37790}, T{0.38835}}; +template +inline constexpr math::vec2 deg2_f7 = {T{0.31292}, T{0.32933}}; +template +inline constexpr math::vec2 deg2_f8 = {T{0.34588}, T{0.35875}}; +template +inline constexpr math::vec2 deg2_f9 = {T{0.37417}, T{0.37281}}; +template +inline constexpr math::vec2 deg2_f10 = {T{0.34609}, T{0.35986}}; +template +inline constexpr math::vec2 deg2_f11 = {T{0.38052}, T{0.37713}}; +template +inline constexpr math::vec2 deg2_f12 = {T{0.43695}, T{0.40441}}; +template +inline constexpr math::vec2 deg2_led_b1 = {T{0.4560}, T{0.4078}}; +template +inline constexpr math::vec2 deg2_led_b2 = {T{0.4357}, T{0.4012}}; +template +inline constexpr math::vec2 deg2_led_b3 = {T{0.3756}, T{0.3723}}; +template +inline constexpr math::vec2 deg2_led_b4 = {T{0.3422}, T{0.3502}}; +template +inline constexpr math::vec2 deg2_led_b5 = {T{0.3118}, T{0.3236}}; +template +inline constexpr math::vec2 deg2_led_bh1 = {T{0.4474}, T{0.4066}}; +template +inline constexpr math::vec2 deg2_led_rgb1 = {T{0.4557}, T{0.4211}}; +template +inline constexpr math::vec2 deg2_led_v1 = {T{0.4560}, T{0.4548}}; +template +inline constexpr math::vec2 deg2_led_v2 = {T{0.3781}, T{0.3775}}; + +/// @} + +/// @name CIE 1964 10 Degree Standard Observer illuminants +/// @{ + +template +inline constexpr math::vec2 deg10_a = {T{0.45117}, T{0.40594}}; +template +inline constexpr math::vec2 deg10_b = {T{0.34980}, T{0.35270}}; +template +inline constexpr math::vec2 deg10_c = {T{0.31039}, T{0.31905}}; +template +inline constexpr math::vec2 deg10_d50 = {T{0.34773}, T{0.35952}}; +template +inline constexpr math::vec2 deg10_d55 = {T{0.33411}, T{0.34877}}; +template +inline constexpr math::vec2 deg10_d65 = {T{0.31382}, T{0.33100}}; +template +inline constexpr math::vec2 deg10_d75 = {T{0.29968}, T{0.31740}}; +template +inline constexpr math::vec2 deg10_d93 = {T{0.28327}, T{0.30043}}; +template +inline constexpr math::vec2 deg10_e = {T{0.33333}, T{0.33333}}; +template +inline constexpr math::vec2 deg10_f1 = {T{0.31811}, T{0.33559}}; +template +inline constexpr math::vec2 deg10_f2 = {T{0.37925}, T{0.36733}}; +template +inline constexpr math::vec2 deg10_f3 = {T{0.41761}, T{0.38324}}; +template +inline constexpr math::vec2 deg10_f4 = {T{0.44920}, T{0.39074}}; +template +inline constexpr math::vec2 deg10_f5 = {T{0.31975}, T{0.34246}}; +template +inline constexpr math::vec2 deg10_f6 = {T{0.38660}, T{0.37847}}; +template +inline constexpr math::vec2 deg10_f7 = {T{0.31569}, T{0.32960}}; +template +inline constexpr math::vec2 deg10_f8 = {T{0.34902}, T{0.35939}}; +template +inline constexpr math::vec2 deg10_f9 = {T{0.37829}, T{0.37045}}; +template +inline constexpr math::vec2 deg10_f10 = {T{0.35090}, T{0.35444}}; +template +inline constexpr math::vec2 deg10_f11 = {T{0.38541}, T{0.37123}}; +template +inline constexpr math::vec2 deg10_f12 = {T{0.44256}, T{0.39717}}; + +/// @} + +/// @} + +} // namespace color + +#endif // ANTKEEPER_COLOR_ILLUMINANTS_HPP diff --git a/src/engine/color/index.hpp b/src/engine/color/index.hpp index cc7002d..046b1c9 100644 --- a/src/engine/color/index.hpp +++ b/src/engine/color/index.hpp @@ -22,8 +22,8 @@ namespace color { -/// Color indices. -namespace index { +/// @name Color indices. +/// @{ /** * Approximates the temperature of a star, given its B-V index. @@ -39,7 +39,8 @@ template return T{4600} * (T{1} / (T{0.92} * bv + T{1.7}) + T{1} / (T{0.92} * bv + T{0.62})); } -} // namespace index +/// @} + } // namespace color #endif // ANTKEEPER_COLOR_INDEX_HPP diff --git a/src/engine/color/rgb.hpp b/src/engine/color/rgb.hpp index d897150..75b2a65 100644 --- a/src/engine/color/rgb.hpp +++ b/src/engine/color/rgb.hpp @@ -26,8 +26,8 @@ namespace color { -/// RGB color spaces. -namespace rgb { +/// @name RGB color spaces +/// @{ /** * Constructs a matrix which transforms an RGB color into a CIE XYZ color. @@ -43,7 +43,7 @@ namespace rgb { * @see https://mina86.com/2019/srgb-xyz-matrix/ */ template -[[nodiscard]] constexpr math::mat3 to_xyz(const math::vec2& r, const math::vec2& g, const math::vec2& b, const math::vec2& w) +[[nodiscard]] constexpr math::mat3 rgb_to_xyz(const math::vec2& r, const math::vec2& g, const math::vec2& b, const math::vec2& w) { const math::mat3 m = { @@ -66,10 +66,10 @@ template * RGB color space. */ template -struct color_space +struct rgb_color_space { /// Transfer function function pointer type. - typedef math::vec3 (*transfer_function_type)(const math::vec3&); + using transfer_function_type = math::vec3 (*)(const math::vec3&); /// CIE xy chromaticity coordinates of the red primary. const math::vec2 r; @@ -106,14 +106,14 @@ struct color_space * @param b CIE xy chromaticity coordinates of the blue primary. * @param w CIE xy chromaticity coordinates of the white point. */ - constexpr color_space(const math::vec2& r, const math::vec2& g, const math::vec2& b, const math::vec2& w, transfer_function_type eotf, transfer_function_type oetf): + constexpr rgb_color_space(const math::vec2& r, const math::vec2& g, const math::vec2& b, const math::vec2& w, transfer_function_type eotf, transfer_function_type oetf): r(r), g(g), b(b), w(w), eotf(eotf), oetf(oetf), - to_xyz(color::rgb::to_xyz(r, g, b, w)), + to_xyz(rgb_to_xyz(r, g, b, w)), from_xyz(math::inverse(to_xyz)), to_y{to_xyz[0][1], to_xyz[1][1], to_xyz[2][1]} {} @@ -140,12 +140,13 @@ struct color_space * @return Color space transformation matrix. */ template -[[nodiscard]] constexpr math::mat3 to_rgb(const color_space& s0, const color_space& s1, const math::mat3& cone_response = color::cat::bradford) +[[nodiscard]] constexpr math::mat3 rgb_to_rgb(const rgb_color_space& s0, const rgb_color_space& s1, const math::mat3& cone_response = bradford_cone_response) { - return s1.from_xyz * color::cat::matrix(s0.w, s1.w, cone_response) * s0.to_xyz; + return s1.from_xyz * cat_matrix(s0.w, s1.w, cone_response) * s0.to_xyz; } -} // namespace rgb +/// @} + } // namespace color #endif // ANTKEEPER_COLOR_RGB_HPP diff --git a/src/engine/color/srgb.hpp b/src/engine/color/srgb.hpp index 4f670ea..54eba0c 100644 --- a/src/engine/color/srgb.hpp +++ b/src/engine/color/srgb.hpp @@ -21,12 +21,15 @@ #define ANTKEEPER_COLOR_SRGB_HPP #include -#include +#include #include #include namespace color { +/// @name sRGB color space +/// @{ + /** * sRGB opto-electronic transfer function (OETF). Maps a linear sRGB color to a non-linear sRGB signal. * @@ -69,16 +72,18 @@ template /// sRGB color space. template -constexpr rgb::color_space srgb +constexpr rgb_color_space srgb ( {T{0.64}, T{0.33}}, {T{0.30}, T{0.60}}, {T{0.15}, T{0.06}}, - color::illuminant::deg2::d65, + deg2_d65, &srgb_eotf, &srgb_oetf ); +/// @} + } // namespace color #endif // ANTKEEPER_COLOR_SRGB_HPP diff --git a/src/engine/color/ucs.hpp b/src/engine/color/ucs.hpp index 9c29852..ab68f88 100644 --- a/src/engine/color/ucs.hpp +++ b/src/engine/color/ucs.hpp @@ -24,8 +24,8 @@ namespace color { -/// CIE 1960 UCS color space. -namespace ucs { +/// @name CIE 1960 UCS color space +/// @{ /** * Transforms CIE 1960 UCS chromaticity coordinates into the CIE xyY colorspace. @@ -35,13 +35,14 @@ namespace ucs { * @return CIE xyY color. */ template -[[nodiscard]] constexpr math::vec3 to_xyy(const math::vec2& uv, T y = T{1}) noexcept +[[nodiscard]] constexpr math::vec3 ucs_to_xyy(const math::vec2& uv, T y = T{1}) noexcept { const T d = T{1} / (T{2} * uv[0] - T{8} * uv[1] + T{4}); return math::vec3{(T{3} * uv[0]) * d, (T{2} * uv[1]) * d, y}; } -} // namespace ucs +/// @} + } // namespace color #endif // ANTKEEPER_COLOR_UCS_HPP diff --git a/src/engine/color/xyy.hpp b/src/engine/color/xyy.hpp index 4fa8391..e0dc31b 100644 --- a/src/engine/color/xyy.hpp +++ b/src/engine/color/xyy.hpp @@ -24,17 +24,18 @@ namespace color { -/// CIE xyY color space. -namespace xyy { +/// @name CIE xyY color space. +/// @{ /** * Returns the luminance of a CIE xyY color. * * @param x CIE xyY color. + * * @return return Luminance of @p x. */ template -[[nodiscard]] inline constexpr T luminance(const math::vec3& x) noexcept +[[nodiscard]] inline constexpr T xyy_to_luminance(const math::vec3& x) noexcept { return x[2]; } @@ -43,10 +44,11 @@ template * Transforms a CIE xyY color into the CIE 1960 UCS colorspace. * * @param x CIE xyY color. + * * @return CIE 1960 UCS color. */ template -[[nodiscard]] constexpr math::vec2 to_ucs(const math::vec3& x) noexcept +[[nodiscard]] constexpr math::vec2 xyy_to_ucs(const math::vec3& x) noexcept { const T d = (T{1} / (T{-2} * x[0] + T{12} * x[1] + T{3})); return math::vec2{(T{4} * x[0]) * d, (T{6} * x[1]) * d}; @@ -56,15 +58,17 @@ template * Transforms a CIE xyY color into the CIE XYZ colorspace. * * @param x CIE xyY color. + * * @return CIE XYZ color. */ template -[[nodiscard]] constexpr math::vec3 to_xyz(const math::vec3& x) noexcept +[[nodiscard]] constexpr math::vec3 xyy_to_xyz(const math::vec3& x) noexcept { return math::vec3{(x[0] * x[2]) / x[1], x[2], ((T{1} - x[0] - x[1]) * x[2]) / x[1]}; } -} // namespace xyy +/// @} + } // namespace color #endif // ANTKEEPER_COLOR_XYY_HPP diff --git a/src/engine/color/xyz.hpp b/src/engine/color/xyz.hpp index 614c1b0..d127664 100644 --- a/src/engine/color/xyz.hpp +++ b/src/engine/color/xyz.hpp @@ -24,12 +24,8 @@ namespace color { -/** - * CIE XYZ color space. - * - * @see https://en.wikipedia.org/wiki/CIE_1931_color_space - */ -namespace xyz { +/// @name CIE XYZ color space +/// @{ /** * Returns the luminance of a CIE XYZ color. @@ -38,7 +34,7 @@ namespace xyz { * @return return Luminance of @p x. */ template -[[nodiscard]] inline constexpr T luminance(const math::vec3& x) noexcept +[[nodiscard]] inline constexpr T xyz_to_luminance(const math::vec3& x) noexcept { return x[1]; } @@ -50,7 +46,7 @@ template * @return CIE xyY color. */ template -[[nodiscard]] constexpr math::vec3 to_xyy(const math::vec3& x) noexcept +[[nodiscard]] constexpr math::vec3 xyz_to_xyy(const math::vec3& x) noexcept { const T sum = x[0] + x[1] + x[2]; return math::vec3{x[0] / sum, x[1] / sum, x[1]}; @@ -65,7 +61,7 @@ template * @see match(T) */ template -[[nodiscard]] T match_x(T lambda) +[[nodiscard]] T xyz_match_x(T lambda) { const T t0 = (lambda - T{442.0}) * ((lambda < T{442.0}) ? T{0.0624} : T{0.0374}); const T t1 = (lambda - T{599.8}) * ((lambda < T{599.8}) ? T{0.0264} : T{0.0323}); @@ -87,7 +83,7 @@ template * @see match(T) */ template -[[nodiscard]] T match_y(T lambda) +[[nodiscard]] T xyz_match_y(T lambda) { const T t0 = (lambda - T{568.8}) * ((lambda < T{568.8}) ? T{0.0213} : T{0.0247}); const T t1 = (lambda - T{530.9}) * ((lambda < T{530.9}) ? T{0.0613} : T{0.0322}); @@ -107,7 +103,7 @@ template * @see match(T) */ template -[[nodiscard]] T match_z(T lambda) +[[nodiscard]] T xyz_match_z(T lambda) { const T t0 = (lambda - T{437.0}) * ((lambda < T{437.0}) ? T{0.0845} : T{0.0278}); const T t1 = (lambda - T{459.0}) * ((lambda < T{459.0}) ? T{0.0385} : T{0.0725}); @@ -131,17 +127,18 @@ template * @see Wyman, C., Sloan, P.J., & Shirley, P. (2013). Simple Analytic Approximations to the CIE XYZ Color Matching Functions. */ template -[[nodiscard]] math::vec3 match(T lambda) +[[nodiscard]] math::vec3 xyz_match(T lambda) { return math::vec3 { - match_x(lambda), - match_y(lambda), - match_z(lambda) + xyz_match_x(lambda), + xyz_match_y(lambda), + xyz_match_z(lambda) }; } -} // namespace xyz +/// @} + } // namespace color #endif // ANTKEEPER_COLOR_XYZ_HPP diff --git a/src/engine/config.hpp.in b/src/engine/config.hpp.in index e426f7e..1eb55c7 100644 --- a/src/engine/config.hpp.in +++ b/src/engine/config.hpp.in @@ -28,6 +28,7 @@ #endif #include +#include /// Global configuration constants. namespace config { @@ -92,6 +93,19 @@ inline constexpr int opengl_min_stencil_size = 0; /// @} +/// @name Rendering config +/// @{ + +/** + * Scene-linear color space. + * + * @tparam T Scalar type. + */ +template +inline constexpr color::rgb_color_space scene_linear_color_space = color::aces_ap1; + +/// @} + inline constexpr math::vector global_forward = {0.0f, 0.0f, -1.0f}; inline constexpr math::vector global_up = {0.0f, 1.0f, 0.0f}; inline constexpr math::vector global_right = {1.0f, 0.0f, 0.0f}; diff --git a/src/engine/render/passes/shadow-map-pass.cpp b/src/engine/render/passes/cascaded-shadow-map-pass.cpp similarity index 66% rename from src/engine/render/passes/shadow-map-pass.cpp rename to src/engine/render/passes/cascaded-shadow-map-pass.cpp index cce63be..f508f3b 100644 --- a/src/engine/render/passes/shadow-map-pass.cpp +++ b/src/engine/render/passes/cascaded-shadow-map-pass.cpp @@ -17,7 +17,7 @@ * along with Antkeeper source code. If not, see . */ -#include +#include #include #include #include @@ -44,47 +44,40 @@ namespace render { static bool operation_compare(const render::operation* a, const render::operation* b); -shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager): +cascaded_shadow_map_pass::cascaded_shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager): pass(rasterizer, nullptr) { - std::unordered_map definitions; - definitions["VERTEX_POSITION"] = std::to_string(vertex_attribute::position); - definitions["VERTEX_UV"] = std::to_string(vertex_attribute::uv); - definitions["VERTEX_NORMAL"] = std::to_string(vertex_attribute::normal); - definitions["VERTEX_TANGENT"] = std::to_string(vertex_attribute::tangent); - definitions["VERTEX_COLOR"] = std::to_string(vertex_attribute::color); - definitions["VERTEX_BONE_INDEX"] = std::to_string(vertex_attribute::bone_index); - definitions["VERTEX_BONE_WEIGHT"] = std::to_string(vertex_attribute::bone_weight); - definitions["VERTEX_BONE_WEIGHT"] = std::to_string(vertex_attribute::bone_weight); - definitions["MAX_BONE_COUNT"] = std::to_string(64); + // Init shader template definitions + m_shader_template_definitions["VERTEX_POSITION"] = std::to_string(vertex_attribute::position); + m_shader_template_definitions["VERTEX_UV"] = std::to_string(vertex_attribute::uv); + m_shader_template_definitions["VERTEX_NORMAL"] = std::to_string(vertex_attribute::normal); + m_shader_template_definitions["VERTEX_TANGENT"] = std::to_string(vertex_attribute::tangent); + m_shader_template_definitions["VERTEX_COLOR"] = std::to_string(vertex_attribute::color); + m_shader_template_definitions["VERTEX_BONE_INDEX"] = std::to_string(vertex_attribute::bone_index); + m_shader_template_definitions["VERTEX_BONE_WEIGHT"] = std::to_string(vertex_attribute::bone_weight); + m_shader_template_definitions["VERTEX_BONE_WEIGHT"] = std::to_string(vertex_attribute::bone_weight); + m_shader_template_definitions["MAX_BONE_COUNT"] = std::to_string(m_max_bone_count); - // Load unskinned shader template - auto unskinned_shader_template = resource_manager->load("depth-unskinned.glsl"); - - // Build unskinned shader program - unskinned_shader_program = unskinned_shader_template->build(definitions); - if (!unskinned_shader_program->linked()) + // Static mesh shader { - debug::log::error("Failed to build unskinned shadow map shader program: {}", unskinned_shader_program->info()); - debug::log::warning("{}", unskinned_shader_template->configure(gl::shader_stage::vertex)); + // Load static mesh shader template + m_static_mesh_shader_template = resource_manager->load("shadow-cascade-static-mesh.glsl"); + + // Build static mesh shader program + rebuild_static_mesh_shader_program(); } - unskinned_model_view_projection_var = unskinned_shader_program->variable("model_view_projection"); - - // Load skinned shader template - auto skinned_shader_template = resource_manager->load("depth-skinned.glsl"); - // Build skinned shader program - skinned_shader_program = skinned_shader_template->build(definitions); - if (!skinned_shader_program->linked()) + // Skeletal mesh shader { - debug::log::error("Failed to build skinned shadow map shader program: {}", skinned_shader_program->info()); - debug::log::warning("{}", skinned_shader_template->configure(gl::shader_stage::vertex)); + // Load skeletal mesh shader template + m_skeletal_mesh_shader_template = resource_manager->load("shadow-cascade-skeletal-mesh.glsl"); + + // Build static mesh shader program + rebuild_skeletal_mesh_shader_program(); } - skinned_model_view_projection_var = skinned_shader_program->variable("model_view_projection"); - skinned_matrix_palette_var = skinned_shader_program->variable("matrix_palette"); } -void shadow_map_pass::render(render::context& ctx) +void cascaded_shadow_map_pass::render(render::context& ctx) { // For each light const auto& lights = ctx.collection->get_objects(scene::light::object_type_id); @@ -104,29 +97,39 @@ void shadow_map_pass::render(render::context& ctx) continue; } + // Ignore lights that don't share a common layer with the camera + if (!(directional_light.get_layer_mask() & ctx.camera->get_layer_mask())) + { + return; + } + // Ignore improperly-configured lights - if (!directional_light.get_shadow_framebuffer() || !directional_light.get_shadow_cascade_count()) + if (!directional_light.get_shadow_framebuffer()) { continue; } - // Render cascaded shadow maps - render_csm(directional_light, ctx); + // Render shadow atlas + render_atlas(directional_light, ctx); } } -void shadow_map_pass::render_csm(scene::directional_light& light, render::context& ctx) +void cascaded_shadow_map_pass::set_max_bone_count(std::size_t bone_count) { - // Get light layer mask - const auto light_layer_mask = light.get_layer_mask(); - - if (!(light_layer_mask & ctx.camera->get_layer_mask())) + if (m_max_bone_count != bone_count) { - return; + m_max_bone_count = bone_count; + + // Update max bone count shader template definition + m_shader_template_definitions["MAX_BONE_COUNT"] = std::to_string(m_max_bone_count); + + // Rebuild skeletal mesh shader + rebuild_skeletal_mesh_shader_program(); } - - rasterizer->use_framebuffer(*light.get_shadow_framebuffer()); - +} + +void cascaded_shadow_map_pass::render_atlas(scene::directional_light& light, render::context& ctx) +{ // Disable blending glDisable(GL_BLEND); @@ -140,11 +143,19 @@ void shadow_map_pass::render_csm(scene::directional_light& light, render::contex glCullFace(GL_BACK); bool two_sided = false; + // Bind and clear shadow atlas framebuffer + rasterizer->use_framebuffer(*light.get_shadow_framebuffer()); + rasterizer->clear_framebuffer(false, true, false); + + // Get light layer mask + const auto light_layer_mask = light.get_layer_mask(); + // Get camera const scene::camera& camera = *ctx.camera; // Calculate distance to shadow cascade depth clipping planes - const float shadow_clip_far = math::lerp(camera.get_clip_near(), camera.get_clip_far(), light.get_shadow_cascade_coverage()); + const auto shadow_clip_near = camera.get_clip_near(); + const auto shadow_clip_far = camera.get_clip_near() + light.get_shadow_distance(); // Get light shadow cascade distances and matrices const auto cascade_count = light.get_shadow_cascade_count(); @@ -157,11 +168,11 @@ void shadow_map_pass::render_csm(scene::directional_light& light, render::contex { const float weight = static_cast(i + 1) / static_cast(cascade_count); - // Calculate linear and logarithmic distribution distances - const float linear_distance = math::lerp(camera.get_clip_near(), shadow_clip_far, weight); - const float log_distance = math::log_lerp(camera.get_clip_near(), shadow_clip_far, weight); + // Calculate linear and logarithmic split distances + const float linear_distance = math::lerp(shadow_clip_near, shadow_clip_far, weight); + const float log_distance = math::log_lerp(shadow_clip_near, shadow_clip_far, weight); - // Interpolate between linear and logarithmic distribution distances + // Interpolate between linear and logarithmic split distances cascade_distances[i] = math::lerp(linear_distance, log_distance, light.get_shadow_cascade_distribution()); } @@ -276,7 +287,7 @@ void shadow_map_pass::render_csm(scene::directional_light& light, render::contex } // Switch shader programs if necessary - gl::shader_program* shader_program = (operation->matrix_palette.empty()) ? unskinned_shader_program.get() : skinned_shader_program.get(); + gl::shader_program* shader_program = (operation->matrix_palette.empty()) ? m_static_mesh_shader_program.get() : m_skeletal_mesh_shader_program.get(); if (active_shader_program != shader_program) { active_shader_program = shader_program; @@ -287,14 +298,14 @@ void shadow_map_pass::render_csm(scene::directional_light& light, render::contex math::fmat4 model_view_projection = light_view_projection * operation->transform; // Upload operation-dependent parameters to shader program - if (active_shader_program == unskinned_shader_program.get()) + if (active_shader_program == m_static_mesh_shader_program.get()) { - unskinned_model_view_projection_var->update(model_view_projection); + m_static_mesh_model_view_projection_var->update(model_view_projection); } - else if (active_shader_program == skinned_shader_program.get()) + else if (active_shader_program == m_skeletal_mesh_shader_program.get()) { - skinned_model_view_projection_var->update(model_view_projection); - skinned_matrix_palette_var->update(operation->matrix_palette); + m_skeletal_mesh_model_view_projection_var->update(model_view_projection); + m_skeletal_mesh_matrix_palette_var->update(operation->matrix_palette); } // Draw geometry @@ -303,6 +314,40 @@ void shadow_map_pass::render_csm(scene::directional_light& light, render::contex } } +void cascaded_shadow_map_pass::rebuild_static_mesh_shader_program() +{ + m_static_mesh_shader_program = m_static_mesh_shader_template->build(m_shader_template_definitions); + if (!m_static_mesh_shader_program->linked()) + { + debug::log::error("Failed to build cascaded shadow map shader program for static meshes: {}", m_static_mesh_shader_program->info()); + debug::log::warning("{}", m_static_mesh_shader_template->configure(gl::shader_stage::vertex)); + + m_static_mesh_model_view_projection_var = nullptr; + } + else + { + m_static_mesh_model_view_projection_var = m_static_mesh_shader_program->variable("model_view_projection"); + } +} + +void cascaded_shadow_map_pass::rebuild_skeletal_mesh_shader_program() +{ + m_skeletal_mesh_shader_program = m_skeletal_mesh_shader_template->build(m_shader_template_definitions); + if (!m_skeletal_mesh_shader_program->linked()) + { + debug::log::error("Failed to build cascaded shadow map shader program for skeletal meshes: {}", m_skeletal_mesh_shader_program->info()); + debug::log::warning("{}", m_skeletal_mesh_shader_template->configure(gl::shader_stage::vertex)); + + m_skeletal_mesh_model_view_projection_var = nullptr; + m_skeletal_mesh_matrix_palette_var = nullptr; + } + else + { + m_skeletal_mesh_model_view_projection_var = m_skeletal_mesh_shader_program->variable("model_view_projection"); + m_skeletal_mesh_matrix_palette_var = m_skeletal_mesh_shader_program->variable("matrix_palette"); + } +} + bool operation_compare(const render::operation* a, const render::operation* b) { const bool skinned_a = !a->matrix_palette.empty(); diff --git a/src/engine/render/passes/cascaded-shadow-map-pass.hpp b/src/engine/render/passes/cascaded-shadow-map-pass.hpp new file mode 100644 index 0000000..5fcc63d --- /dev/null +++ b/src/engine/render/passes/cascaded-shadow-map-pass.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 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_RENDER_CASCADED_SHADOW_MAP_PASS_HPP +#define ANTKEEPER_RENDER_CASCADED_SHADOW_MAP_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * Renders cascaded shadow maps for directional lights. + */ +class cascaded_shadow_map_pass: public pass +{ +public: + /** + * Constructs a shadow map pass. + * + * @param rasterizer Rasterizer. + * @param framebuffer Shadow map framebuffer. + * @param resource_manage Resource manager. + */ + cascaded_shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); + + /** + * Renders shadow maps for a single camera. + * + * @param ctx Render context. + * @param queue Render queue. + */ + void render(render::context& ctx) override; + + /** + * Sets the maximum bone count for shadow-casting skeletal meshes. + * + * @param bone_count Max bone count. + * + * @warning Triggers rebuilding of skeletal mesh shader. + */ + void set_max_bone_count(std::size_t bone_count); + + /// Returns the maximum bone count for shadow-casting skeletal meshes. + [[nodiscard]] inline constexpr std::size_t get_max_bone_count() const noexcept + { + return m_max_bone_count; + } + +private: + /** + * Renders an atlas of cascaded shadow maps for a single directional light. + * + * @param light Shadow-casting directional light. + * @param ctx Render context. + */ + void render_atlas(scene::directional_light& light, render::context& ctx); + + /// Rebuilds the shader program for static meshes. + void rebuild_static_mesh_shader_program(); + + /// Rebuilds the shader program for skeletal meshes. + void rebuild_skeletal_mesh_shader_program(); + + std::size_t m_max_bone_count{64}; + + std::unordered_map m_shader_template_definitions; + + std::shared_ptr m_static_mesh_shader_template; + std::unique_ptr m_static_mesh_shader_program; + const gl::shader_variable* m_static_mesh_model_view_projection_var; + + std::shared_ptr m_skeletal_mesh_shader_template; + std::unique_ptr m_skeletal_mesh_shader_program; + const gl::shader_variable* m_skeletal_mesh_model_view_projection_var; + const gl::shader_variable* m_skeletal_mesh_matrix_palette_var; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_CASCADED_SHADOW_MAP_PASS_HPP diff --git a/src/engine/render/passes/shadow-map-pass.hpp b/src/engine/render/passes/shadow-map-pass.hpp deleted file mode 100644 index 3cbc172..0000000 --- a/src/engine/render/passes/shadow-map-pass.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2023 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_RENDER_SHADOW_MAP_PASS_HPP -#define ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP - -#include -#include -#include -#include -#include -#include - -class resource_manager; - -namespace render { - -/** - * Renders shadow maps. - */ -class shadow_map_pass: public pass -{ -public: - /** - * Constructs a shadow map pass. - * - * @param rasterizer Rasterizer. - * @param framebuffer Shadow map framebuffer. - * @param resource_manage Resource manager. - */ - shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); - - /** - * Renders shadow maps for a single camera. - * - * @param ctx Render context. - * @param queue Render queue. - */ - void render(render::context& ctx) override; - -private: - /** - * Renders cascaded shadow maps for a single directional light. - * - * @param light Shadow-casting directional light. - * @param ctx Render context. - * @param queue Render queue. - */ - void render_csm(scene::directional_light& light, render::context& ctx); - - std::unique_ptr unskinned_shader_program; - const gl::shader_variable* unskinned_model_view_projection_var; - - std::unique_ptr skinned_shader_program; - const gl::shader_variable* skinned_model_view_projection_var; - const gl::shader_variable* skinned_matrix_palette_var; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP diff --git a/src/engine/render/passes/sky-pass.cpp b/src/engine/render/passes/sky-pass.cpp index cfee738..b5e0a02 100644 --- a/src/engine/render/passes/sky-pass.cpp +++ b/src/engine/render/passes/sky-pass.cpp @@ -255,9 +255,6 @@ void sky_pass::render(render::context& ctx) math::fvec3 moon_illuminance = moon_illuminance_tween.interpolate(ctx.alpha) * camera_exposure; float moon_angular_radius = moon_angular_radius_tween.interpolate(ctx.alpha) * magnification; - float sun_y = color::aces::ap1.luminance(sun_transmitted_illuminance); - float moon_y = color::aces::ap1.luminance(moon_transmitted_illuminance); - // if (math::max(sun_illuminance) > math::max(moon_illuminance)) // { dominant_light_direction = sun_direction; diff --git a/src/engine/scene/directional-light.cpp b/src/engine/scene/directional-light.cpp index 2849497..77744e1 100644 --- a/src/engine/scene/directional-light.cpp +++ b/src/engine/scene/directional-light.cpp @@ -27,6 +27,7 @@ directional_light::directional_light(): m_shadow_scale_bias_matrices(m_shadow_cascade_count) { set_shadow_bias(m_shadow_bias); + update_shadow_cascade_distances(); } void directional_light::set_direction(const math::fvec3& direction) @@ -52,21 +53,24 @@ void directional_light::set_shadow_bias(float bias) noexcept void directional_light::set_shadow_cascade_count(unsigned int count) noexcept { - m_shadow_cascade_count = count; + m_shadow_cascade_count = std::min(std::max(count, 1u), 4u); m_shadow_cascade_distances.resize(m_shadow_cascade_count); m_shadow_cascade_matrices.resize(m_shadow_cascade_count); m_shadow_scale_bias_matrices.resize(m_shadow_cascade_count); update_shadow_scale_bias_matrices(); + update_shadow_cascade_distances(); } -void directional_light::set_shadow_cascade_coverage(float factor) noexcept +void directional_light::set_shadow_distance(float distance) noexcept { - m_shadow_cascade_coverage = factor; + m_shadow_distance = distance; + update_shadow_cascade_distances(); } void directional_light::set_shadow_cascade_distribution(float weight) noexcept { m_shadow_cascade_distribution = weight; + update_shadow_cascade_distances(); } void directional_light::transformed() @@ -102,4 +106,27 @@ void directional_light::update_shadow_scale_bias_matrices() } } +void directional_light::update_shadow_cascade_distances() +{ + if (!m_shadow_cascade_count) + { + return; + } + + m_shadow_cascade_distances[m_shadow_cascade_count - 1] = m_shadow_distance; + for (unsigned int i = 0; i < m_shadow_cascade_count - 1; ++i) + { + const auto weight = static_cast(i + 1) / static_cast(m_shadow_cascade_count); + + // Calculate linear and logarithmic distribution distances + const auto linear_distance = m_shadow_distance * weight; + // const auto log_distance = math::log_lerp(0.0f, m_shadow_distance, weight); + + // Interpolate between linear and logarithmic distribution distances + // cascade_distances[i] = math::lerp(linear_distance, log_distance, light.get_shadow_cascade_distribution()); + + m_shadow_cascade_distances[i] = linear_distance; + } +} + } // namespace scene diff --git a/src/engine/scene/directional-light.hpp b/src/engine/scene/directional-light.hpp index 922bdbe..45bdacc 100644 --- a/src/engine/scene/directional-light.hpp +++ b/src/engine/scene/directional-light.hpp @@ -129,16 +129,18 @@ public: /** * Sets the number of shadow cascades. * - * @param count Number of shadow cascades. + * @param count Number of shadow cascades, on `[1, 4]`. + * + * @note The number of shadow cascades will be clamped to `[1, 4]`. */ void set_shadow_cascade_count(unsigned int count) noexcept; /** - * Sets the shadow cascade coverage factor. + * Sets the distance from the camera up to which shadows are visible. * - * @param factor Percentage of the view frustum clipping range covered by shadow cascades. A value of `1.0` results in full coverage of the view frustum clipping range, `0.5` results in coverage of half of the clipping range, etc. + * @param distance Shadow distance. */ - void set_shadow_cascade_coverage(float factor) noexcept; + void set_shadow_distance(float distance) noexcept; /** * Sets the shadow cascade distribution. @@ -148,37 +150,37 @@ public: void set_shadow_cascade_distribution(float weight) noexcept; /// Returns `true` if the light casts shadows, `false` otherwise. - [[nodiscard]] inline bool is_shadow_caster() const noexcept + [[nodiscard]] inline constexpr bool is_shadow_caster() const noexcept { return m_shadow_caster; } /// Returns the shadow map framebuffer, of `nullptr` if no shadow map framebuffer is set. - [[nodiscard]] inline const std::shared_ptr& get_shadow_framebuffer() const noexcept + [[nodiscard]] inline constexpr const std::shared_ptr& get_shadow_framebuffer() const noexcept { return m_shadow_framebuffer; } /// Returns the shadow bias factor. - [[nodiscard]] inline float get_shadow_bias() const noexcept + [[nodiscard]] inline constexpr float get_shadow_bias() const noexcept { return m_shadow_bias; } /// Returns the number of shadow cascades. - [[nodiscard]] inline unsigned int get_shadow_cascade_count() const noexcept + [[nodiscard]] inline constexpr unsigned int get_shadow_cascade_count() const noexcept { return m_shadow_cascade_count; } - /// Returns the shadow cascade coverage factor. - [[nodiscard]] inline float get_shadow_cascade_coverage() const noexcept + /// Returns the distance from the camera up to which shadows are visible. + [[nodiscard]] inline constexpr float get_shadow_distance() const noexcept { - return m_shadow_cascade_coverage; + return m_shadow_distance; } /// Returns the shadow cascade distribution weight. - [[nodiscard]] inline float get_shadow_cascade_distribution() const noexcept + [[nodiscard]] inline constexpr float get_shadow_cascade_distribution() const noexcept { return m_shadow_cascade_distribution; } @@ -220,6 +222,7 @@ private: void color_updated(); void illuminance_updated(); void update_shadow_scale_bias_matrices(); + void update_shadow_cascade_distances(); math::fvec3 m_direction{0.0f, 0.0f, -1.0f}; math::fvec3 m_color{1.0f, 1.0f, 1.0f}; @@ -228,9 +231,9 @@ private: bool m_shadow_caster{false}; std::shared_ptr m_shadow_framebuffer{nullptr}; - float m_shadow_bias{0.001f}; + float m_shadow_bias{0.005f}; unsigned int m_shadow_cascade_count{4}; - float m_shadow_cascade_coverage{1.0f}; + float m_shadow_distance{1000.0f}; float m_shadow_cascade_distribution{0.8f}; std::vector m_shadow_cascade_distances; std::vector m_shadow_cascade_matrices; diff --git a/src/engine/scene/light.cpp b/src/engine/scene/light.cpp index 7204e20..db3bb86 100644 --- a/src/engine/scene/light.cpp +++ b/src/engine/scene/light.cpp @@ -18,9 +18,22 @@ */ #include +#include +#include namespace scene { +void light::set_color(const math::fvec3& color) +{ + m_color = color; + color_updated(); +} + +void light::set_color_temperature(float temperature) +{ + set_color(config::scene_linear_color_space.from_xyz * color::cct_to_xyz(temperature)); +} + void light::transformed() { m_bounds = {get_translation(), get_translation()}; diff --git a/src/engine/scene/light.hpp b/src/engine/scene/light.hpp index 82f8664..39c7b3c 100644 --- a/src/engine/scene/light.hpp +++ b/src/engine/scene/light.hpp @@ -38,10 +38,37 @@ public: { return m_bounds; } + + /** + * Sets the color of the light. + * + * @param color Scene-linear RGB color, on `[0, 1]`. + */ + void set_color(const math::fvec3& color); + + /** + * Sets the color of the light from a color temperature. + * + * @param temperature Color temperature, in Kelvin. + */ + void set_color_temperature(float temperature); + + /// Returns the scene-linear RGB color of the light. + [[nodiscard]] inline constexpr const math::fvec3& get_color() const noexcept + { + return m_color; + } + +protected: + /// Called each time the light color is modified. + inline virtual void color_updated() {}; private: void transformed() override; + aabb_type m_bounds{}; + + math::fvec3 m_color{1.0f, 1.0f, 1.0f}; }; } // namespace scene diff --git a/src/engine/scene/point-light.cpp b/src/engine/scene/point-light.cpp index f216a5c..018ad61 100644 --- a/src/engine/scene/point-light.cpp +++ b/src/engine/scene/point-light.cpp @@ -23,12 +23,12 @@ namespace scene { void point_light::color_updated() { - m_colored_luminous_flux = m_color * m_luminous_flux; + m_colored_luminous_flux = get_color() * m_luminous_flux; } void point_light::luminous_flux_updated() { - m_colored_luminous_flux = m_color * m_luminous_flux; + m_colored_luminous_flux = get_color() * m_luminous_flux; } } // namespace scene diff --git a/src/engine/scene/point-light.hpp b/src/engine/scene/point-light.hpp index e357614..816b048 100644 --- a/src/engine/scene/point-light.hpp +++ b/src/engine/scene/point-light.hpp @@ -37,17 +37,6 @@ public: return light_type::point; } - /** - * Sets the color of the light. - * - * @param color Light color. - */ - inline void set_color(const math::fvec3& color) noexcept - { - m_color = color; - color_updated(); - } - /** * Sets the luminous flux of the light. * @@ -59,29 +48,22 @@ public: luminous_flux_updated(); } - /// Returns the color of the light. - [[nodiscard]] inline const math::fvec3& get_color() const noexcept - { - return m_color; - } - /// Returns the luminous flux of the light. - [[nodiscard]] inline float get_luminous_flux() const noexcept + [[nodiscard]] inline constexpr float get_luminous_flux() const noexcept { return m_luminous_flux; } - /// Returns the color-modulated luminous flux of light. - [[nodiscard]] inline const math::fvec3& get_colored_luminous_flux() const noexcept + /// Returns the color-modulated luminous flux of the light. + [[nodiscard]] inline constexpr const math::fvec3& get_colored_luminous_flux() const noexcept { return m_colored_luminous_flux; } private: - void color_updated(); - void luminous_flux_updated(); + void color_updated() override; + void luminous_flux_updated() noexcept; - math::fvec3 m_color{1.0f, 1.0f, 1.0f}; float m_luminous_flux{}; math::fvec3 m_colored_luminous_flux{}; }; diff --git a/src/engine/scene/rectangle-light.cpp b/src/engine/scene/rectangle-light.cpp index c819d3c..0a87b66 100644 --- a/src/engine/scene/rectangle-light.cpp +++ b/src/engine/scene/rectangle-light.cpp @@ -27,6 +27,11 @@ rectangle_light::rectangle_light() transformed(); } +void rectangle_light::set_size(const math::fvec2& size) +{ + set_scale({size.x(), size.y(), 1.0f}); +} + void rectangle_light::transformed() { const auto& transform = get_transform(); @@ -42,39 +47,39 @@ void rectangle_light::transformed() m_corners[3] = transform * math::fvec3{ 0.5f, -0.5f, 0.0f}; // Update area - m_area = get_scale().x() * get_scale().z(); + m_area = get_scale().x() * get_scale().y(); area_updated(); } -void rectangle_light::area_updated() +void rectangle_light::color_updated() { - // Calculate luminance from luminous flux - m_luminance = m_luminous_flux / (m_area * math::pi); - m_colored_luminance = m_color * m_luminance; + m_colored_luminous_flux = get_color() * m_luminous_flux; + m_colored_luminance = get_color() * m_luminance; } -void rectangle_light::color_updated() +void rectangle_light::area_updated() noexcept { - m_colored_luminous_flux = m_color * m_luminous_flux; - m_colored_luminance = m_color * m_luminance; + // Calculate luminance from luminous flux + m_luminance = m_luminous_flux / (m_area * math::pi); + m_colored_luminance = get_color() * m_luminance; } -void rectangle_light::luminous_flux_updated() +void rectangle_light::luminous_flux_updated() noexcept { - m_colored_luminous_flux = m_color * m_luminous_flux; + m_colored_luminous_flux = get_color() * m_luminous_flux; // Calculate luminance from luminous flux m_luminance = m_luminous_flux / (m_area * math::pi); - m_colored_luminance = m_color * m_luminance; + m_colored_luminance = get_color() * m_luminance; } -void rectangle_light::luminance_updated() +void rectangle_light::luminance_updated() noexcept { - m_colored_luminance = m_color * m_luminance; + m_colored_luminance = get_color() * m_luminance; // Calculate luminous flux from luminance m_luminous_flux = m_luminance * (m_area * math::pi); - m_colored_luminous_flux = m_color * m_luminous_flux; + m_colored_luminous_flux = get_color() * m_luminous_flux; } } // namespace scene diff --git a/src/engine/scene/rectangle-light.hpp b/src/engine/scene/rectangle-light.hpp index b55cc56..17a113c 100644 --- a/src/engine/scene/rectangle-light.hpp +++ b/src/engine/scene/rectangle-light.hpp @@ -31,6 +31,7 @@ namespace scene { class rectangle_light: public light { public: + /// Constructs a rectangular area light. rectangle_light(); /// Returns light_type::rectangle. @@ -40,15 +41,11 @@ public: } /** - * Sets the color of the light. + * Sets the size of the light. * - * @param color Light color. + * @param size Dimensions of the light. */ - inline void set_color(const math::fvec3& color) noexcept - { - m_color = color; - color_updated(); - } + void set_size(const math::fvec2& size); /** * Sets the luminous flux of the light. @@ -72,56 +69,55 @@ public: luminance_updated(); } - /// Returns the color of the light. - [[nodiscard]] inline const math::fvec3& get_color() const noexcept + /// Returns the dimensions of the light. + [[nodiscard]] inline constexpr math::fvec2 get_size() const noexcept { - return m_color; + return math::fvec2(get_scale()); } /// Returns the luminous flux of the light. - [[nodiscard]] inline float get_luminous_flux() const noexcept + [[nodiscard]] inline constexpr float get_luminous_flux() const noexcept { return m_luminous_flux; } /// Returns the color-modulated luminous flux of the light. - [[nodiscard]] inline const math::fvec3& get_colored_luminous_flux() const noexcept + [[nodiscard]] inline constexpr const math::fvec3& get_colored_luminous_flux() const noexcept { return m_colored_luminous_flux; } /// Returns the luminance of the light. - [[nodiscard]] inline float get_luminance() const noexcept + [[nodiscard]] inline constexpr float get_luminance() const noexcept { return m_luminance; } /// Returns the color-modulated luminance of the light. - [[nodiscard]] inline const math::fvec3& get_colored_luminance() const noexcept + [[nodiscard]] inline constexpr const math::fvec3& get_colored_luminance() const noexcept { return m_colored_luminance; } - /// Returns the positions of the light corners. - [[nodiscard]] inline std::span get_corners() const noexcept + /// Returns the world-space positions of the light corners. + [[nodiscard]] inline constexpr std::span get_corners() const noexcept { return m_corners; } private: void transformed() override; - void area_updated(); - void color_updated(); - void luminous_flux_updated(); - void luminance_updated(); + void color_updated() override; + void area_updated() noexcept; + void luminous_flux_updated() noexcept; + void luminance_updated() noexcept; float m_area{1.0f}; math::fvec3 m_corners[4]; - math::fvec3 m_color{1.0f, 1.0f, 1.0f}; float m_luminous_flux{}; math::fvec3 m_colored_luminous_flux{}; float m_luminance{}; - math::fvec3 m_colored_luminance; + math::fvec3 m_colored_luminance{}; }; } // namespace scene diff --git a/src/game/game.cpp b/src/game/game.cpp index d99cc2a..a4ddd9b 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -85,7 +85,7 @@ #include #include #include -#include +#include #include #include #include @@ -735,11 +735,7 @@ void game::setup_rendering() // Setup surface compositor { - surface_shadow_map_clear_pass = std::make_unique(window->get_rasterizer(), shadow_map_framebuffer.get()); - surface_shadow_map_clear_pass->set_cleared_buffers(false, true, false); - surface_shadow_map_clear_pass->set_clear_depth(0.0f); - - surface_shadow_map_pass = std::make_unique(window->get_rasterizer(), resource_manager.get()); + surface_cascaded_shadow_map_pass = std::make_unique(window->get_rasterizer(), resource_manager.get()); surface_clear_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get()); surface_clear_pass->set_clear_color({0.0f, 0.0f, 0.0f, 1.0f}); @@ -758,8 +754,7 @@ void game::setup_rendering() surface_outline_pass->set_outline_color(math::fvec4{1.0f, 1.0f, 1.0f, 1.0f}); surface_compositor = std::make_unique(); - surface_compositor->add_pass(surface_shadow_map_clear_pass.get()); - surface_compositor->add_pass(surface_shadow_map_pass.get()); + surface_compositor->add_pass(surface_cascaded_shadow_map_pass.get()); surface_compositor->add_pass(surface_clear_pass.get()); surface_compositor->add_pass(sky_pass.get()); surface_compositor->add_pass(surface_material_pass.get()); @@ -812,7 +807,7 @@ void game::setup_scenes() // Allocate and init surface camera surface_camera = std::make_shared(); - surface_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.1f, 1000.0f); + surface_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.5f, 1000.0f); surface_camera->set_compositor(surface_compositor.get()); surface_camera->set_composite_index(0); @@ -1091,7 +1086,6 @@ void game::setup_systems() // Setup blackbody system blackbody_system = std::make_unique<::blackbody_system>(*entity_registry); - blackbody_system->set_illuminant(color::illuminant::deg2::d55); // Setup atmosphere system atmosphere_system = std::make_unique<::atmosphere_system>(*entity_registry); diff --git a/src/game/game.hpp b/src/game/game.hpp index 490cbba..3bebb97 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -90,7 +90,7 @@ namespace render class material_pass; class renderer; class outline_pass; - class shadow_map_pass; + class cascaded_shadow_map_pass; class simple_render_pass; class sky_pass; } @@ -333,8 +333,7 @@ public: std::unique_ptr underground_clear_pass; std::unique_ptr underground_material_pass; std::unique_ptr underground_compositor; - std::unique_ptr surface_shadow_map_clear_pass; - std::unique_ptr surface_shadow_map_pass; + std::unique_ptr surface_cascaded_shadow_map_pass; std::unique_ptr surface_clear_pass; std::unique_ptr sky_pass; std::unique_ptr surface_material_pass; diff --git a/src/game/states/experiments/treadmill-experiment-state.cpp b/src/game/states/experiments/treadmill-experiment-state.cpp index edfbe11..d41c211 100644 --- a/src/game/states/experiments/treadmill-experiment-state.cpp +++ b/src/game/states/experiments/treadmill-experiment-state.cpp @@ -114,74 +114,13 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx): std::shared_ptr worker_model = ant_morphogenesis(*worker_phenome); debug::log::trace("Generated worker model"); - // Create directional light - // ctx.underground_directional_light = std::make_unique(); - // ctx.underground_directional_light->set_color({1.0f, 1.0f, 1.0f}); - // ctx.underground_directional_light->set_illuminance(2.0f); - // ctx.underground_directional_light->set_direction(math::normalize(math::fvec3{0, -1, 0})); - // ctx.underground_directional_light->set_shadow_caster(true); - // ctx.underground_directional_light->set_shadow_framebuffer(ctx.shadow_map_framebuffer); - // ctx.underground_directional_light->set_shadow_bias(0.005f); - // ctx.underground_directional_light->set_shadow_cascade_count(4); - // ctx.underground_directional_light->set_shadow_cascade_coverage(0.15f); - // ctx.underground_directional_light->set_shadow_cascade_distribution(0.8f); - // ctx.underground_scene->add_object(*ctx.underground_directional_light); - - // ctx.underground_clear_pass->set_clear_color({0.214f, 0.214f, 0.214f, 1.0f}); - // ctx.underground_clear_pass->set_clear_color({}); - // light_probe = std::make_shared(); - // light_probe->set_luminance_texture(ctx.resource_manager->load("grey-furnace.tex")); - // ctx.underground_scene->add_object(*light_probe); - - //const float color_temperature = 5000.0f; - //const math::fvec3 light_color = color::aces::ap1.from_xyz * color::cat::matrix(color::illuminant::deg2::d50, color::aces::white_point) * color::cct::to_xyz(color_temperature); - // const math::fvec3 light_color{1.0f, 1.0f, 1.0f}; - - // Create rectangle light - // ctx.underground_rectangle_light = std::make_unique(); - // ctx.underground_rectangle_light->set_color(light_color); - // ctx.underground_rectangle_light->set_luminous_flux(1500.0f); - // ctx.underground_rectangle_light->set_translation({0.0f, 10.0f, 0.0f}); - // ctx.underground_rectangle_light->set_rotation(math::fquat::rotate_x(math::radians(90.0f))); - // ctx.underground_rectangle_light->set_scale(7.0f); - // ctx.underground_scene->add_object(*ctx.underground_rectangle_light); - - // Create light rectangle - // auto light_rectangle_model = ctx.resource_manager->load("light-rectangle.mdl"); - // auto light_rectangle_material = std::make_shared(*light_rectangle_model->get_groups().front().material); - // light_rectangle_emissive = std::static_pointer_cast(light_rectangle_material->get_variable("emissive")); - // light_rectangle_emissive->set(ctx.underground_rectangle_light->get_colored_luminance()); - // auto light_rectangle_static_mesh = std::make_shared(light_rectangle_model); - // light_rectangle_static_mesh->set_material(0, light_rectangle_material); - - // auto light_rectangle_eid = ctx.entity_registry->create(); - // ctx.entity_registry->emplace(light_rectangle_eid, std::move(light_rectangle_static_mesh), std::uint8_t{1}); - // ctx.entity_registry->patch - // ( - // light_rectangle_eid, - // [&](auto& component) - // { - // component.object->set_transform(ctx.underground_rectangle_light->get_transform()); - // } - // ); - - // Create nest exterior - // { - // scene_component nest_exterior_scene_component; - // nest_exterior_scene_component.object = std::make_shared(ctx.resource_manager->load("round-petri-dish-nest-100mm-exterior.mdl")); - // nest_exterior_scene_component.layer_mask = 1; - - // auto nest_exterior_eid = ctx.entity_registry->create(); - // ctx.entity_registry->emplace(nest_exterior_eid, std::move(nest_exterior_scene_component)); - // } - // Create nest exterior { scene_component nest_exterior_scene_component; - nest_exterior_scene_component.object = std::make_shared(ctx.resource_manager->load("sphere-nest-100mm-exterior.mdl")); + nest_exterior_scene_component.object = std::make_shared(ctx.resource_manager->load("cube-nest-200mm-exterior.mdl")); nest_exterior_scene_component.layer_mask = 1; - auto nest_exterior_mesh = ctx.resource_manager->load("sphere-nest-100mm-exterior.msh"); + auto nest_exterior_mesh = ctx.resource_manager->load("cube-nest-200mm-exterior.msh"); auto nest_exterior_rigid_body = std::make_unique(); nest_exterior_rigid_body->set_mass(0.0f); @@ -195,11 +134,11 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx): // Create nest interior { scene_component nest_interior_scene_component; - nest_interior_scene_component.object = std::make_shared(ctx.resource_manager->load("sphere-nest-100mm-interior.mdl")); + nest_interior_scene_component.object = std::make_shared(ctx.resource_manager->load("soil-nest.mdl")); nest_interior_scene_component.object->set_layer_mask(0b10); nest_interior_scene_component.layer_mask = 1; - auto nest_interior_mesh = ctx.resource_manager->load("sphere-nest-100mm-interior.msh"); + auto nest_interior_mesh = ctx.resource_manager->load("soil-nest.msh"); auto nest_interior_rigid_body = std::make_unique(); nest_interior_rigid_body->set_mass(0.0f); @@ -215,10 +154,11 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx): // Create rectangle light { area_light = std::make_unique(); - area_light->set_luminous_flux(5000000.0f); + area_light->set_luminous_flux(12.57f * 100.0f); + area_light->set_color_temperature(20000.0f); area_light->set_translation({0.0f, 0.0f, 0.0f}); area_light->set_rotation(math::fquat::rotate_x(math::radians(90.0f))); - area_light->set_scale(5.0f); + area_light->set_size({1.0f, 2.0f}); area_light->set_layer_mask(0b10); ctx.surface_scene->add_object(*area_light); diff --git a/src/game/states/nest-view-state.cpp b/src/game/states/nest-view-state.cpp index 3597644..62d54aa 100644 --- a/src/game/states/nest-view-state.cpp +++ b/src/game/states/nest-view-state.cpp @@ -132,8 +132,6 @@ nest_view_state::nest_view_state(::game& ctx): light_probe->set_luminance_texture(ctx.resource_manager->load("grey-furnace.tex")); ctx.underground_scene->add_object(*light_probe); - //const float color_temperature = 5000.0f; - //const math::fvec3 light_color = color::aces::ap1.from_xyz * color::cat::matrix(color::illuminant::deg2::d50, color::aces::white_point) * color::cct::to_xyz(color_temperature); const math::fvec3 light_color{1.0f, 1.0f, 1.0f}; // Create rectangle light diff --git a/src/game/systems/blackbody-system.cpp b/src/game/systems/blackbody-system.cpp index 2402817..86160d1 100644 --- a/src/game/systems/blackbody-system.cpp +++ b/src/game/systems/blackbody-system.cpp @@ -18,10 +18,11 @@ */ #include "game/systems/blackbody-system.hpp" -#include #include #include #include +#include +#include #include blackbody_system::blackbody_system(entity::registry& registry): @@ -31,9 +32,6 @@ blackbody_system::blackbody_system(entity::registry& registry): m_visible_wavelengths_nm.resize(780 - 280); std::iota(m_visible_wavelengths_nm.begin(), m_visible_wavelengths_nm.end(), 280); - // Set illuminant - set_illuminant(color::illuminant::deg2::d50); - registry.on_construct<::blackbody_component>().connect<&blackbody_system::on_blackbody_construct>(this); registry.on_update<::blackbody_component>().connect<&blackbody_system::on_blackbody_update>(this); } @@ -47,12 +45,6 @@ blackbody_system::~blackbody_system() void blackbody_system::update(float t, float dt) {} -void blackbody_system::set_illuminant(const math::vec2& illuminant) -{ - m_illuminant = illuminant; - m_xyz_to_rgb = color::aces::ap1.from_xyz * color::cat::matrix(m_illuminant, color::aces::white_point); -} - void blackbody_system::update_blackbody(entity::id entity_id) { // Get blackbody component @@ -71,10 +63,11 @@ void blackbody_system::update_blackbody(entity::id entity_id) const double spectral_luminance = spectral_radiance * 1e-9 * physics::light::max_luminous_efficacy; // Calculate the XYZ color of the wavelength using CIE color matching functions then transform to RGB - const math::dvec3 rgb_color = m_xyz_to_rgb * color::xyz::match(wavelength_nm); + const auto color_xyz = color::xyz_match(wavelength_nm); + const auto color_rgb = config::scene_linear_color_space.from_xyz * color_xyz; // Scale RGB color by spectral luminance - return rgb_color * spectral_luminance; + return color_rgb * spectral_luminance; }; // Integrate the blackbody RGB spectral luminance over wavelengths in the visible spectrum diff --git a/src/game/systems/blackbody-system.hpp b/src/game/systems/blackbody-system.hpp index 2f0f254..78161d9 100644 --- a/src/game/systems/blackbody-system.hpp +++ b/src/game/systems/blackbody-system.hpp @@ -39,13 +39,6 @@ public: void update(float t, float dt) override; - /** - * Sets the blackbody illuminant. - * - * @param illuminant CIE chromaticity coordinates of an illuminant. - */ - void set_illuminant(const math::vec2& illuminant); - private: void update_blackbody(entity::id entity_id); @@ -53,8 +46,6 @@ private: void on_blackbody_update(entity::registry& registry, entity::id entity_id); std::vector m_visible_wavelengths_nm; - math::vec2 m_illuminant; - math::mat3 m_xyz_to_rgb; }; diff --git a/src/game/world.cpp b/src/game/world.cpp index e974d14..32ad064 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -55,7 +55,6 @@ #include #include #include -#include #include #include #include @@ -265,13 +264,13 @@ void create_stars(::game& ctx) math::fvec3 position = physics::orbit::frame::bci::cartesian(math::fvec3{1.0f, dec, ra}); // Convert color index to color temperature - float cct = color::index::bv_to_cct(bv); + float cct = color::bv_to_cct(bv); // Calculate XYZ color from color temperature - math::fvec3 color_xyz = color::cct::to_xyz(cct); + math::fvec3 color_xyz = color::cct_to_xyz(cct); - // Transform XYZ color to ACEScg colorspace - math::fvec3 color_acescg = color::aces::ap1.from_xyz * color_xyz; + // Transform XYZ color to RGB + math::fvec3 color_rgb = config::scene_linear_color_space.from_xyz * color_xyz; // Convert apparent magnitude to brightness factor relative to a 0th magnitude star float brightness = physics::light::vmag::to_brightness(vmag); @@ -280,13 +279,13 @@ void create_stars(::game& ctx) *(star_vertex++) = position.x(); *(star_vertex++) = position.y(); *(star_vertex++) = position.z(); - *(star_vertex++) = color_acescg.x(); - *(star_vertex++) = color_acescg.y(); - *(star_vertex++) = color_acescg.z(); + *(star_vertex++) = color_rgb.x(); + *(star_vertex++) = color_rgb.y(); + *(star_vertex++) = color_rgb.z(); *(star_vertex++) = brightness; // Calculate spectral illuminance - math::dvec3 illuminance = math::dvec3(color_acescg * physics::light::vmag::to_illuminance(vmag)); + math::dvec3 illuminance = math::dvec3(color_rgb * physics::light::vmag::to_illuminance(vmag)); // Add spectral illuminance to total starlight illuminance starlight_illuminance += illuminance; @@ -362,9 +361,9 @@ void create_sun(::game& ctx) ctx.sun_light = std::make_unique(); ctx.sun_light->set_shadow_caster(true); ctx.sun_light->set_shadow_framebuffer(ctx.shadow_map_framebuffer); - ctx.sun_light->set_shadow_bias(0.0025f); + ctx.sun_light->set_shadow_bias(0.005f); + ctx.sun_light->set_shadow_distance(50.0f); ctx.sun_light->set_shadow_cascade_count(4); - ctx.sun_light->set_shadow_cascade_coverage(0.05f); ctx.sun_light->set_shadow_cascade_distribution(0.8f); // Add sun light scene objects to surface scene