Failing to dynamically change color. #1566
-
Okay, so I've tried for another two days and I think there is one missing piece somewhere... Setup: //... initializing the vsg
Options = vsg::Options::create();
Options->sharedObjects = vsg::SharedObjects::create();
Options->add(vsgXchange::all::create());
Options->add(vsgXchange::assimp::create());
Options->add(vsg::glsl::create());
Options->shaderSets["pbr"] = vsg::createPhysicsBasedRenderingShaderSet(Options);
//... window creation, compilation aso We then add Nodes dynamically to the scene. We do this by using vsgXchange::assimp and we load .stl files (single object, they have no color attached to them, so vsg seems to assign per vertex positon and normal, but per instance UV and color) I use a visitor to either: change the instance color value from the default 1.0, 1.0, 1.0, 1.0 (even though its rendered in grey, so that might be the first hint) struct DynamicColorVisitor : public vsg::Visitor
{
DynamicColorVisitor() {}
void apply(vsg::Object& object) override
{
object.traverse(*this);
}
void apply(vsg::VertexIndexDraw& vid) override
{
for (auto& buffer : vid.arrays) {
auto data = buffer->data;
if (data->is_compatible(typeid(vsg::vec4Value))) {
// Change the property to dynamic
data->properties.dataVariance = vsg::DYNAMIC_DATA;
data->dirty();
}
}
}
};
struct ChangeColorVisitor : public vsg::Visitor
{
ChangeColorVisitor(vsg::vec4 color)
: Color(color)
{ }
vsg::vec4 Color;
void apply(vsg::Object& object) override
{
object.traverse(*this);
}
void apply(vsg::DescriptorSet& ds) override
{
for (auto& desc : ds.descriptors)
{
auto db = desc.cast<vsg::DescriptorBuffer>();
if (!db) continue;
auto mat = db->bufferInfoList.front()->data.cast<vsg::PbrMaterialValue>();
if (mat)
{
mat->value().baseColorFactor = Color;
mat->dirty();
return; // found it, stop searching
}
}
}
void apply(vsg::VertexIndexDraw& vid) override
{
for (auto& buffer : vid.arrays) {
auto data = buffer->data;
if (data->is_compatible(typeid(vsg::vec4Value))) {
(*(vsg::vec4*)(data->dataPointer(0))) = Color;
data->dirty();
}
}
}
}; I also used the PrintVisitor form the vsg-tutorial, which helps with debugging ALOT :) I checked (using step-by-step debugging) which pipeline is bound to the nodes StateGroup (which is generated automatically when I load a .stl model (should be somethingl ike CullNode->GroupNode->StateGroup->BindGraphicsPipeline etc) And this is where I#m really confused: If the material is changed during runtime, marked as dirty and seem to end up on the GPU AND the (seemingly) correct shader is used, what might the reason be that the model does not change color? I also tried changing the material/vertex color immediately after loading the model BEFORE I start the rendering thread or before I compile the viewer and that did nothing as well. I also tried applying the visitor to both the CullNode as well as the GroupNode that parents that (structure is: GroupNode->MatrixTransform->CullNode->...->VertexIndexDraw). Debugging the data flow here (following the visitor) the data seems to change correctly. Its just either not consumed/updated OR the actually used graphics pipeline is something I do not expect/know... I'm not sure what information I can provide to you to help me figure it out. Thanks alot! I really need to figure this out soon, its a crucial part of the renderer we need. |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 12 replies
-
|
Beta Was this translation helpful? Give feedback.
-
As a general comment, STL is probably amongst the simplest of model types, so the structure that vsgXchange::assimp will create will likely be on the more simple end of spectrum. The quickest way to the look at the scene graph structure it creates is to convert the file to .vsgt (VSG native ascii file format) using vsgconv: vsgconv myfile.stl myfile.vsgt With more complex scene types, such as a gltf model the structure could well be far more complex, with multiple models integrated into a single scene, each one having multiple colours coming from material, vertex colours or texturing. I mention this as a warning that things could get much more complex than a single colour per model. Now if you load a single model and only need to render one instance of it in the scene then traversing the scene graph looking from materials (look in vsg::StateGroup for vsg::BindDescriptorSet's that have vsg::DescriptorBuffer with a vsg::PbrMaterialValue/PhongMaterialValue data entry, or looking for vertex colours assigned to VertexDraw/VerexIndexDraw/Geometry/BindVertexBuffers nodes. If you use this approach then you can modify the data value and then let the scene graph be compiled and this will allocated all the Vulkan objects and make sure everything is copied to the GPU. If you load the model, put into the scene, compile the scene graph then start rendering, then a while later want to modify the colour then you'll need to find the data as before but this time as the associated data on the GPU is already resident a compile is not required, but you still need to tell the vsg::TransferTask (assigned to the Viewer's RecordAndSubmitTasks) that you want to update the data on GPU associated with the colour.. If it's a one off you can call data->dirty() and just assign the data directly to the TransferTask and then it'll be automatically synced for that frame only. If you want to regularly update the colour, such as on every frame, then you'll need to set the data->dataProperties.dataVarianvce to DYNAMIC_DATA. Doing this will tell the TransferTask that when that data is assigned to it then it'll need to be retained on it's check list so that each subsequent frame it'll be checked to so if it's been dirtied since the last time it was copied, if it's dirty then it'll be copied. This usage approach enables you to set the dataVariance once and won't need to assign to the TransferTask again. The TransferTask prunes data assigned to with with dataVariance set to STATIC_DATA after it's copied or data that no longer has an external references to it, such as when a subgraph is deleted from the main scene graph, but as the TransferTask still holds a reference to it it prevents it from being deleted from memory. This pruning helps deletes this orphaned data objects automatically for you. If you have thousands of dynamic data objects you might want to asses which need dataVariance as DYNAMIC_DATA and which should be STATIC_DATA but just assigned to the TransferData when they are changed. Essentially you don't wan the TransferTask to be stuck checking data dirty status on ten's of thousands of objects every frame if only a few are ever dirty. Things get more complicated when you want to instance a loaded model multiple times, but still want unique properties like colours on each instance of the model. In this case diving into the subgraph to change the data values directly won't work, the only way to provide the data is provide that data from above the shared subgraph. A couple of approach are possible in this case:
The new vsg::InstanceNode functionality allows you to translate, rotate, scale and colour each instance of model that is shared. There are constraints for the types of models it can work with, I outline these constraints in the header doxygen comments. vsg::InstanceNode is a specialist node enabling GPU instancing in a more flexible way that transitional GPU instance like done with vsg::Builder where all the instance data is assigned to the low level mesh. However, it's still using GPU instancing so there constraints to it's usage we can't escape without introducing mesh or compute shaders to do culling, sorting and binning. This is BIG bleeding edge topic, for future development. For now you'll need to work with the constraints. Another caveat is the only the vsgXchange::gltf/Tiles3D loaders know about and support vsg::InstanceNode, the vsgXchange::assimp loader predates the existence of InstanceNode by years. It will be possible to support InstanceNode in vsgXchange::assimp but it's not a quick addition. Looking through the commits to vsgXchange::gltf associated with adding InstanceNode support might give you an idea of where to start it you want to implement this yourself and create a PR for it. |
Beta Was this translation helpful? Give feedback.
-
You may want to examine how assimp and the assimp loader is treating color for a single-color resource like STL (though there are extensions to STL adding 16-bit RGB color to to each triangle). Your ChangeColorVisitor assumes that the color for the entire resource is stored in a single vec4. What you want to verify is that the VkVertexInputRate for your color is VK_VERTEX_INPUT_RATE_INSTANCE and not VK_VERTEX_INPUT_RATE_VERTEX. I have just looked at the vsgt file produced by When I needed to load STL, I did not use vsgXchange, nor use any color attribute in the vertex shader. But instead passed the color of the object through a uniform. |
Beta Was this translation helpful? Give feedback.
-
One way to change the colour of STL files imported via Assimp is to run a
visitor through the graph returned from the read to find
the vsg::PbrMaterialValue nodes. You then set them to DYNAMIC_DATA. From
there, you can update the diffuseFactor value and call dirty(). I also use
this technique for DXF files and most other 3D imports.
Roland Hill
Director
Four Winds Technology Pty Ltd
Spatial Integration <https://www.spatialintegration.com/>
…On Tue, 19 Aug 2025 at 16:45, Dov Grobgeld ***@***.***> wrote:
You may want to examine how assimp and the assimp loader is treating color
for a single-color resource like STL (though there are extensions to STL
adding 16-bit RGB color to to each triangle). Your ChangeColorVisitor
assumes that the color for the entire resource is stored in a single vec4.
What you want to verify is that the VkVertexInputRate for your color is
VK_VERTEX_INPUT_RATE_INSTANCE and not VK_VERTEX_INPUT_RATE_VERTEX. I have
just looked at the vsgt file produced by vsgconv input.stl output.vsgt
and you can see in the vsgt file that this is indeed the case! You can see
this both from the fact that the VertexIndexDraw array of type
vsg::vec4Array (why don't the arrays have names?) only has a single value,
as well as by inspecting the inputRate for the vsgColor attribute
(location=6, binding=3) and verifying that it is 1, i.e.
VK_VERTEX_INPUT_RATE_INSTANCE .
When I needed to load STL, I did not use vsgXchange, nor use any color
attribute in the vertex shader. But instead passed the color of the object
through a uniform.
—
Reply to this email directly, view it on GitHub
<#1566 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAPOEQZJ5Q7BBM7TMBSTKVT3OLBZJAVCNFSM6AAAAACD7DP35KVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTIMJUHE3DOMQ>
.
You are receiving this because you are subscribed to this thread.Message
ID: <vsg-dev/VulkanSceneGraph/repo-discussions/1566/comments/14149672@
github.com>
|
Beta Was this translation helpful? Give feedback.
-
Ok, looking at the standard_pbr.frag shader, that makes sense! You just have to set |
Beta Was this translation helpful? Give feedback.
As a general comment, STL is probably amongst the simplest of model types, so the structure that vsgXchange::assimp will create will likely be on the more simple end of spectrum. The quickest way to the look at the scene graph structure it creates is to convert the file to .vsgt (VSG native ascii file format) using vsgconv:
With more complex scene types, such as a gltf model the structure could well be far more complex, with multiple models integrated into a single scene, each one having multiple colours coming from material, vertex colours or texturing. I mention this as a warning that things could get much more complex than a single colour per model.
Now i…