Vertex colours: some work, some don't

I’ve hit a bit of an odd problem: vertex colours seem to be showing up for some objects, but not others.

Specifically, I have a scene that I’m exporting from Blender, and some of the objects have vertex colours. However, not all of these objects show the vertex colours as expected in Panda: When viewing the objects in PView with no lighting, or when attempting to access the vertex colours in my shaders, certain objects appear to have either no vertex colours, or all their vertices coloured white (it’s not clear which). When viewing the objects in PView with the default (non-per-pixel) lights enabled, the vertex colours become visible.

To illustrate, I’ll show two objects, one above the other. Both have vertex colours, and both use the same material (which has “Vertex Color Paint” checked), I believe.

This is what I see in Blender; the cyan colouring is the vertex-colouring in question:

This is what I see in PView with no lights; note the lack of colour on the lower object:

This is what I see in PView with basic lighting enabled; note that the lower object appears to now show the vertex colours:

Looking at the egg file, the two objects appear very similar. Take a look at the following excerpts:

# ...

<Texture> Texture.008 {
  "tex/stoneNormals.png"
  <Scalar> wrap { repeat }
  <Scalar> minfilter { linear_mipmap_linear }
  <Scalar> magfilter { linear_mipmap_linear }
  <Scalar> envtype { normal }
}
<Texture> Texture.007 {
  "tex/sketch3.png"
  <Scalar> wrap { repeat }
  <Scalar> minfilter { linear_mipmap_linear }
  <Scalar> magfilter { linear_mipmap_linear }
  <Scalar> envtype { modulate }
}
<Material> Material.004 {
  <Scalar> ambr { 1 }
  <Scalar> ambg { 1 }
  <Scalar> ambb { 1 }
  <Scalar> emitr { 0 }
  <Scalar> emitg { 0 }
  <Scalar> emitb { 0 }
  <Scalar> specr { 0.5 }
  <Scalar> specg { 0.5 }
  <Scalar> specb { 0.5 }
  <Scalar> shininess { 12.5 }
}

# ...

# This is the object displaying the issue, I believe:
<Group> "inner chamber" {
  <Transform> {
    <Matrix4> {
      3.68231 0 0 0
      0 3.68231 0 0
      0 0 3.68231 0
      0.00287303 -0.125973 6.26969 1
    }
  }
  <VertexPool> "inner chamber" {
    <Vertex> 0 {
      -6.90264 -7.03148 -2.16922
      <UV> {
        0 0.5
        <Tangent> { 0 1 0 }
        <Binormal> { -1 0 0 }
      }
      <RGBA> { 0 1 1 1 }
    }
# < more vertices here... >
<Polygon> {
    <Normal> { 0 0 1 }
    <TRef> { Texture.007 }
    <TRef> { Texture.008 }
    <MRef> { Material.004 }
    <VertexRef> { 30 38 39 37 <Ref> { "inner chamber" } }
  }
# < more polygons here... >

# ...

# This is the one that works, I believe.
<Group> antechamber {
  <Transform> {
    <Matrix4> {
      3.68231 0 0 0
      0 3.68231 0 0
      0 0 3.68231 0
      0.00287303 -0.125973 6.26969 1
    }
  }
  <VertexPool> antechamber {
    <Vertex> 0 {
      -1.27935 -1.4082 5.36891
      <UV> {
        0 0.346031
        <Tangent> { 1.36327e-06 1 0 }
        <Binormal> { -1 1.36327e-06 0 }
      }
      <RGBA> { 0 1 1 1 }
    }
# < more vertices here... >
<Polygon> {
    <Normal> { 0 -1 0 }
    <TRef> { Texture.007 }
    <TRef> { Texture.008 }
    <MRef> { Material.004 }
    <VertexRef> { 62 9 6 59 <Ref> { antechamber } }
  }
# < more polygons here... >

(I’m happy to post the full version of the above, if desired; I stuck to excerpts for the sake of (relative) brevity.)

I’m using Panda 1.9.1, I believe.

So… I’m mystified. Does anyone have any ideas regarding where the problem may lie?

All right, I think that I’ve figured out what set of conditions produces this issue, if not the reason for it: it seems that Panda ignores vertex colours (except when applying the fixed-function, built-in lights, as described above) when all vertices of an object have the same colour applied.

In the above example, applying a slightly different colour to one of the vertices of the bottom object seems to be enough to cause its vertex colours to show up as expected. The top object was thus presumably working because it already had more than one colour across its vertices–note the white section near the top.

This is a slightly annoying issue, but it’s now something that I can work around, I believe.

By Panda’s design, nodePath.setColor call should be equivalent to setting vertex colors on all the vertices. The egg loader in particular makes this assumption. You may have to write your shaders to take this into account.

If you use the p3d_Color vertex attribute in GLSL in Panda 1.10 and above, it should do the right thing - it will contain either the vertex colors or the color set via the setColor call, if any.

I think that the auto-generated shaders aren’t clever enough to figure that if the diffuse color is missing, it should use either the vertex colors, or the node’s assigned color. This distinction is tricky to make in a shader without branching.

I’m not quite sure of what you’re suggesting. Would you elaborate, please?

For the sake of clarity, let me note that I’m not using “setColor” at all–my vertices are being painted in Blender, and then exported. It’s just that painting all of the vertices in exactly the same colour produces the issue described above, while giving at least one vertex a different colour–even if only slightly different–produces the expected result.

It’s as if Panda is looking at the vertices, noting that they’re all the same colour, and then treating that as a special case, perhaps going in the opposite direction to your description: marking the node as lacking vertex colours, but having an assigned uniform colour matching that of the vertices.

Conversely, as I read your description it would seem that vertex painting should be safe: functionally-speaking, at least, “setColor” mimics vertex colouring, and thus the case of a node having the same colour applied to all vertices should work in the same manner as a node with different colours across its vertices.

I’m not clear on the distinction between a “diffuse colour” and an “assigned colour” here; I presume that by the former you’re not referring to a diffuse texture. Do you mean the material’s diffuse colour? If so, then I’m pretty sure that all of my objects have a material with a diffuse colour (which is white in all cases at the moment, I believe).

Unless I’ve missed something in my scene, all that I’m assigning to any of my nodes–working as expected or otherwise–are a material with its default values, two textures (one diffuse and one normal-map), and, in some cases, painted vertex-colours. (Unpainted vertices seem to produce a white vertex colour, which I’m happy with.)

Fair enough. I’m hesitant however to switch over to an unreleased, and thus potentially unstable, version at this point in the project.

Your understanding seems correct. The intent is that setColor should be equivalent to setting the same color on all the vertices. Therefore, the .egg loader looks at all the vertices, and if it notes that they are the same color, it removes the vertex colors and instead replaces it with the equivalent of a .setColor() call.

No, if you look at your .egg file, you’ll see that it misses diffr, diffg, etc. assignments in the section. This is because you enabled “Vertex Color Paint” in Blender, which causes these lines to be emitted when exporting.

The design of Panda’s shading pipeline, modelled after the OpenGL fixed-function pipeline, is that when the material does not specify a diffuse color, the diffuse color instead comes from the setColor or vertex color assignments. This matches what you see in pview when you enable fixed-function lighting. If your material were to specify a diffuse color (ie. you disabled Vertex Color Paint in Blender), it would ignore the vertex color completely.

The exact semantics of this are kind of fiddly, and sometimes hard to emulate in a shader, which is why it’s so easy to get inconsistent results. Perhaps we’ll revise these rules in a future version of Panda. (FWIW, when you don’t enable lighting but you do have a material, all bets are off as to how it will show up - though I suppose it could be considered a bug that Panda doesn’t show the color in the same way then.)

Feel free to ignore this as it’s just a nitpick, but Panda, like OpenGL, does not have a notion of a “diffuse texture” per se, because the fixed-function texture pipeline is applied after the lighting calculations, not before. It’s kind of like a diffuse texture because the specular component is calculated separately nowadays, but not really.

Fair enough. Just keep in mind that in order to get consistent results, you need to consider both attr_color and vtx_color inputs in your shaders. You could multiply them together in order to get the final colour, which will produce the correct colour in most cases.

Hmm… That does seem rather counter-intuitive to me. But I gather that this is addressed in Panda 1.10, so fair enough, and thank you for the explanation!

Ah, I see–fair enough!

Ah, I wasn’t aware that there was a difference–thank you for the clarification. All that I was really referring to, I believe, was a texture that provides the “base colours” of the object, as opposed to normal-maps, gloss-maps, specular maps, etc.

Ah, noted, and thank you.