[C++] Unwanted color gradient with colored vertex meshes display conduit

Hello,

I’ve noticed this so far only with meshes having colored vertices (field m_C): when drawing them in a display conduit, in shaded mode there’s a sort of gradient:

I need to draw them exactly as they look when a mesh is just added to the scene in Shaded mode:

How do I get this? In my conduit I draw meshes like this:

dp.DrawWireframeMesh(*m_pMesh, RGB(0, 0, 0));
dp.DrawShadedMesh(*m_pMesh);

Here I attach a sample illustrating the issue (similar to @dale’s SampleDrawColorMesh):
cmdSampleDrawMesh.cpp (3.6 KB)

By the way, in Rendered mode the conduit gradient disappears, though there seems to be a clipping issue:

Many thanks,
Pablo

@jeff, can you look into this?

What you’re calling a “gradient” is really called “Shading”. When a mesh has vertex colors, the default behavior is to not shade the mesh. What happens if you check “Shade vertex colors” in the display panel? (see pic)

Another observation is that you’re using dp to get at the display attributes… Which is fine, but every conduit has their own copy of the display attributes, which get copied going into and out of your conduit channel (the member m_pDisplayAttrs). I mention this because if you ever plan on changing a display attribute in your conduit(s), then you must do so using the member I just mentioned…changing the pipeline’s attributes will only be temporary within your conduit, since the pipeline will copy the conduit’s attributes back into its own copy post execution of your conduit. Hope that makes sense.

-Jeff

Also, the reason you don’t see the shading in Rendered mode is because the default for Rendered mode is to use the Skylight lighting model…which produces no specular highlights. If you want standard shading in Rendered mode, then you’ll need to turn off the Skylight in the Rendered tab… or add a light type that produces specular highlights (i.e. A directional light).

-J

Hi @jeff,

I’ll check that as soon as I get back to my workstation.

I see. So this eventually boils down to: how exactly do I change the attributes of an object I’m displaying in a conduit? I know how to do that with CRhinoObject, like this (using ON_DisplayMaterialRef dmr)

    CRhinoObject* obj; //This object comes from a reference to a scene object
    // Make a copy of the object's attributes
    ON_3dmObjectAttributes attributes = obj->Attributes();
    // Add a display material reference
    attributes.AddDisplayMaterialRef( dmr );
    // Modify the object's attributes
    context.m_doc.ModifyObjectAttributes( ref, attributes );

Or using CRhinoObject::ModifyAttributes(attributes)

What would be the equivalent of the code above (for example to set up an ON_DisplayMaterialRef) in a conduit?

Thank you very much,
Pablo

Hi @Pablo_Garcia-Amorena, not sure if this available in C++ but with RhinoCommon you can draw a mesh unshaded with vertex colors without setting up a display material using DrawMeshFalseColors

_
c.

@jeff Yes, this is the attribute doing the thing! How do I access it in the SDK?

Thanks for the hint @clement, this seemed to be in the SDK but is now deprecated by DrawShadedMesh, though this one does not take into account vertex colors. I guess it’s an attribute as discussed above…

Pablo

Well, it gets a little more complicated than just an attribute… There is an attribute yes, and it’s called m_pDisplayAttrs->m_bShadeVertexColors, however, that attribute is only used to determine if “Lighting” should be turned On or Off if/when drawing meshes that contain vertex colors. “Lighting” is what actually produces the shading effect…basically, “shading” is computed by calculating angles between your eye, the surface normal and a vector from the surface to the light source…so when there is no light source, you get no shading (which is what happens in Rendered mode by default because there is no “light source”). The complication is “when” that attribute is used, and when the lighting is turned on or off.

In your example you posted, you’re calling dp.DrawShadedMesh(mesh)… However, that method actually takes two parameters; A mesh and a pointer to a Display Pipeline Material (CDisplayPipelineMaterial)…I’m not going to go into all the differences between a pipeline material vs. an ON_Material, or why the pipeline needs its own material definition… Just suffice it to say, that when the pipeline draws a mesh, it does so using a CDisplayPipelineMaterial to define what the mesh should look like (i.e. Diffuse color, Shine, Transparency, textures, etc, etc…). Since you’re only passing one parameter to DrawShadedMesh(), it means you’re picking the default value for the second parameter, which is just a nullptr. If Rhino sees a nullptr for the material, then it just uses the pipeline’s current display attributes material (m_pDisplayAttrs->m_pMaterial).

So given that, you basically have two choices here:

  1. Modify the display attributes’ material before calling DrawShadedMesh()
    or
  2. Create your own instance of a CDisplayPipelineMaterial, make the appropriate settings, and then pass it as the second parameter to DrawShadedMesh()

Which ever one you choose, in order to get the effect you’re after, you will need to set the “disable lighting” attribute within the material, something like so:

material->m_FrontMaterial.SetDisableLighting(false);

Note: pipeline materials consist of two materials, one for front faces and if needed, one for backfaces… The line above is only setting the flag for the front material…but also note, that unless back face materials have been setup or overridden, then the back faces will also just use the front material settings…keep that in mind if you ever plan on doing anything specific with back faces.

By setting the disable lighting flag to false, you’re actually enabling lighting… I know it seems backwards in thinking, but the default for all materials is to have lighting ON… So I guess the thought process there was to provide a way to turn it off, or “disable” it… And since the default for drawing meshes that contain vertex colors is to turn lighting off, that means you need turn the lighting back on before drawing your vertex colored meshes… Hope that makes sense.

Anyways, that’s what needs to be done.

Let me know if any of that doesn’t make sense and I’ll try to clear it up for you.

-Jeff

Hi @jeff,

Thank you so much for your detailed explanation, I’m getting to understand a bit more about display conduits. :smiley:

I tried (among others) the following lines to disable lighting:

const CDisplayPipelineAttributes* attributes = dp.DisplayAttrs();
CDisplayPipelineMaterial material = *attributes->m_pMaterial;
material.m_FrontMaterial.SetDisableLighting(false);
dp.DrawShadedMesh(*m_pMesh, &material);

It doesn’t work though, no apparent change. I think I’m assigning the material incorrectly because I even tried putting some different colors like material.m_FrontMaterial.SetDiffuse(ON_Color::SaturatedBlue), with no difference… Maybe the fact of coloring the mesh vertices prevents any subsequent material to override the display properties?

I found this example by @dale, SampleMeshFaceColor, that does exactly what I’m looking for! Though, I don’t find where the magic is exactly happening, can you have a look? (This sample comes from a thread about switching face/vertex color interpolation)

Just to provide some context, we are using mesh vertex coloring to represent images coming from medical CT/MRI scans in Rhino. These are normally of resolutions ranging hundreds of pixels. For some reason Rhino’s ON_Texture does not fit this purpose (there’s some undesired warping/downsampling), so we use meshes with vertex coloring. And there’s the issue of this thread. Here I show an 128x128 image/mesh non-shaded (scene object) vs shaded (conduit):

Pablo

Ok, so wait… It looks/sounds like the behavior you want is to not have shading ON, if so then you need to SetDisableLighting(true) in the material… Sorry, it’s getting blurred now with all of the other stuff being mentioned… I guess the best step right now is for you to show provide me a brief explanation of your “process”, so that I know when things are supposed to occur and how your plugin needs to interact with the rest of Rhino.

It also sounds like Dale’s example is working for you? Does the example give you exactly what you want, or does it still lack desired functionality? If so, what is it lacking? I’ll be glad to go over exactly what it’s doing to clear up anything that’s not clear or understood.

You are correct that vertex colors override the “Diffuse” channel in a material. Since there is currently no way to tell a material to use (or not use) its vertex colors instead of the diffuse setting, Rhino needs to make a decision somehow… and it makes that decision based on the “existence” of vertex colors. In other words, if vertex colors exist for a given mesh, then Rhino assumes that’s what should be used for the diffuse component…if they don’t exist, then Rhino simply uses the material’s diffuse component. But as you’ve discovered, there are a lot of other things that need to be considered when drawing a mesh.

When is the conduit drawing this mesh? Is the result you show here gotten by drawing the mesh in the PostDrawObjects() channel? If so, I’m a bit at a loss as to why you’re getting the result you are…can I get the exact source code you used to produce this result?

Lastly,

This sounds like you just haven’t provided the proper texture mapping (texture coordinates) for the given mesh (which might not be very easy to produce), and/or filtering for the texture is OFF. Again, the best way to convey the problem is to provide as simple of an example as you can that shows and details the problem, along with what your expected behavior should be.

P.S.

I didn’t mention this before because I was making a very specific assumption about the source code example you provided…but I’d like to mention it now… Your example does everything in one fell swoop within the RunCommand() function… It creates the conduit, it creates the mesh, it enables the conduit, it then forces an entire redraw of all viewports to occur right then and there, and then it disables and deletes everything on the way out of the function. This is not a very good way to do things, and it’s highly recommended that you do not implement conduit solutions in this manner… The assumption I was making (and I still am), is that this is just a quick example you threw together to demonstrate the problem, so I need to just ignore the overall design and architecture of the “solution” and only focus on what results it produces. So I’d like confirmation on that assumption.

Thanks,
-Jeff

1 Like

@jeff

That fixed the thing!! Awesome, many thanks :smiley:

Ok ok, so now I start to get how it works. Just a couple more questions:

  1. In Dale’s example you can see the lines

    CDisplayPipelineAttributes da(*m_pDisplayAttrs);
    da.m_pMaterial->m_FrontMaterial.SetTransparency(0.0);
    ... //Setting up da and da.m_pMaterial
    dp.DrawFace(m_pChannelAttrs->m_pObject, i, &da);
    

Here, I don’t get the difference between attributes and material. From my understanding, attributes involve the whole scene, and material refers to an object material, right? What is the material m_pMaterial being referenced by da?

  1. What does CRhinoDisplayPipeline::SetupDisplayMaterial(CDisplayPipelineMaterial mat) do?
    Is it the same doing this

       CDisplayPipelineMaterial mat;
       dp.SetupDisplayMaterial(mat);
       dp.DrawShadedMesh(*m_pMesh);
    

than this

      CDisplayPipelineMaterial mat;
      dp.DrawShadedMesh(*m_pMesh, &mat);

?

Yes, also referring to the assumption you made, the actual code I use is indeed more complex than the sample I gave, the conduit is not run in a single command but is a member of the plug-in, etc. The conduit itself is the same though, and the sample is simple enough to reproduce the problem.

Yes, I’ll reproduce the issue with ON_Texture and go through it in another thread asap.

Thanks again for clarifying, conduits are extremely powerful :+1: (but sometimes a bit obscure)
Pablo

Here is a very generalized overview…it’s going to be lengthy and long winded though, so I apologize in advance.

Rhino has/uses the concept of “Display Modes” to define specific visual appearances and effects. Each display mode is a huge collection of values and states that define how the overall frame buffer generation will look if/when that mode is provided to the pipeline. The mode describes “everything” about what can be drawn inside Rhino’s pipeline; from every object type and state, to the world axis icon and background color, to line thicknesses and color, etc…, everything is there. Making specific changes to these settings is what forms the differences between each of the display modes in Rhino. For example: “Shaded” vs. “Wireframe”, “Rendered” vs. “Shaded”, etc, etc… All display modes have exactly the same definition, but their values differ slightly (or a lot), and are what define each specific mode. So from Rhino’s display pipeline perspective, these settings are known as the “display attributes”, and are managed and moved around within the pipeline via the m_pDisplayAttrs member. Note: Rhino comes with several “built-in” display modes, but you as user and a programmer can define and create however many different display modes you want, which are referred to as “custom display modes”…which is a topic for another time.

Rhino’s display pipeline is divided up into logical phases called “channels”, and each channel has a very specific and dedicated purpose and functional responsibility within a single frame buffer pass. Since plugins, commands, and even Rhino itself may want to change, modify, are even disable specific things within that pass, a secondary set of attributes exists called “channel attributes”. However, in order to better manage the logical break up of each channel, Rhino also uses the concept of a “Conduit”, which provides ways to entirely override or slightly modify the behavior of each channel. Modifying channel behavior is done by modifying the channel attributes within your conduit through m_pChannelAttrs…Some of the channel attributes are actually used to communicate and translate specific information across different channels. You will find that there are specific channel attributes that coincide with specific pipeline channels, so when in a specific channel, only those channel attributes are used and payed attention to. Modifying different channel attributes outside the scope of their given channel(s) usage, will have undefined behavior. But that’s not all conduits are for, as you’re discovering…conduits are also used to provide much more than just channel behavior, they also give you the ability to provide all kinds of additional and different functionality to the pipeline.

So when a viewport is updated it starts by giving the display pipeline its “display mode” or more specifically its “display attributes”. From there, the pipeline does the following (again, this is a very generalized overview):

The pipeline is “opened”, and then the logical phases/channels are executed in the following order:

  • Object culling
  • Bounding box calculation
  • Near and Far clipping plane setup and adjustments
  • Viewport Frustum is defined and set
  • The “lighting model” is setup and initialized
  • The frame buffer is initialized and cleared
  • The “background” is drawn
  • The “middleground” is drawn (which is where all object drawing occurs)
  • The “foreground” is drawn
  • Post processing of the current frame buffer is performed

…the pipeline is then “closed”, and the resulting frame buffer is ready to be presented to whatever “device” currently initiated the draw…most of the time that will be the screen/monitor, but there are many other forms of output in Rhino to where the frame buffer can go.

As Rhino moves from channel to channel, both the channel attributes and the display attributes can and will change, depending on what is needed.

Given that, there are also some transient display attributes that exist in order to convey specific states at specific times. Note: These transient settings do not exist in the “Display Modes” (i.e. you as a user can’t set them), but they do exist within the same set of attributes and definition. An example of this would be the m_isHighlighted attribute…that attribute can change from one second to the next depending on what is happening and what is currently “selected”, and it is part of m_pDisplayAttrs, but as I mentioned, it really has nothing to do with a given “Display Mode”, which is why I would call it a “transient” attribute. I mention transient attributes here because the m_pMaterial is also somewhat of a transient value. I say somewhat because display modes can and do define specific material settings, however, the material can change over the course of the entire object drawing phases. The material is what defines “how” a given object is going to look, and is usually only used when drawing some kind of shaded polygon mesh. Take Rhino’s “Shaded” mode for instance. It defines a customized material such that when Shaded mode is in use, all shaded objects look exactly the same…their colors might change, but the overall shading will be exactly the same. Ghosted mode is like Shaded mode, only it also specifies a transparency value so that all objects are drawn using the exact same transparency value… The Rendered mode is where things start to really differ, because in Rendered mode, each object can have its own material defined and assigned to it, which means the shading can differ from object to object, and in most cases, every object looks completely different from the other. The pipeline handles this by copying the object’s material into the display pipeline’s material just before the object is drawn…so if the object doesn’t have a material, the pipeline simply uses the one defined in the given display mode. This allows the pipeline to function and operate exactly the same regardless of which display mode is in use…in other words, the pipeline doesn’t care where the material comes from, it only cares about which material is currently set in m_pDisplayAttrs->m_pMaterial. What’s actually in m_pMaterial is determined by differing logic within the pipeline and is why I sort of consider it a transient setting. So for example, you could create a conduit that turns everything to a shiny red simply by setting m_pMaterial appropriately and at the appropriate time. It wouldn’t matter what display mode was currently active, because you’d be overriding what the pipeline is using to draw the mesh… I hope most of that made sense.

So having said all of that…

Knowing what each attribute does, and when to set or reference a specific attribute is just something that you will learn over time. There are so many possibilities that I cannot and will not list them here…most of those possibilities are ones that you probably will never need to know or come across as a developer, which is why it’s just better to address and ask about specifics each and every time you come up against something you can’t quite figure out or understand.

1 Like

Awesome! Thanks for such a lengthy explanation @jeff

Pablo