-
Notifications
You must be signed in to change notification settings - Fork 21
/
apply_color_palettes_done.py
251 lines (184 loc) · 8.61 KB
/
apply_color_palettes_done.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
"""
This script is used to render color palettes in Blender. It loads color palettes from a JSON file, selects a random color palette, and updates the colors of materials and nodes in the Blender scene based on the selected palette. The scene is then rendered and saved as a PNG image.
The script contains several helper functions for tasks such as setting the random seed, converting hex color strings to RGBA values, selecting random color palettes, choosing random colors from a palette, and updating colors in the scene.
To use the script, simply run it. By default, it will render all the color palettes loaded from the JSON file. You can also specify a specific color palette index and random seed to render a single palette.
Note: This script assumes that the JSON file containing the color palettes is located at the path specified in the `load_color_palettes` function.
"""
import json
import math
import pathlib
import random
import time
import bpy
################################################################
# helper functions BEGIN
################################################################
def time_seed():
"""
Sets the random seed based on the time
and copies the seed into the clipboard
Returns:
- seed (int): The random seed based on the current time.
"""
seed = int(time.time())
print(f"seed: {seed}")
random.seed(seed)
# add the seed value to your clipboard
bpy.context.window_manager.clipboard = str(seed)
return seed
def hex_color_str_to_rgba(hex_color: str):
"""
Converting from a color in the form of a hex triplet string (en.wikipedia.org/wiki/Web_colors#Hex_triplet)
to a Linear RGB with an Alpha of 1.0
Args:
- hex_color (str): The hex color string in the format "#RRGGBB" or "RRGGBB"
Returns:
- rgba_color (tuple): The Linear RGB color with an Alpha of 1.0
"""
# remove the leading '#' symbol if present
if hex_color.startswith("#"):
hex_color = hex_color[1:]
assert len(hex_color) == 6, "RRGGBB is the supported hex color format"
# extracting the Red color component - RRxxxx
red = int(hex_color[:2], 16)
# dividing by 255 to get a number between 0.0 and 1.0
srgb_red = red / 255
linear_red = convert_srgb_to_linear_rgb(srgb_red)
# extracting the Green color component - xxGGxx
green = int(hex_color[2:4], 16)
# dividing by 255 to get a number between 0.0 and 1.0
srgb_green = green / 255
linear_green = convert_srgb_to_linear_rgb(srgb_green)
# extracting the Blue color component - xxxxBB
blue = int(hex_color[4:6], 16)
# dividing by 255 to get a number between 0.0 and 1.0
srgb_blue = blue / 255
linear_blue = convert_srgb_to_linear_rgb(srgb_blue)
alpha = 1.0
return tuple([linear_red, linear_green, linear_blue, alpha])
def convert_srgb_to_linear_rgb(srgb_color_component):
"""
Converting from sRGB to Linear RGB
based on https://en.wikipedia.org/wiki/SRGB#From_sRGB_to_CIE_XYZ
Args:
- srgb_color_component (float): The sRGB color component value
Returns:
- linear_color_component (float): The linear RGB color component value
"""
if srgb_color_component <= 0.04045:
linear_color_component = srgb_color_component / 12.92
else:
linear_color_component = math.pow((srgb_color_component + 0.055) / 1.055, 2.4)
return linear_color_component
def choose_random_color(palette, exclude_colors=None):
"""
Chooses a random color from the given palette, excluding the specified colors if provided.
Args:
- palette (list): The color palette to choose from.
- exclude_colors (list, optional): The colors to exclude from the selection.
Returns:
- color (str): The randomly selected color.
"""
if not exclude_colors:
return random.choice(palette)
while True:
color = random.choice(palette)
if color not in exclude_colors:
return color
################################################################
# helper functions END
################################################################
def load_color_palettes():
"""
Loads the color palettes from a JSON file.
Returns:
- color_palettes (list): The list of color palettes loaded from the JSON file.
"""
# https://github.com/CGArtPython/get_color_palettes_py/blob/main/palettes/1000_five_color_palettes.json
path = pathlib.Path.home() / "tmp" / "1000_five_color_palettes.json"
with open(path, "r") as color_palette:
color_palettes = json.loads(color_palette.read())
return color_palettes
def setup_scene(palette_index, seed=0):
"""
Sets up the scene for rendering with the specified palette index and seed.
Args:
- palette_index (int): The index of the color palette to use.
- seed (float, optional): The random seed to use. If not provided, a new seed will be generated based on the current time.
"""
if seed:
random.seed(seed)
else:
seed = time_seed()
project_name = "applying_1k_color_palettes"
render_dir_path = pathlib.Path.home() / project_name / f"palette_{palette_index}_seed_{seed}.png"
render_dir_path.parent.mkdir(parents=True, exist_ok=True)
bpy.context.scene.render.image_settings.file_format = "PNG"
bpy.context.scene.render.filepath = str(render_dir_path)
def prepare_and_render_scene(palette, palette_index, seed=None):
"""
Prepares and renders the scene with the specified palette and index.
Args:
- palette (list): The color palette to use for updating the colors.
- palette_index (int): The index of the color palette.
- seed (float, optional): The random seed to use. If not provided, a new seed will be generated based on the current time.
"""
setup_scene(palette_index, seed)
update_colors(palette)
bpy.ops.render.render(write_still=True)
def render_all_palettes(palettes):
"""
Renders all the color palettes.
Args:
palettes (list): A list of color palettes to be rendered.
"""
# make sure we are using the EEVEE render engine for faster rendering
bpy.context.scene.render.engine = "BLENDER_EEVEE"
start_time = time.time()
for palette_index, palette in enumerate(palettes):
prepare_and_render_scene(palette, palette_index)
# remove the following line to render all the palettes
break
end_time = time.time()
execution_time = end_time - start_time
print(f"Execution time: {execution_time} seconds")
def update_colors(palette):
"""
Updates the colors of the materials and nodes in the Blender scene based on the given palette.
Args:
- palette (list): The color palette to use for updating the colors.
"""
random.shuffle(palette)
palette = [hex_color_str_to_rgba(hex_color) for hex_color in palette]
backdrop_bsdf_node = bpy.data.materials["backdrop"].node_tree.nodes["Principled BSDF"]
backdrop_color = choose_random_color(palette)
backdrop_bsdf_node.inputs["Base Color"].default_value = backdrop_color
upper_platform_bsdf_node = bpy.data.materials["upper_platform"].node_tree.nodes["Principled BSDF"]
platform_color = choose_random_color(palette, exclude_colors=[backdrop_color])
upper_platform_bsdf_node.inputs["Base Color"].default_value = platform_color
pillar_bsdf_node = bpy.data.materials["pillar"].node_tree.nodes["Principled BSDF"]
pillar_color = choose_random_color(palette, exclude_colors=[backdrop_color, platform_color])
pillar_bsdf_node.inputs["Base Color"].default_value = pillar_color
icing_bsdf_node = bpy.data.materials["icing"].node_tree.nodes["Principled BSDF"]
icing_color = choose_random_color(palette, exclude_colors=[backdrop_color])
icing_bsdf_node.inputs["Base Color"].default_value = pillar_color
color_ramp = bpy.data.materials["sprinkles"].node_tree.nodes["ColorRamp"].color_ramp
color_ramp.elements[0].color = choose_random_color(palette, exclude_colors=[icing_color])
color_ramp.elements[1].color = choose_random_color(palette, exclude_colors=[icing_color])
color_ramp.elements[2].color = choose_random_color(palette, exclude_colors=[icing_color])
color_ramp.elements[3].color = choose_random_color(palette, exclude_colors=[icing_color])
def main():
"""
The main entry point of the script.
"""
palettes = load_color_palettes()
palette_index = None
seed = None
if palette_index is not None and seed is not None:
bpy.context.scene.render.engine = "CYCLES"
selected_palette = palettes[palette_index]
prepare_and_render_scene(selected_palette, palette_index, seed)
return
render_all_palettes(palettes)
if __name__ == "__main__":
main()