При работе с цветом Blender оперирует значениями из линейного (Linear) цветового пространства. Это позволяет демонстрировать цвета и градиенты в более привычном для человеческого глаза виде, однако этот момент нужно всегда учитывать при работе напрямую с цветом в Blender.
Если мы попробуем вывести значения цветов пикселей для изображения красного цвета, полученного, например, с нода композитинга Viewer Node, мы получим набор значений в линейном цветовом пространстве.
1 2 3 4 5 |
bpy.data.images['Viewer Node'].pixels[:] # (0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0, # 0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0, # 0.5, 0.0, 0.0, 1.0, ... |
Если мы сохраним такой массив точек в изображение, например в формате PNG, а затем откроем его в графическом редакторе, результат будет заметно отличаться от ожидаемого.
Так происходит потому, что обычно для изображений используется цветовое пространство sRGB, а не Linear, как мы получили на выходе в Blender. Для того чтобы получить значения пикселей изображения в цветовом пространстве sRGB, пригодном для сохранения, их нужно преобразовать.
Определим функцию, преобразующую значение из цветового пространства Linear в цветовое пространство sRGB:
1 2 3 4 5 |
def linear_to_srgb(color_value: float): if color_value <= 0.0031308: return int(12.92 * color_value * 255.99) else: return int((1.055 * color_value ** (1 / 2.4) - 0.055) * 255.99) |
В параметре функции мы будем передавать значение цвета из цветового пространства Linear в диапазоне от 0.0 до 1.0. Функция возвращает значение в цветовом пространстве sRGB в диапазоне от 0 до 255.
Например, для красного цвета:
1 2 3 4 |
linear_color = 0.5 print(linear_color, '-->', linear_to_srgb(linear_color), '(', hex(linear_to_srgb(linear_color)), ')') # 0.5 --> 188 (0xbc) |
Преобразовав таким образом все значения цветов пикселей изображения, мы можем сохранить его в файл без потери цвета.
При необходимости можно сделать и обратное преобразование из цветового пространства sRGB в Linear.
1 2 3 4 5 6 |
def srgb_to_linear(value): value /= 255.99 if value <= 0.04045: return value / 12.92 else: return ((value + 0.055) / 1.055) ** 2.4 |
В эту функцию в параметре мы передаем цвет из цветового пространства sRGB в диапазоне от 0 до 255, а на выходе получаем цвет в цветовом пространстве Linear в диапазоне от 0.0 до 1.0.
1 2 3 |
print('188 (0xbc)', '-->', round(srgb_to_linear(0xbc), 2)) # 188 (0xbc) --> 0.5 |