diff --git a/intern/opencolorio/fallback_impl.cc b/intern/opencolorio/fallback_impl.cc index f982d695a81..2f6aed57ac3 100644 --- a/intern/opencolorio/fallback_impl.cc +++ b/intern/opencolorio/fallback_impl.cc @@ -494,6 +494,9 @@ OCIO_ConstProcessorRcPtr *FallbackImpl::createDisplayProcessor(OCIO_ConstConfigR const char * /*look*/, const float scale, const float exponent, + const float /*temperature*/, + const float /*tint*/, + const bool /*use_white_balance*/, const bool inverse) { FallbackTransform transform; diff --git a/intern/opencolorio/gpu_shader_display_transform_frag.glsl b/intern/opencolorio/gpu_shader_display_transform_frag.glsl index 75c440e5264..57e3994720c 100644 --- a/intern/opencolorio/gpu_shader_display_transform_frag.glsl +++ b/intern/opencolorio/gpu_shader_display_transform_frag.glsl @@ -159,8 +159,8 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) /* Convert to scene linear (usually a no-op). */ col = OCIO_to_scene_linear(col); - /* Apply exposure in scene linear. */ - col.rgb *= parameters.scale; + /* Apply exposure and white balance in scene linear. */ + col = parameters.scene_linear_matrix * col; /* Convert to display space. */ col = OCIO_to_display(col); diff --git a/intern/opencolorio/ocio_capi.cc b/intern/opencolorio/ocio_capi.cc index 9ecc3586473..588a1a81ff2 100644 --- a/intern/opencolorio/ocio_capi.cc +++ b/intern/opencolorio/ocio_capi.cc @@ -257,10 +257,22 @@ OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *con const char *look, const float scale, const float exponent, + const float temperature, + const float tint, + const bool use_white_balance, const bool inverse) { - return impl->createDisplayProcessor( - config, input, view, display, look, scale, exponent, inverse); + return impl->createDisplayProcessor(config, + input, + view, + display, + look, + scale, + exponent, + temperature, + tint, + use_white_balance, + inverse); } OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data, @@ -294,9 +306,12 @@ bool OCIO_gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, const float scale, const float exponent, const float dither, + const float temperature, + const float tint, const bool use_predivide, const bool use_overlay, - const bool use_hdr) + const bool use_hdr, + const bool use_white_balance) { return impl->gpuDisplayShaderBind(config, input, @@ -307,9 +322,12 @@ bool OCIO_gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, scale, exponent, dither, + temperature, + tint, use_predivide, use_overlay, - use_hdr); + use_hdr, + use_white_balance); } void OCIO_gpuDisplayShaderUnbind() diff --git a/intern/opencolorio/ocio_capi.h b/intern/opencolorio/ocio_capi.h index ae28e75a286..e9b49dbbebe 100644 --- a/intern/opencolorio/ocio_capi.h +++ b/intern/opencolorio/ocio_capi.h @@ -175,6 +175,9 @@ OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *con const char *look, const float scale, const float exponent, + const float temperature, + const float tint, + const bool use_white_balance, const bool inverse); struct OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data, @@ -197,9 +200,12 @@ bool OCIO_gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, const float scale, const float exponent, const float dither, + const float temperature, + const float tint, const bool use_predivide, const bool use_overlay, - const bool use_hdr); + const bool use_hdr, + const bool use_white_balance); void OCIO_gpuDisplayShaderUnbind(void); void OCIO_gpuCacheFree(void); diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc index 448b8f12db3..9ebfca54ae2 100644 --- a/intern/opencolorio/ocio_impl.cc +++ b/intern/opencolorio/ocio_impl.cc @@ -22,7 +22,9 @@ using namespace OCIO_NAMESPACE; #include "MEM_guardedalloc.h" #include "BLI_math_color.h" +#include "BLI_math_color.hh" #include "BLI_math_matrix.h" +#include "BLI_math_matrix.hh" #include "ocio_impl.h" @@ -37,6 +39,10 @@ using namespace OCIO_NAMESPACE; # define __func__ __FUNCTION__ #endif +using blender::double4x4; +using blender::float3; +using blender::float3x3; + static void OCIO_reportError(const char *err) { std::cerr << "OpenColorIO Error: " << err << std::endl; @@ -670,15 +676,18 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr const char *look, const float scale, const float exponent, + const float temperature, + const float tint, + const bool use_white_balance, const bool inverse) { ConstConfigRcPtr config = *(ConstConfigRcPtr *)config_; GroupTransformRcPtr group = GroupTransform::Create(); - /* Exposure. */ - if (scale != 1.0f) { - /* Always apply exposure in scene linear. */ + /* Linear transforms. */ + if (scale != 1.0f || use_white_balance) { + /* Always apply exposure and/or white balance in scene linear. */ ColorSpaceTransformRcPtr ct = ColorSpaceTransform::Create(); ct->setSrc(input); ct->setDst(ROLE_SCENE_LINEAR); @@ -689,9 +698,26 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr /* Apply scale. */ MatrixTransformRcPtr mt = MatrixTransform::Create(); - const double matrix[16] = { - scale, 0.0, 0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0, 0.0, 1.0}; - mt->setMatrix(matrix); + float3x3 matrix = float3x3::identity() * scale; + + /* Apply white balance. */ + if (use_white_balance) { + /* Compute white point of the scene space in XYZ.*/ + float3x3 xyz_to_scene; + configGetXYZtoSceneLinear(config_, xyz_to_scene.ptr()); + float3x3 scene_to_xyz = blender::math::invert(xyz_to_scene); + float3 target = scene_to_xyz * float3(1.0f); + + /* Add operations to the matrix. + * Note: Since we're multiplying from the right, the operations here will be performed in + * reverse list order (scene-to-XYZ, then adaption, then XYZ-to-scene, then exposure). */ + matrix *= xyz_to_scene; + matrix *= blender::math::chromatic_adaption_matrix( + blender::math::whitepoint_from_temp_tint(temperature, tint), target); + matrix *= scene_to_xyz; + } + + mt->setMatrix(double4x4(blender::math::transpose(matrix)).base_ptr()); group->appendTransform(mt); } diff --git a/intern/opencolorio/ocio_impl.h b/intern/opencolorio/ocio_impl.h index 3f8ffa93e97..b03c2a0c18e 100644 --- a/intern/opencolorio/ocio_impl.h +++ b/intern/opencolorio/ocio_impl.h @@ -87,6 +87,9 @@ class IOCIOImpl { const char *look, const float scale, const float exponent, + const float temperature, + const float tint, + const bool use_white_balance, const bool inverse) = 0; virtual OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, @@ -113,9 +116,12 @@ class IOCIOImpl { const float /*scale*/, const float /*exponent*/, const float /*dither*/, + const float /*temperature*/, + const float /*tint*/, const bool /*use_predivide*/, const bool /*use_overlay*/, - const bool /*use_hdr*/) + const bool /*use_hdr*/, + const bool /*use_white_balance*/) { return false; } @@ -200,6 +206,9 @@ class FallbackImpl : public IOCIOImpl { const char *look, const float scale, const float exponent, + const float temperature, + const float tint, + const bool use_white_balance, const bool inverse); OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, @@ -291,6 +300,9 @@ class OCIOImpl : public IOCIOImpl { const char *look, const float scale, const float exponent, + const float temperature, + const float tint, + const bool use_white_balance, const bool inverse); OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, @@ -313,9 +325,12 @@ class OCIOImpl : public IOCIOImpl { const float scale, const float exponent, const float dither, + const float temperature, + const float tint, const bool use_predivide, const bool use_overlay, - const bool use_hdr); + const bool use_hdr, + const bool use_white_balance); void gpuDisplayShaderUnbind(void); void gpuCacheFree(void); diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index 92f5cd3b932..21b0b98b3e7 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -27,11 +27,17 @@ using namespace OCIO_NAMESPACE; +#include "BLI_math_color.h" +#include "BLI_math_color.hh" +#include "BLI_math_matrix.hh" + #include "MEM_guardedalloc.h" #include "ocio_impl.h" #include "ocio_shader_shared.hh" +using blender::float3x3; + /* **** OpenGL drawing routines using GLSL for color space transform ***** */ enum OCIO_GPUTextureSlots { @@ -547,8 +553,8 @@ static void updateGPUCurveMapping(OCIO_GPUCurveMappping &curvemap, } static void updateGPUDisplayParameters(OCIO_GPUShader &shader, - float scale, float exponent, + float4x4 scene_linear_matrix, float dither, bool use_predivide, bool use_overlay, @@ -560,8 +566,8 @@ static void updateGPUDisplayParameters(OCIO_GPUShader &shader, do_update = true; } OCIO_GPUParameters &data = shader.parameters; - if (data.scale != scale) { - data.scale = scale; + if (data.scene_linear_matrix != scene_linear_matrix) { + data.scene_linear_matrix = scene_linear_matrix; do_update = true; } if (data.exponent != exponent) { @@ -645,20 +651,22 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader( /* Create Processors. * - * Scale and exponent are handled outside of OCIO shader so we can handle them - * as uniforms at the binding stage. OCIO would otherwise bake them into the - * shader code, requiring slow recompiles when interactively adjusting them. + * Scale, white balance and exponent are handled outside of OCIO shader so we + * can handle them as uniforms at the binding stage. OCIO would otherwise bake + * them into the shader code, requiring slow recompiles when interactively + * adjusting them. * * Note that OCIO does have the concept of dynamic properties, however there * is no dynamic gamma and exposure is part of more expensive operations only. * - * Since exposure must happen in scene linear, we use two processors. The input - * is usually scene linear already and so that conversion is often a no-op. + * Since exposure and white balance must happen in scene linear, we use two + * processors. The input is usually scene linear already and so that conversion + * is often a no-op. */ OCIO_ConstProcessorRcPtr *processor_to_scene_linear = OCIO_configGetProcessorWithNames( config, input, ROLE_SCENE_LINEAR); OCIO_ConstProcessorRcPtr *processor_to_display = OCIO_createDisplayProcessor( - config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f, false); + config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f, 0.0f, 0.0f, false, false); /* Create shader descriptions. */ if (processor_to_scene_linear && processor_to_display) { @@ -724,9 +732,12 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, const float scale, const float exponent, const float dither, + const float temperature, + const float tint, const bool use_predivide, const bool use_overlay, - const bool use_hdr) + const bool use_hdr, + const bool use_white_balance) { /* Get GPU shader from cache or create new one. */ OCIO_GPUDisplayShader &display_shader = getGPUDisplayShader( @@ -761,7 +772,24 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, GPU_uniformbuf_bind(textures.uniforms_buffer, UNIFORMBUF_SLOT_LUTS); } - updateGPUDisplayParameters(shader, scale, exponent, dither, use_predivide, use_overlay, use_hdr); + float3x3 matrix = float3x3::identity() * scale; + if (use_white_balance) { + /* Compute white point of the scene space in XYZ.*/ + float3x3 xyz_to_scene; + configGetXYZtoSceneLinear(config, xyz_to_scene.ptr()); + float3x3 scene_to_xyz = blender::math::invert(xyz_to_scene); + float3 target = scene_to_xyz * float3(1.0f); + + /* Add operations to the matrix. + * Note: Since we're multiplying from the right, the operations here will be performed in + * reverse list order (scene-to-XYZ, then adaption, then XYZ-to-scene, then exposure). */ + matrix *= xyz_to_scene; + matrix *= blender::math::chromatic_adaption_matrix( + blender::math::whitepoint_from_temp_tint(temperature, tint), target); + matrix *= scene_to_xyz; + } + updateGPUDisplayParameters( + shader, exponent, float4x4(matrix), dither, use_predivide, use_overlay, use_hdr); GPU_uniformbuf_bind(shader.parameters_buffer, UNIFORMBUF_SLOT_DISPLAY); /* TODO(fclem): remove remains of IMM. */ diff --git a/intern/opencolorio/ocio_shader_shared.hh b/intern/opencolorio/ocio_shader_shared.hh index 3797e5842ba..d84edbb306f 100644 --- a/intern/opencolorio/ocio_shader_shared.hh +++ b/intern/opencolorio/ocio_shader_shared.hh @@ -32,11 +32,12 @@ struct OCIO_GPUCurveMappingParameters { struct OCIO_GPUParameters { float dither; - float scale; float exponent; bool32_t use_predivide; bool32_t use_overlay; bool32_t use_hdr; int _pad0; int _pad1; + int _pad2; + float4x4 scene_linear_matrix; }; diff --git a/scripts/presets/color_management/white_balance/Illuminant_A.py b/scripts/presets/color_management/white_balance/Illuminant_A.py new file mode 100644 index 00000000000..f72bc131e98 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_A.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 2856 +view_settings.white_balance_tint = 0.0 diff --git a/scripts/presets/color_management/white_balance/Illuminant_B.py b/scripts/presets/color_management/white_balance/Illuminant_B.py new file mode 100644 index 00000000000..88efaa365eb --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_B.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4873 +view_settings.white_balance_tint = -3.8 diff --git a/scripts/presets/color_management/white_balance/Illuminant_C.py b/scripts/presets/color_management/white_balance/Illuminant_C.py new file mode 100644 index 00000000000..b8a0ac5ef15 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_C.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 6774 +view_settings.white_balance_tint = -6.4 diff --git a/scripts/presets/color_management/white_balance/Illuminant_D50.py b/scripts/presets/color_management/white_balance/Illuminant_D50.py new file mode 100644 index 00000000000..b930dd3f087 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_D50.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 5002 +view_settings.white_balance_tint = 9.6 diff --git a/scripts/presets/color_management/white_balance/Illuminant_D55.py b/scripts/presets/color_management/white_balance/Illuminant_D55.py new file mode 100644 index 00000000000..8734d5df778 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_D55.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 5501 +view_settings.white_balance_tint = 10.0 diff --git a/scripts/presets/color_management/white_balance/Illuminant_D65.py b/scripts/presets/color_management/white_balance/Illuminant_D65.py new file mode 100644 index 00000000000..3cc5a58a8c0 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_D65.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 6502 +view_settings.white_balance_tint = 9.8 diff --git a/scripts/presets/color_management/white_balance/Illuminant_D75.py b/scripts/presets/color_management/white_balance/Illuminant_D75.py new file mode 100644 index 00000000000..7d1349d547a --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_D75.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 7506 +view_settings.white_balance_tint = 9.6 diff --git a/scripts/presets/color_management/white_balance/Illuminant_D93.py b/scripts/presets/color_management/white_balance/Illuminant_D93.py new file mode 100644 index 00000000000..17dadb92e14 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_D93.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 9298 +view_settings.white_balance_tint = 9.5 diff --git a/scripts/presets/color_management/white_balance/Illuminant_E.py b/scripts/presets/color_management/white_balance/Illuminant_E.py new file mode 100644 index 00000000000..c3cb4d61adb --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_E.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 5454 +view_settings.white_balance_tint = -13.0 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F1.py b/scripts/presets/color_management/white_balance/Illuminant_F1.py new file mode 100644 index 00000000000..de5927a57f7 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F1.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 6424 +view_settings.white_balance_tint = 21.8 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F10.py b/scripts/presets/color_management/white_balance/Illuminant_F10.py new file mode 100644 index 00000000000..1adc9a0e76b --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F10.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4991 +view_settings.white_balance_tint = 11.1 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F11.py b/scripts/presets/color_management/white_balance/Illuminant_F11.py new file mode 100644 index 00000000000..f6012a70af0 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F11.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4001 +view_settings.white_balance_tint = 0.5 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F12.py b/scripts/presets/color_management/white_balance/Illuminant_F12.py new file mode 100644 index 00000000000..7f3493089f2 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F12.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 3002 +view_settings.white_balance_tint = 0.6 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F2.py b/scripts/presets/color_management/white_balance/Illuminant_F2.py new file mode 100644 index 00000000000..2c2fe545868 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F2.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4225 +view_settings.white_balance_tint = 5.9 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F3.py b/scripts/presets/color_management/white_balance/Illuminant_F3.py new file mode 100644 index 00000000000..1681bc1ae20 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F3.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 3447 +view_settings.white_balance_tint = 2.5 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F4.py b/scripts/presets/color_management/white_balance/Illuminant_F4.py new file mode 100644 index 00000000000..aec40bd3e48 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F4.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 2940 +view_settings.white_balance_tint = -2.0 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F5.py b/scripts/presets/color_management/white_balance/Illuminant_F5.py new file mode 100644 index 00000000000..9708389a29b --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F5.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 6342 +view_settings.white_balance_tint = 32.7 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F6.py b/scripts/presets/color_management/white_balance/Illuminant_F6.py new file mode 100644 index 00000000000..38836046488 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F6.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4148 +view_settings.white_balance_tint = 18.6 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F7.py b/scripts/presets/color_management/white_balance/Illuminant_F7.py new file mode 100644 index 00000000000..1c72ddea007 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F7.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 6489 +view_settings.white_balance_tint = 10.0 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F8.py b/scripts/presets/color_management/white_balance/Illuminant_F8.py new file mode 100644 index 00000000000..e299fd24023 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F8.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4995 +view_settings.white_balance_tint = 9.7 diff --git a/scripts/presets/color_management/white_balance/Illuminant_F9.py b/scripts/presets/color_management/white_balance/Illuminant_F9.py new file mode 100644 index 00000000000..95326731c62 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_F9.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4147 +view_settings.white_balance_tint = 0.3 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-B1.py b/scripts/presets/color_management/white_balance/Illuminant_LED-B1.py new file mode 100644 index 00000000000..56eefa203ee --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-B1.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 2733 +view_settings.white_balance_tint = -2.0 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-B2.py b/scripts/presets/color_management/white_balance/Illuminant_LED-B2.py new file mode 100644 index 00000000000..a4b8cc57a6e --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-B2.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 2997 +view_settings.white_balance_tint = -2.8 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-B3.py b/scripts/presets/color_management/white_balance/Illuminant_LED-B3.py new file mode 100644 index 00000000000..a002a23137e --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-B3.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4103 +view_settings.white_balance_tint = -1.8 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-B4.py b/scripts/presets/color_management/white_balance/Illuminant_LED-B4.py new file mode 100644 index 00000000000..ec41d94135b --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-B4.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 5108 +view_settings.white_balance_tint = 1.6 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-B5.py b/scripts/presets/color_management/white_balance/Illuminant_LED-B5.py new file mode 100644 index 00000000000..74ff327b810 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-B5.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 6598 +view_settings.white_balance_tint = 2.7 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-BH1.py b/scripts/presets/color_management/white_balance/Illuminant_LED-BH1.py new file mode 100644 index 00000000000..054ce9d3109 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-BH1.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 2852 +view_settings.white_balance_tint = -0.9 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-RGB1.py b/scripts/presets/color_management/white_balance/Illuminant_LED-RGB1.py new file mode 100644 index 00000000000..af6dbbc20e9 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-RGB1.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 2840 +view_settings.white_balance_tint = 12.8 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-V1.py b/scripts/presets/color_management/white_balance/Illuminant_LED-V1.py new file mode 100644 index 00000000000..86ad9584f3d --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-V1.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 3079 +view_settings.white_balance_tint = 48.9 diff --git a/scripts/presets/color_management/white_balance/Illuminant_LED-V2.py b/scripts/presets/color_management/white_balance/Illuminant_LED-V2.py new file mode 100644 index 00000000000..ec984c466b9 --- /dev/null +++ b/scripts/presets/color_management/white_balance/Illuminant_LED-V2.py @@ -0,0 +1,5 @@ +import bpy +view_settings = bpy.context.scene.view_settings + +view_settings.white_balance_temperature = 4070 +view_settings.white_balance_tint = 3.3 diff --git a/scripts/startup/bl_operators/presets.py b/scripts/startup/bl_operators/presets.py index e19c1b3f4ab..cb88cf0efa8 100644 --- a/scripts/startup/bl_operators/presets.py +++ b/scripts/startup/bl_operators/presets.py @@ -569,6 +569,24 @@ class AddPresetEEVEERaytracing(AddPresetBase, Operator): preset_subdir = "eevee/raytracing" +class AddPresetColorManagementWhiteBalance(AddPresetBase, Operator): + """Add or remove a white balance preset""" + bl_idname = "render.color_management_white_balance_preset_add" + bl_label = "Add White Balance Preset" + preset_menu = "RENDER_PT_color_management_white_balance_presets" + + preset_defines = [ + "view_settings = bpy.context.scene.view_settings", + ] + + preset_values = [ + "view_settings.white_balance_temperature", + "view_settings.white_balance_tint", + ] + + preset_subdir = "color_management/white_balance" + + class AddPresetNodeColor(AddPresetBase, Operator): """Add or remove a Node Color Preset""" bl_idname = "node.node_color_preset_add" @@ -981,6 +999,7 @@ classes = ( AddPresetGpencilBrush, AddPresetGpencilMaterial, AddPresetEEVEERaytracing, + AddPresetColorManagementWhiteBalance, ExecutePreset, WM_MT_operator_presets, WM_PT_operator_presets, diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 09651d7634d..fab8c0023e3 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -119,7 +119,7 @@ class RENDER_PT_color_management_display_settings(RenderButtonsPanel, Panel): class RENDER_PT_color_management_curves(RenderButtonsPanel, Panel): - bl_label = "Use Curves" + bl_label = "Curves" bl_parent_id = "RENDER_PT_color_management" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = { @@ -145,11 +145,62 @@ class RENDER_PT_color_management_curves(RenderButtonsPanel, Panel): layout.use_property_split = False layout.use_property_decorate = False # No animation. - layout.enabled = view.use_curve_mapping + layout.active = view.use_curve_mapping layout.template_curve_mapping(view, "curve_mapping", type='COLOR', levels=True) +class RENDER_PT_color_management_white_balance_presets(PresetPanel, Panel): + bl_label = "White Balance Presets" + preset_subdir = "color_management/white_balance" + preset_operator = "script.execute_preset" + preset_add_operator = "render.color_management_white_balance_preset_add" + + +class RENDER_PT_color_management_white_balance(RenderButtonsPanel, Panel): + bl_label = "White Balance" + bl_parent_id = "RENDER_PT_color_management" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = { + 'BLENDER_RENDER', + 'BLENDER_EEVEE', + 'BLENDER_EEVEE_NEXT', + 'BLENDER_WORKBENCH', + } + + def draw_header(self, context): + scene = context.scene + view = scene.view_settings + + self.layout.prop(view, "use_white_balance", text="") + + def draw_header_preset(self, context): + layout = self.layout + + scene = context.scene + view = scene.view_settings + + RENDER_PT_color_management_white_balance_presets.draw_panel_header(layout) + + eye = layout.operator("ui.eyedropper_color", text="", icon='EYEDROPPER') + eye.prop_data_path = "scene.view_settings.white_balance_whitepoint" + + def draw(self, context): + layout = self.layout + + scene = context.scene + view = scene.view_settings + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + layout.active = view.use_white_balance + + col = layout.column() + col.prop(view, "white_balance_temperature") + col.prop(view, "white_balance_tint") + + class RENDER_PT_eevee_ambient_occlusion(RenderButtonsPanel, Panel): bl_label = "Ambient Occlusion" bl_options = {'DEFAULT_CLOSED'} @@ -1385,6 +1436,8 @@ classes = ( RENDER_PT_color_management, RENDER_PT_color_management_display_settings, RENDER_PT_color_management_curves, + RENDER_PT_color_management_white_balance_presets, + RENDER_PT_color_management_white_balance, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index dd29eb5dff9..8b131e0a18c 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 3 +#define BLENDER_FILE_SUBVERSION 4 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/intern/colortools.cc b/source/blender/blenkernel/intern/colortools.cc index c6b2f6496ee..76702c38664 100644 --- a/source/blender/blenkernel/intern/colortools.cc +++ b/source/blender/blenkernel/intern/colortools.cc @@ -1954,6 +1954,8 @@ void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings new_settings->flag = settings->flag; new_settings->exposure = settings->exposure; new_settings->gamma = settings->gamma; + new_settings->temperature = settings->temperature; + new_settings->tint = settings->tint; if (settings->curve_mapping) { new_settings->curve_mapping = BKE_curvemapping_copy(settings->curve_mapping); diff --git a/source/blender/blenlib/BLI_math_color.hh b/source/blender/blenlib/BLI_math_color.hh index 4c9ba96b96e..356b47081c8 100644 --- a/source/blender/blenlib/BLI_math_color.hh +++ b/source/blender/blenlib/BLI_math_color.hh @@ -13,6 +13,7 @@ #include "BLI_color.hh" #include "BLI_math_base.hh" +#include "BLI_math_matrix_types.hh" namespace blender::math { @@ -39,4 +40,14 @@ inline ColorSceneLinearByteEncoded4b interpolate( math::interpolate(a.a, b.a, t)}; } +blender::float3 whitepoint_from_temp_tint(const float temperature, const float tint); + +bool whitepoint_to_temp_tint(const blender::float3 white, float &temperature, float &tint); + +/* Computes a matrix to perform chromatic adaption from a source white point (given in the form of + * temperature and tint) to a target white point (given as its XYZ values). + * The resulting matrix operates on XYZ values, the caller is responsible for RGB conversion. */ +blender::float3x3 chromatic_adaption_matrix(const blender::float3 from_XYZ, + const blender::float3 to_XYZ); + } // namespace blender::math diff --git a/source/blender/blenlib/intern/math_color.cc b/source/blender/blenlib/intern/math_color.cc index 77e2e022bd4..e9925015e59 100644 --- a/source/blender/blenlib/intern/math_color.cc +++ b/source/blender/blenlib/intern/math_color.cc @@ -6,7 +6,11 @@ * \ingroup bli */ +#include "BLI_array.hh" #include "BLI_math_color.h" +#include "BLI_math_color.hh" +#include "BLI_math_matrix.hh" +#include "BLI_math_vector.hh" #include "BLI_simd.hh" #include "BLI_utildefines.h" @@ -839,3 +843,124 @@ void BLI_init_srgb_conversion() BLI_color_to_srgb_table[i] = ushort(b * 0x100); } } + +namespace blender::math { + +struct locus_entry_t { + float mired; /* Inverse temperature */ + float2 uv; /* CIE 1960 uv coordinates */ + float t; /* Isotherm parameter */ + float dist(const float2 p) const + { + const float2 diff = p - uv; + return diff.y - t * diff.x; + } +}; + +/* Tabulated approximation of the Planckian locus. + * Based on http://www.brucelindbloom.com/Eqn_XYZ_to_T.html. + * Original source: + * "Color Science: Concepts and Methods, Quantitative Data and Formulae", Second Edition, + * Gunter Wyszecki and W. S. Stiles, John Wiley & Sons, 1982, pp. 227, 228. */ +static const std::array planck_locus{{ + {0.0f, {0.18006f, 0.26352f}, -0.24341f}, {10.0f, {0.18066f, 0.26589f}, -0.25479f}, + {20.0f, {0.18133f, 0.26846f}, -0.26876f}, {30.0f, {0.18208f, 0.27119f}, -0.28539f}, + {40.0f, {0.18293f, 0.27407f}, -0.30470f}, {50.0f, {0.18388f, 0.27709f}, -0.32675f}, + {60.0f, {0.18494f, 0.28021f}, -0.35156f}, {70.0f, {0.18611f, 0.28342f}, -0.37915f}, + {80.0f, {0.18740f, 0.28668f}, -0.40955f}, {90.0f, {0.18880f, 0.28997f}, -0.44278f}, + {100.0f, {0.19032f, 0.29326f}, -0.47888f}, {125.0f, {0.19462f, 0.30141f}, -0.58204f}, + {150.0f, {0.19962f, 0.30921f}, -0.70471f}, {175.0f, {0.20525f, 0.31647f}, -0.84901f}, + {200.0f, {0.21142f, 0.32312f}, -1.0182f}, {225.0f, {0.21807f, 0.32909f}, -1.2168f}, + {250.0f, {0.22511f, 0.33439f}, -1.4512f}, {275.0f, {0.23247f, 0.33904f}, -1.7298f}, + {300.0f, {0.24010f, 0.34308f}, -2.0637f}, {325.0f, {0.24792f, 0.34655f}, -2.4681f}, + {350.0f, {0.25591f, 0.34951f}, -2.9641f}, {375.0f, {0.26400f, 0.35200f}, -3.5814f}, + {400.0f, {0.27218f, 0.35407f}, -4.3633f}, {425.0f, {0.28039f, 0.35577f}, -5.3762f}, + {450.0f, {0.28863f, 0.35714f}, -6.7262f}, {475.0f, {0.29685f, 0.35823f}, -8.5955f}, + {500.0f, {0.30505f, 0.35907f}, -11.324f}, {525.0f, {0.31320f, 0.35968f}, -15.628f}, + {550.0f, {0.32129f, 0.36011f}, -23.325f}, {575.0f, {0.32931f, 0.36038f}, -40.770f}, + {600.0f, {0.33724f, 0.36051f}, -116.45f}, +}}; + +bool whitepoint_to_temp_tint(const blender::float3 white, float &temperature, float &tint) +{ + /* Convert XYZ -> CIE 1960 uv. */ + const float2 uv = float2{4.0f * white.x, 6.0f * white.y} / dot(white, {1.0f, 15.0f, 3.0f}); + + /* Find first entry that's "to the right" of the white point. */ + auto check = [uv](const float val, const locus_entry_t &entry) { return entry.dist(uv) < val; }; + const auto entry = std::upper_bound(planck_locus.begin(), planck_locus.end(), 0.0f, check); + if (entry == planck_locus.begin() || entry == planck_locus.end()) { + return false; + } + const size_t i = (size_t)(entry - planck_locus.begin()); + const locus_entry_t &low = planck_locus[i - 1], high = planck_locus[i]; + + /* Find closest point on locus. */ + const float d_low = low.dist(uv) / sqrtf(1.0f + low.t * low.t); + const float d_high = high.dist(uv) / sqrtf(1.0f + high.t * high.t); + const float f = d_low / (d_low - d_high); + + /* Find tint based on distance to closest point on locus. */ + const float2 uv_temp = interpolate(low.uv, high.uv, f); + const float abs_tint = length(uv - uv_temp) * 3000.0f; + if (abs_tint > 150.0f) { + return false; + } + + temperature = 1e6f / interpolate(low.mired, high.mired, f); + tint = abs_tint * ((uv.x < uv_temp.x) ? 1.0f : -1.0f); + return true; +} + +blender::float3 whitepoint_from_temp_tint(const float temperature, const float tint) +{ + /* Find table entry. */ + const float mired = clamp( + 1e6f / temperature, planck_locus[0].mired, planck_locus[planck_locus.size() - 1].mired); + auto check = [](const locus_entry_t &entry, const float val) { return entry.mired < val; }; + const auto entry = std::lower_bound(planck_locus.begin(), planck_locus.end(), mired, check); + const size_t i = (size_t)(entry - planck_locus.begin()); + const locus_entry_t &low = planck_locus[i - 1], high = planck_locus[i]; + + /* Find interpolation factor. */ + const float f = (mired - low.mired) / (high.mired - low.mired); + + /* Interpolate point along Planckian locus. */ + float2 uv = interpolate(low.uv, high.uv, f); + + /* Compute and interpolate isotherm. */ + const float2 isotherm0 = normalize(float2(1.0f, low.t)); + const float2 isotherm1 = normalize(float2(1.0f, high.t)); + const float2 isotherm = normalize(interpolate(isotherm0, isotherm1, f)); + + /* Offset away from the Planckian locus according to the tint. + * Tint is parametrized such that +-3000 tint corresponds to +-1 delta UV. */ + uv -= isotherm * tint / 3000.0f; + + /* Convert CIE 1960 uv -> xyY. */ + const float x = 3.0f * uv.x / (2.0f * uv.x - 8.0f * uv.y + 4.0f); + const float y = 2.0f * uv.y / (2.0f * uv.x - 8.0f * uv.y + 4.0f); + + /* Convert xyY -> XYZ (assuming Y=1). */ + return float3{x / y, 1.0f, (1.0f - x - y) / y}; +} + +blender::float3x3 chromatic_adaption_matrix(const blender::float3 from_XYZ, + const blender::float3 to_XYZ) +{ + /* Bradford transformation matrix (XYZ -> LMS). */ + static const blender::float3x3 bradford{ + {0.8951f, -0.7502f, 0.0389f}, + {0.2664f, 1.7135f, -0.0685f}, + {-0.1614f, 0.0367f, 1.0296f}, + }; + + /* Compute white points in LMS space. */ + const float3 from_LMS = bradford * from_XYZ / from_XYZ.y; + const float3 to_LMS = bradford * to_XYZ / to_XYZ.y; + + /* Assemble full transform: XYZ -> LMS -> adapted LMS -> adapted XYZ. */ + return invert(bradford) * from_scale(to_LMS / from_LMS) * bradford; +} + +} // namespace blender::math diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index b9038789c4d..a147254019c 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -4230,6 +4230,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 4)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->view_settings.temperature = 6500.0f; + scene->view_settings.tint = 10.0f; + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_color.cc b/source/blender/editors/interface/eyedroppers/eyedropper_color.cc index e9e3b9a0fb5..55d6e7f29c8 100644 --- a/source/blender/editors/interface/eyedroppers/eyedropper_color.cc +++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.cc @@ -23,11 +23,14 @@ #include "BKE_context.hh" #include "BKE_cryptomatte.h" #include "BKE_image.h" +#include "BKE_report.hh" #include "BKE_screen.hh" #include "NOD_composite.hh" #include "RNA_access.hh" +#include "RNA_define.hh" +#include "RNA_path.hh" #include "RNA_prototypes.h" #include "UI_interface.hh" @@ -84,7 +87,29 @@ static bool eyedropper_init(bContext *C, wmOperator *op) { Eyedropper *eye = MEM_cnew(__func__); - uiBut *but = UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index); + PropertyRNA *prop; + if ((prop = RNA_struct_find_property(op->ptr, "prop_data_path")) && + RNA_property_is_set(op->ptr, prop)) + { + char *prop_data_path = RNA_string_get_alloc(op->ptr, "prop_data_path", nullptr, 0, nullptr); + BLI_SCOPED_DEFER([&] { MEM_SAFE_FREE(prop_data_path); }); + if (!prop_data_path || prop_data_path[0] == '\0') { + MEM_freeN(eye); + return false; + } + PointerRNA ctx_ptr = RNA_pointer_create(nullptr, &RNA_Context, C); + if (!RNA_path_resolve(&ctx_ptr, prop_data_path, &eye->ptr, &eye->prop)) { + BKE_reportf(op->reports, RPT_ERROR, "Could not resolve path '%s'", prop_data_path); + MEM_freeN(eye); + return false; + } + eye->is_undo = true; + } + else { + uiBut *but = UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index); + eye->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + } + const enum PropertySubType prop_subtype = eye->prop ? RNA_property_subtype(eye->prop) : PropertySubType(0); @@ -99,8 +124,6 @@ static bool eyedropper_init(bContext *C, wmOperator *op) } op->customdata = eye; - eye->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); - float col[4]; RNA_property_float_get_array(&eye->ptr, eye->prop, col); if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) { @@ -585,4 +608,14 @@ void UI_OT_eyedropper_color(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* Paths relative to the context. */ + PropertyRNA *prop; + prop = RNA_def_string(ot->srna, + "prop_data_path", + nullptr, + 0, + "Data Path", + "Path of property to be set with the depth"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index 2503ee8eb26..4b9e5250f6d 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -6884,6 +6884,14 @@ void uiTemplateColormanagedViewSettings(uiLayout *layout, uiTemplateCurveMapping( col, &view_transform_ptr, "curve_mapping", 'c', true, false, false, false); } + + col = uiLayoutColumn(layout, false); + uiItemR(col, &view_transform_ptr, "use_white_balance", UI_ITEM_NONE, nullptr, ICON_NONE); + if (view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE) { + uiItemR( + col, &view_transform_ptr, "white_balance_temperature", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, &view_transform_ptr, "white_balance_tint", UI_ITEM_NONE, nullptr, ICON_NONE); + } } /** \} */ diff --git a/source/blender/imbuf/IMB_colormanagement.hh b/source/blender/imbuf/IMB_colormanagement.hh index db3b018f7fe..d44635a4aaf 100644 --- a/source/blender/imbuf/IMB_colormanagement.hh +++ b/source/blender/imbuf/IMB_colormanagement.hh @@ -85,6 +85,14 @@ BLI_INLINE void IMB_colormanagement_scene_linear_to_aces(float aces[3], const float scene_linear[3]); const float *IMB_colormanagement_get_xyz_to_scene_linear(); +/** + * Functions for converting between color temperature/tint and RGB white points. + */ +void IMB_colormanagement_get_view_whitepoint(const ColorManagedViewSettings *view_settings, + float whitepoint[3]); +bool IMB_colormanagement_set_view_whitepoint(ColorManagedViewSettings *view_settings, + const float whitepoint[3]); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/imbuf/intern/colormanagement.cc b/source/blender/imbuf/intern/colormanagement.cc index 9e0cae61824..5e8d6cbe686 100644 --- a/source/blender/imbuf/intern/colormanagement.cc +++ b/source/blender/imbuf/intern/colormanagement.cc @@ -30,6 +30,7 @@ #include "BLI_blenlib.h" #include "BLI_math_color.h" +#include "BLI_math_color.hh" #include "BLI_rect.h" #include "BLI_string.h" #include "BLI_task.h" @@ -195,6 +196,8 @@ struct ColormanageCacheViewSettings { float exposure; float gamma; float dither; + float temperature; + float tint; CurveMapping *curve_mapping; }; @@ -213,6 +216,8 @@ struct ColormanageCacheData { float exposure; /* exposure value cached buffer is calculated with */ float gamma; /* gamma value cached buffer is calculated with */ float dither; /* dither value cached buffer is calculated with */ + float temperature; /* temperature value cached buffer is calculated with */ + float tint; /* tint value cached buffer is calculated with */ CurveMapping *curve_mapping; /* curve mapping used for cached buffer */ int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */ }; @@ -299,6 +304,8 @@ static void colormanage_view_settings_to_cache(ImBuf *ibuf, cache_view_settings->exposure = view_settings->exposure; cache_view_settings->gamma = view_settings->gamma; cache_view_settings->dither = ibuf->dither; + cache_view_settings->temperature = view_settings->temperature; + cache_view_settings->tint = view_settings->tint; cache_view_settings->flag = view_settings->flag; cache_view_settings->curve_mapping = view_settings->curve_mapping; } @@ -378,7 +385,9 @@ static uchar *colormanage_cache_get(ImBuf *ibuf, if (cache_data->look != view_settings->look || cache_data->exposure != view_settings->exposure || cache_data->gamma != view_settings->gamma || cache_data->dither != view_settings->dither || - cache_data->flag != view_settings->flag || cache_data->curve_mapping != curve_mapping || + cache_data->temperature != view_settings->temperature || + cache_data->tint != view_settings->tint || cache_data->flag != view_settings->flag || + cache_data->curve_mapping != curve_mapping || cache_data->curve_mapping_timestamp != curve_mapping_timestamp) { *cache_handle = nullptr; @@ -424,6 +433,8 @@ static void colormanage_cache_put(ImBuf *ibuf, cache_data->exposure = view_settings->exposure; cache_data->gamma = view_settings->gamma; cache_data->dither = view_settings->dither; + cache_data->temperature = view_settings->temperature; + cache_data->tint = view_settings->tint; cache_data->flag = view_settings->flag; cache_data->curve_mapping = curve_mapping; cache_data->curve_mapping_timestamp = curve_mapping_timestamp; @@ -874,8 +885,11 @@ static ColorSpace *display_transform_get_colorspace( static OCIO_ConstCPUProcessorRcPtr *create_display_buffer_processor(const char *look, const char *view_transform, const char *display, - float exposure, - float gamma, + const float exposure, + const float gamma, + const float temperature, + const float tint, + const bool use_white_balance, const char *from_colorspace) { OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); @@ -890,6 +904,9 @@ static OCIO_ConstCPUProcessorRcPtr *create_display_buffer_processor(const char * (use_look) ? look : "", scale, exponent, + temperature, + tint, + use_white_balance, false); OCIO_configRelease(config); @@ -982,6 +999,9 @@ static OCIO_ConstCPUProcessorRcPtr *display_from_scene_linear_processor( nullptr, 1.0f, 1.0f, + 0.0f, + 0.0f, + false, false); OCIO_configRelease(config); @@ -1011,8 +1031,17 @@ static OCIO_ConstCPUProcessorRcPtr *display_to_scene_linear_processor(ColorManag OCIO_ConstProcessorRcPtr *processor = nullptr; if (view_name && config) { - processor = OCIO_createDisplayProcessor( - config, global_role_scene_linear, view_name, display->name, nullptr, 1.0f, 1.0f, true); + processor = OCIO_createDisplayProcessor(config, + global_role_scene_linear, + view_name, + display->name, + nullptr, + 1.0f, + 1.0f, + 0.0f, + 0.0f, + false, + true); OCIO_configRelease(config); } @@ -1056,6 +1085,8 @@ void IMB_colormanagement_init_default_view_settings( view_settings->flag = 0; view_settings->gamma = 1.0f; view_settings->exposure = 0.0f; + view_settings->temperature = 6500.0f; + view_settings->tint = 10.0f; view_settings->curve_mapping = nullptr; } @@ -1484,6 +1515,29 @@ const float *IMB_colormanagement_get_xyz_to_scene_linear() /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Functions for converting between color temperature/tint and RGB white points + * \{ */ + +void IMB_colormanagement_get_view_whitepoint(const ColorManagedViewSettings *view_settings, + float whitepoint[3]) +{ + blender::float3 xyz = blender::math::whitepoint_from_temp_tint(view_settings->temperature, + view_settings->tint); + IMB_colormanagement_xyz_to_scene_linear(whitepoint, xyz); +} + +bool IMB_colormanagement_set_view_whitepoint(ColorManagedViewSettings *view_settings, + const float whitepoint[3]) +{ + blender::float3 xyz; + IMB_colormanagement_scene_linear_to_xyz(xyz, whitepoint); + return blender::math::whitepoint_to_temp_tint( + xyz, view_settings->temperature, view_settings->tint); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Threaded Display Buffer Transform Routines * \{ */ @@ -3934,12 +3988,16 @@ ColormanageProcessor *IMB_colormanagement_display_processor_new( cm_processor->is_data_result = display_space->is_data; } + const bool use_white_balance = applied_view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE; cm_processor->cpu_processor = create_display_buffer_processor( applied_view_settings->look, applied_view_settings->view_transform, display_settings->display_device, applied_view_settings->exposure, applied_view_settings->gamma, + applied_view_settings->temperature, + applied_view_settings->tint, + use_white_balance, global_role_scene_linear); if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) { @@ -4252,6 +4310,10 @@ bool IMB_colormanagement_setup_glsl_draw_from_space( const float gamma = applied_view_settings->gamma; const float scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure); const float exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma); + const float temperature = applied_view_settings->temperature; + const float tint = applied_view_settings->tint; + const bool use_white_balance = (applied_view_settings->flag & + COLORMANAGE_VIEW_USE_WHITE_BALANCE) != 0; const bool use_hdr = GPU_hdr_support() && (applied_view_settings->flag & COLORMANAGE_VIEW_USE_HDR) != 0; @@ -4267,9 +4329,12 @@ bool IMB_colormanagement_setup_glsl_draw_from_space( scale, exponent, dither, + temperature, + tint, predivide, do_overlay_merge, - use_hdr); + use_hdr, + use_white_balance); OCIO_configRelease(config); diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index 54ac42d220f..bd22b40c3d7 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -197,6 +197,9 @@ typedef struct ColorManagedViewSettings { float exposure; /** Post-display gamma transform. */ float gamma; + /** White balance parameters. */ + float temperature; + float tint; /** Pre-display RGB curves transform. */ struct CurveMapping *curve_mapping; void *_pad2; @@ -215,4 +218,5 @@ typedef struct ColorManagedColorspaceSettings { enum { COLORMANAGE_VIEW_USE_CURVES = (1 << 0), COLORMANAGE_VIEW_USE_HDR = (1 << 1), + COLORMANAGE_VIEW_USE_WHITE_BALANCE = (1 << 2), }; diff --git a/source/blender/makesrna/intern/rna_color.cc b/source/blender/makesrna/intern/rna_color.cc index 148ec14eb98..153fa10f34f 100644 --- a/source/blender/makesrna/intern/rna_color.cc +++ b/source/blender/makesrna/intern/rna_color.cc @@ -558,6 +558,18 @@ static std::optional rna_ColorManagedViewSettings_path(const Pointe return "view_settings"; } +static void rna_ColorManagedViewSettings_whitepoint_get(PointerRNA *ptr, float value[3]) +{ + const ColorManagedViewSettings *view_settings = (ColorManagedViewSettings *)ptr->data; + IMB_colormanagement_get_view_whitepoint(view_settings, value); +} + +static void rna_ColorManagedViewSettings_whitepoint_set(PointerRNA *ptr, const float value[3]) +{ + ColorManagedViewSettings *view_settings = (ColorManagedViewSettings *)ptr->data; + IMB_colormanagement_set_view_whitepoint(view_settings, value); +} + static bool rna_ColorManagedColorspaceSettings_is_data_get(PointerRNA *ptr) { ColorManagedColorspaceSettings *colorspace = (ColorManagedColorspaceSettings *)ptr->data; @@ -1309,6 +1321,41 @@ static void rna_def_colormanage(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Curves", "Use RGB curved for pre-display transformation"); RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + prop = RNA_def_property(srna, "use_white_balance", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", COLORMANAGE_VIEW_USE_WHITE_BALANCE); + RNA_def_property_ui_text( + prop, "Use White Balance", "Perform chromatic adaption from a different white point"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + prop = RNA_def_property(srna, "white_balance_temperature", PROP_FLOAT, PROP_COLOR_TEMPERATURE); + RNA_def_property_float_sdna(prop, nullptr, "temperature"); + RNA_def_property_float_default(prop, 6500.0f); + RNA_def_property_range(prop, 1800.0f, 100000.0f); + RNA_def_property_ui_range(prop, 2000.0f, 11000.0f, 100, 0); + RNA_def_property_ui_text(prop, "Temperature", "Color temperature of the scene's white point"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + prop = RNA_def_property(srna, "white_balance_tint", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, nullptr, "tint"); + RNA_def_property_float_default(prop, 10.0f); + RNA_def_property_range(prop, -500.0f, 500.0f); + RNA_def_property_ui_range(prop, -150.0f, 150.0f, 1, 1); + RNA_def_property_ui_text( + prop, "Tint", "Color tint of the scene's white point (the default of 10 matches daylight)"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + prop = RNA_def_property(srna, "white_balance_whitepoint", PROP_FLOAT, PROP_COLOR); + RNA_def_property_array(prop, 3); + RNA_def_property_float_funcs(prop, + "rna_ColorManagedViewSettings_whitepoint_get", + "rna_ColorManagedViewSettings_whitepoint_set", + nullptr); + RNA_def_property_ui_text(prop, + "White Point", + "The color which gets mapped to white " + "(automatically converted to/from temperature and tint)"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + prop = RNA_def_property(srna, "use_hdr_view", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", COLORMANAGE_VIEW_USE_HDR); RNA_def_property_ui_text(