T
by theoryshaw on 22 Apr 2024, edited 26 Apr 2024
#
The following video shows how to 'drive' an object's viewport display color by its base color.
Is there a way, however, to 'drive' an object's viewport display color by the result color from a 'Combine Color' node, for example? Basically want to average all the colors of a texture, and apply that color to the viewport display color.
... but still have the texture viewable in 'Rendered' or 'Material Preview' mode.
S
by stefanm2 on 22 Apr 2024
#
+1 votes
Hi, I've found that add-on.
https://www.blendernation.com/2020/03/06/free-addon-to-set-viewport-color-based-on-nodetree/
T
by theoryshaw on 22 Apr 2024
#
+1 votes
cool!... will give it a spin.
T
by theoryshaw on 22 Apr 2024
#
+1 votes
hmm... doesn't seem to work on my side (blender 4.1). It just sets the viewport display color to white.
T
by theoryshaw on 22 Apr 2024
#
@Gorgious provided some insight as well, here: https://blender.stackexchange.com/a/316981/97239
G
by Gorgious on 24 Apr 2024, edited 24 Apr 2024
#
+2 votes
Hey I'll expand a bit on my BSE answer and try and answer the concerns here.
What the addon does if I understood correctly is aggregate the default values of the color input of all the principled bsdfs of each material, and then set the viewport color accordingly. It does not work if you plug in a noodle coming from a texture node in the color input of the principled bsdf, so it will never work if you're using a PBR based workflow. The reason is because plugging in the noodle tells the node to ditch the default value and wait for the previous node to evaluate its output. For technical and optimization reasons this information is not available to the API. You can see the noodles as a pipeline, and you can only tweak the leftmost "default" values and you can only access the rightmost output from the material, and even then only if you bake the result to a texture. The Blender version won't change this fact, sorry.
The information is not statically available. You need to actively analyse the data from the texture map if you want to achieve your goal. I think the simplest way is as described in my answer, read the pixel colors and average them. This could be turned into an addon but I advise against running this script continuously, even if foreach_get
is extremely fast, the information is not bound to change until the texture map itself changes so a button seems more reasonable.
import bpy
import numpy as np
img = bpy.data.images["Image.png"] # Case sensitive
x, y = img.size
pixels = np.empty(x * y << 2, dtype=np.float32)
img.pixels.foreach_get(pixels)
mean_color_rgb = np.mean(pixels.reshape((x, y, 4))[:, :, :3], axis=(0, 1)) # Don't average alpha value
mat = bpy.data.materials["Material"] # Case sensitive
mat.diffuse_color = list(mean_color_rgb) + [1] # Add alpha value to the mean
Cheers
T
by theoryshaw on 24 Apr 2024
#
Thanks @Gorgious !
Let's say you have a texture that is predominantly a red color, and you use a blue color in a 'color ramp' node.
Is there a clever way to set the viewport display color as the resultant purple color? It would seem not, from your explanation above, but wanted to ask just in case.
T
by theoryshaw on 25 Apr 2024, edited 25 Apr 2024
#
I guess i should say something like a 'mix node', not a 'color ramp'--same concept though.
Something like the following example. Possible to get the average color at .5 factor, for example?

...
G
by Gorgious on 25 Apr 2024, edited 25 Apr 2024
#
Everything is possible in Blender :) The only problem is how to tackle the issue. For very specific scenarii you'll need to write specific scripts. I omitted try/except and if/else clauses so this will throw error on other materials.
import bpy
import numpy as np
def get_mean_color(img):
x, y = img.size
pixels = np.empty(x * y << 2, dtype=np.float32)
img.pixels.foreach_get(pixels)
mean_color_rgb = np.mean(pixels.reshape((x, y, 4))[:, :, :3], axis=(0, 1)) # Don't average alpha value
return mean_color_rgb
obj = bpy.context.active_object
mat = obj.data.materials[obj.active_material_index]
principled_bsdf = next(n for n in mat.node_tree.nodes if n.type == "BSDF_PRINCIPLED")
color_socket = principled_bsdf.inputs["Base Color"]
mix_node = color_socket.links[0].from_node
factor = mix_node.inputs["Factor"].default_value
texture_node_a = mix_node.inputs["A"].links[0].from_node
texture_node_b = mix_node.inputs["B"].links[0].from_node
image_a = texture_node_a.image
image_b = texture_node_b.image
image_a_mean_color = get_mean_color(image_a)
image_b_mean_color = get_mean_color(image_b)
mat.diffuse_color = [
image_a_mean_color[0] * (1-factor) + image_b_mean_color[0] * factor,
image_a_mean_color[1] * (1-factor) + image_b_mean_color[1] * factor,
image_a_mean_color[2] * (1-factor) + image_b_mean_color[2] * factor,
1
]
While I was writing this I noticed that the material icon in the material list updated when you changed the factor. There might be a way to tap into the material icon system to get the icon color. I unfortunately don't know much about it but that's a lead. I think the icons are dynamically re-rendered at very low resolution every time you change the material.

T
by theoryshaw on 25 Apr 2024
#
For very specific scenario you'll need to write specific scripts.
Yeah, that would be less than ideal, as the shader node setup changes a lot, from one material definition to the next.
There might be a way to tap into the material icon system to get the icon color.
That would be awesome.
T
by theoryshaw on 25 Apr 2024
#
+2 votes
Whoops, i got reprimanded by the development coordinator. Guess it wasn't a 'developer-enough' question. :)
G
by Gorgious on 26 Apr 2024
#
Hehe, people can be a bit blunt, don't get discouraged. :)