glTF-Blender-IO
glTF-Blender-IO copied to clipboard
Wrong color index exported (COLOR_0, COLOR_1)
I have a gltf with COLOR_0 and COLOR_1. I import it in blender and then export it in glTF (default parameter).
Issues:
- On Blender 3.3.1, COLOR_1 and COLOR_1 are swapped on export
- On Blender 3.4.0, there is only one channel exported (and it has the data of COLOR_1), even if I use both channels in the shader graph in shading tab.
(both channels have different data, so an easy repro is to simply export-reload to see the difference in shading tab).
On Blender 3.3.1, COLOR_0 is the rendered vertex color slot, the other one is COLOR_1.
There was some refactoring on how vertex color are exported between 3.3 and 3.4. Now, only rendered vertex color are exported as vertex color COLOR_0, and others (so, not rendered, are exported as _COLOR_x .
Maybe linked to #1740
On Blender 3.3.1, COLOR_0 is the rendered vertex color slot, the other one is COLOR_1.
But that's not what the exporter is doing, it definitely exports COLOR_1 data in COLOR_0 slot instead, on both 3.3.1 and 3.4.
This line shows 1 instead of 0, I know nothing about the API but maybe it should be active_color_index or something else. The doc about render_color_index
mentions fallback for rendering but it's a bit vague for me.
In any case, I didn't even fiddle with the blender shader graph, it's the default glTF one:


Loading a glTF, exports it and reloading it shows a different result so there is definitely a bug to me, or I'm missing something.
As for #1740, the @emackey 's main argument on exporting a single channel was This has caused some confusion among artists
. But that's not because there was multiple color channels! It was because COLOR_0 and COLOR_1 were swapped during export.
Also, the current solution doesn't even fix #1740 as it will export COLOR_1 instead (named as COLOR_0).
To me the best solution is simply to export all the channels in the same order as inside blender (the glTF import works perfectly in that regard).
After import, the second color layer Col.001
is the default attribute for rendering (render_color_index == 1). The importer just needs to make sure the first one is always the default.
Somewhat related: the importer should probably also stop setting the name on the Color Attribute node in the shader graph. This will make it always use the default attribute, which would more accurately reflect how it will be exported (by other exporters too).
To me the best solution is simply to export all the channels in the same order as inside blender (the glTF import works perfectly in that regard).
The problem is you can't control the order (some domain/type combos always come first), so you would have no way to control which color attribute becomes the special material-affecting COLOR_0.
After import, the second color layer Col.001 is the default attribute for rendering (render_color_index == 1). The importer just needs to make sure the first one is always the default.
It would help indeed.
The problem is you can't control the order
I'm a bit confused, aren't the "color attributes" section in blender ordered, similarly to UVs? I thought it was handled similarly to UV sets.
Is there any reasons why the following code wouldn't work? I just tested it and it worked fine (3.4.0, both channels exported with correct order). (I don't know much about python or blender internal color handling, so I won't claim it's a good solution, but I'm wondering)
diff --git a/gltf2_blender_gather_primitives_extract.py b/gltf2_blender_gather_primitives_extract.py
index c12a6bdf..0e7aa82a 100644
--- a/gltf2_blender_gather_primitives_extract.py
+++ b/gltf2_blender_gather_primitives_extract.py
@@ -165,12 +165,11 @@ class PrimitiveCreator:
continue
- if self.blender_mesh.color_attributes.find(blender_attribute.name) == self.blender_mesh.color_attributes.render_color_index \
- and self.blender_mesh.color_attributes.render_color_index != -1:
+ if self.blender_mesh.color_attributes.find(blender_attribute.name) >= 0:
if self.export_settings[gltf2_blender_export_keys.COLORS] is False:
continue
- attr['gltf_attribute_name'] = 'COLOR_0'
+ attr['gltf_attribute_name'] = 'COLOR_' + str(self.blender_mesh.color_attributes.find(blender_attribute.name))
attr['get'] = self.get_function()
else:
@@ -539,10 +538,10 @@ class PrimitiveCreator:
def get_function(self):
def getting_function(attr):
- if attr['gltf_attribute_name'] == "COLOR_0":
- self.__get_color_attribute(attr)
- elif attr['gltf_attribute_name'].startswith("_"):
+ if attr['gltf_attribute_name'].startswith("_"):
self.__get_layer_attribute(attr)
+ elif attr['gltf_attribute_name'].startswith("COLOR_"):
+ self.__get_colors_attribute(int(attr['gltf_attribute_name'].split("_")[-1]), attr)
elif attr['gltf_attribute_name'].startswith("TEXCOORD_"):
self.__get_uvs_attribute(int(attr['gltf_attribute_name'].split("_")[-1]), attr)
elif attr['gltf_attribute_name'] == "NORMAL":
@@ -553,9 +552,7 @@ class PrimitiveCreator:
return getting_function
- def __get_color_attribute(self, attr):
- blender_color_idx = self.blender_mesh.color_attributes.render_color_index
-
+ def __get_colors_attribute(self, blender_color_idx, attr):
if attr['blender_domain'] == "POINT":
colors = np.empty(len(self.blender_mesh.vertices) * 4, dtype=np.float32)
elif attr['blender_domain'] == "CORNER":
@@ -877,4 +874,4 @@ class PrimitiveCreator:
self.attributes[attr['gltf_attribute_name']]["data_type"] = gltf2_io_constants.DataType.Vec2
else:
self.attributes[attr['gltf_attribute_name']]["component_type"] = gltf2_blender_conversion.get_component_type(attr['blender_data_type'])
- self.attributes[attr['gltf_attribute_name']]["data_type"] = gltf2_blender_conversion.get_data_type(attr['blender_data_type'])
\ No newline at end of file
+ self.attributes[attr['gltf_attribute_name']]["data_type"] = gltf2_blender_conversion.get_data_type(attr['blender_data_type'])
I just created a PR with @scurest suggestions here #1767
@stephomi We want to export the rendered layer as COLOR_0, even if this is not the first in the loop/list
The rendered layer is the one with active icon
We also have to be careful because we are looping on attributes, and it may not follow the order of color attributes
Thanks, the fix will at least resolve https://github.com/KhronosGroup/glTF-Blender-IO/issues/1740
We also have to be careful because we are looping on attributes, and it may not follow the order of color attributes
Afaik I was only taking the index in the color attributes (self.blender_mesh.color_attributes.find(blender_attribute.name)
)
We want to export the rendered layer as COLOR_0, even if this is not the first in the loop/list
Ok got it, so basically it has nothing to do with how the channel is used in the shader graph. I would have expected a similar behaviour than with the UVs though (re-export all the channels by default, whether used or not)
Also, not sure if it's by design, but the addon doesn't re-import the custom attribute, for example _COL.001 is ignored.
Also, the data (of _COL.001) is incorrect as it's flagged as "U16" but in reality there is float inside. Either it should be flagged as F32 data in the glTF or it should be correctly converted to u16 here.
Thanks, the fix will at least resolve #1740
Aims of the proposition 1740 is to export COLOR_0 only if vertex color are really used in shader graph. It still not the case yet
Also, not sure if it's by design, but the addon doesn't re-import the custom attribute, for example _COL.001 is ignored.
Import of custom attributes is not implemented yet
Also, the data (of _COL.001) is incorrect
You are right. Additional Vertex Color are named based on layer name, not _COLOR* This should be fixed on #1770