Browse Source

Add more color-related functions, deconstruct blackbody function into separate color functions, move B-V color index function to color index namespace

master
C. J. Howard 5 months ago
parent
commit
ee5a9746da
16 changed files with 201 additions and 165 deletions
  1. +1
    -0
      CMakeLists.txt
  2. +0
    -2
      src/astro/astro.hpp
  3. +0
    -54
      src/astro/blackbody.cpp
  4. +0
    -42
      src/astro/blackbody.hpp
  5. +0
    -30
      src/astro/color-index.cpp
  6. +4
    -0
      src/color/acescg.hpp
  7. +88
    -0
      src/color/cct.hpp
  8. +4
    -0
      src/color/color.hpp
  9. +18
    -8
      src/color/index.hpp
  10. +4
    -0
      src/color/srgb.hpp
  11. +7
    -7
      src/color/ucs.hpp
  12. +4
    -4
      src/color/xyy.hpp
  13. +56
    -3
      src/color/xyz.hpp
  14. +5
    -4
      src/ecs/systems/astronomy-system.cpp
  15. +2
    -3
      src/game/states/play-state.cpp
  16. +8
    -8
      src/renderer/passes/sky-pass.cpp

+ 1
- 0
CMakeLists.txt View File

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

+ 0
- 2
src/astro/astro.hpp View File

@ -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"

+ 0
- 54
src/astro/blackbody.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "blackbody.hpp"
#include <algorithm>
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<double>(rgb.x, std::max<double>(rgb.y, rgb.z));
}
} // namespace astro

+ 0
- 42
src/astro/blackbody.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 0
- 30
src/astro/color-index.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 4
- 0
src/color/acescg.hpp View File

@ -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 <class T>
constexpr math::vector2<T> whitepoint = {0.32168, 0.33767};
/**
* Calculates the luminance of an ACEScg color.
*

+ 88
- 0
src/color/cct.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <class T>
math::vector2<T> 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 <class T>
math::vector3<T> 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 <class T>
math::vector3<T> to_xyz(T t);
template <class T>
math::vector2<T> 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>
{
(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 <class T>
math::vector3<T> to_xyy(T t)
{
return ucs::to_xyy(to_ucs(t), T(1.0));
}
template <class T>
math::vector3<T> to_xyz(T t)
{
return xyy::to_xyz(to_xyy(t));
}
} // namespace cct
} // namespace color
#endif // ANTKEEPER_COLOR_CCT_HPP

+ 4
- 0
src/color/color.hpp View File

@ -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

src/astro/color-index.hpp → src/color/index.hpp View File

@ -17,22 +17,32 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#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 <class T>
T bv_to_cct(T bv);
template <class T>
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

+ 4
- 0
src/color/srgb.hpp View File

@ -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 <class T>
constexpr math::vector2<T> whitepoint = {0.31271, 0.32902};
/**
* Performs the sRGB Electro-Optical Transfer Function (EOTF), also known as the sRGB decoding function.
*

+ 7
- 7
src/color/ucs.hpp View File

@ -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 <class T>
math::vector3<T> to_xyy(const math::vector2<T>& uv, T luminance);
math::vector3<T> to_xyy(const math::vector2<T>& uv, T luminance = T(1.0));
template <class T>
math::vector3<T> to_xyy(const math::vector2<T>& uv, T luminance);
math::vector3<T> to_xyy(const math::vector2<T>& 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<T>
{
(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
};
}

+ 4
- 4
src/color/xyy.hpp View File

@ -63,12 +63,12 @@ inline T luminance(const math::vector3& x)
template <class T>
math::vector2<T> to_ucs(const math::vector3<T>& 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<T>
{
(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]
};
}

+ 56
- 3
src/color/xyz.hpp View File

@ -63,6 +63,29 @@ math::vector3 to_srgb(const math::vector3& x);
template <class T>
math::vector3<T> to_xyy(const math::vector3<T>& 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 <class T>
math::vector3<T> aces_to_d65(const math::vector3<T>& 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 <class T>
math::vector3<T> d65_to_aces(const math::vector3<T>& x);
} // namespace cat
template <class T>
inline T luminance(const math::vector3<T>& x)
{
@ -74,9 +97,9 @@ math::vector3 to_acescg(const math::vector3& x)
{
static const math::matrix3<T> 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 <class T>
math::vector3<T> aces_to_d65(const math::vector3<T>& x)
{
static const math::matrix3<T> 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 <class T>
math::vector3<T> d65_to_aces(const math::vector3<T>& x)
{
static const math::matrix3<T> 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

+ 5
- 4
src/ecs/systems/astronomy-system.cpp View File

@ -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 <iostream>
@ -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<float>(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);

+ 2
- 3
src/game/states/play-state.cpp View File

@ -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<float>(astro::blackbody(6000.0)); // NOTE: this is linear sRGB, should be ACEScg
sun->set_color(sun_color);
//float3 sun_color = math::type_cast<float>(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<gl::texture_2d>("forest-gobo.tex"));
sun->set_light_texture_scale({2000, 2000});

+ 8
- 8
src/renderer/passes/sky-pass.cpp View File

@ -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<float>(rectangular.x);

Loading…
Cancel
Save