GLSL: Accessing multiple texture coordinates

This is essentially a continuation of this old thread–but this time I actually do want an actual UV-map.

(I mean, I suppose that I could follow the same solution and store my UVs in vertex-colours, but that’s a complication that I’d rather avoid.)

In short, I have a model which I have given two sets of UV-maps (in Blender). I’ve exported this via YABEE (yes, I know, I’m using archaic tools; leave me to my flint arrowheads :P), and examination of the egg-file indicates the both sets of UV-maps are present and as expected.

But nevertheless, I cannot seem to access the second set in a GLSL shader. :/

Thus far I’ve tried “p3d_MultiTexCoord1”, “texcoord_<myName>”, and “vtx_texcoords_<myName>”, and none have worked. ``:/```

Here below I include a simple program that inlines a basic shader, as well as a test-model, which demonstrates the issue–on my machine at least.

Specifically, the model is a simple quad, with the first set of UVs covering the full 0 to 1 range, and the second set covering one quadrant of the range. The shader then renders its UV-coordinates as colours in the green- and blue- channels.

For the first set of UVs, this seems to work as expected. For the second, it seems to render either black (the UVs are presumably all zero), or the values for the first set of UVs.

from panda3d.core import loadPrcFile, loadPrcFileData

from direct.showbase.ShowBase import ShowBase
from panda3d.core import Shader

from panda3d import __version__ as pandaVersion
print (pandaVersion)

import sys
print (sys.version)

vert = """
#version 130

in vec4 p3d_Vertex;

in vec4 p3d_MultiTexCoord0;
in vec2 vtx_texcoord_carpetMap;

uniform mat4 p3d_ModelViewProjectionMatrix;

out vec2 coords1;
out vec2 coords2;


void main()
{

    gl_Position = p3d_ModelViewProjectionMatrix*p3d_Vertex;
    coords1 = p3d_MultiTexCoord0.st;
    coords2 = vtx_texcoord_carpetMap.st;
}
"""

frag = """
#version 130

in vec2 coords1;
in vec2 coords2;

out vec4 color;

void main()
{
    //color.xyz = vec3(0, coords1.x, coords1.y);
    color.xyz = vec3(0, coords2.x, coords2.y);
    color.w = 1;
}
"""

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        card = loader.loadModel("coordTest")
        card.reparentTo(render)
        card.setPos(0, 5, 0)
        
        shader = Shader.make(Shader.SL_GLSL, vert, frag)
        card.setShader(shader)


app = Game()
app.run()

coordTest.egg (2.3 KB)

PS: For the “texcoord_<myName>” approach, the second set of UVs is named “carpetMap”.

from panda3d.core import loadPrcFile, loadPrcFileData

from direct.showbase.ShowBase import ShowBase
from panda3d.core import Shader

from panda3d import __version__ as pandaVersion
print (pandaVersion)

import sys
print (sys.version)

vert = """
#version 130

in vec4 p3d_Vertex;

in vec4 p3d_MultiTexCoord0;
in vec2 p3d_MultiTexCoord1;

uniform mat4 p3d_ModelViewProjectionMatrix;

out vec2 coords1;
out vec2 coords2;


void main()
{

    gl_Position = p3d_ModelViewProjectionMatrix*p3d_Vertex;
    coords1 = p3d_MultiTexCoord0.st;
    coords2 = p3d_MultiTexCoord1.st;
}
"""

frag = """
#version 130

in vec2 coords1;
in vec2 coords2;

out vec4 color;

void main()
{
    //color.xyz = vec3(0, coords1.x, coords1.y);
    color.xyz = vec3(0, coords2.x, coords2.y);
    color.w = 1;
}
"""

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        card = loader.loadModel("coordTest")
        card.reparentTo(render)
        card.setPos(0, 5, 0)
        
        shader = Shader.make(Shader.SL_GLSL, vert, frag)
        card.setShader(shader)


app = Game()
app.run()

Strangely, it works for me with the name p3d_MultiTexCoord1. As for the input given by vtx_texcoord, then this applies to the Cg syntax.

Huh–that is very strange!

Are you sure that you’re not getting the coords for the first set of UVs?

Note that the first set of UVs runs from 0 to 1 on both axes, while the second set covers only one quadrant–I think 0 to 0.5 on U and 0.5 to 1 on V.

As a result, I would expect the shader to output colour ranging from black to half-green on the x-axis, and from half-blue to full-blue on the y-axis. However, I seem to be seeing colour ranging from black to full-colour on both axes.

That is, I would expect something like this:
colours

But I’m instead seeing something like this:

The latter is, however, I would would expect when outputting “coords1” (i.e. by uncommenting the first line in the fragment shader’s “main” function and commenting-out the line after that).

Ah, this is the data from the first column. Then this is clearly a problem.

1 Like

Add a dummy texture to a texture stage that references that UV set.

Alternatively, rename the vertex column in the GeomVertexFormat so that there’s no period in the name, then it can be treated like any arbitrary aux data column.

I think it would be good to have a good way to bind this, please file a feature request - possibly using an explicit call on the shader to bind a variable to a known set, or just with some name mangling scheme.

The model posted above lacked textures just for simplicity’s sake; I’m seeing the same effect when I apply two textures (or more accurately, the same texture twice) to the object in a material.

Here below is a version that references a texture; to get it to work, place alongside it a folder named “tex” containing a texture named “colours.png”.

coordTest.egg (2.7 KB)

Unless there’s an aspect of connecting the texture to a texture-stage that I’m missing…? (Specifically, when exporting a model, rather than assigning textures in code.)

I’m… not entirely clear on what you’re suggesting here.

Looking at the egg-file, I’m not seeing antying with a full-stop in the name, aside from the name given to the texture.

I’m also not sure of how to change a column of a format in an egg-file–and don’t really want to put in place custom Python code for handling this one shader…

That does sound useful–but I’ll confess, if I understand you correctly, less so for models produced in and exported from modelling packages. It would be awkward to have to implement custom Python code for handling multiple UV-maps…

I misunderstood the problem, apologies.

p3d_MultiTexCoord0/1 should work. And when I test it, it does seem to work. I had to change the version number to 150 since 130 is not supported by my driver.

I need more information about your Panda version, system, etc. so we can determine why it’s different for you.

Hmm… This is interesting!

Trying the code again, it does work–as long as there are textures applied! That is, your earlier suggestion of adding a texture does seem to fix the problem!

… Which prompts the question of why it wasn’t working in the original application, which did have textures applied… o_0

Well, at the least we’ve ascertained that it does work, and what seems to be important for it to work.

I thus intend to investigate the original application–or more precisely, the model that was exhibiting the problem there–to see what I might have missed.

Maybe I just didn’t try one particular combination of elements! (It was a trying afternoon, for extraneous reasons, I will confess.)

But right now it’s late here, so I intend to do that tomorrow!

Thank you both for your help! :slight_smile:

Okay, so this is frustrating! :/

The exact same shader code, dropped into the original application, once again only shows one set of UV-coordinates. (It seems to be whichever is selected in Blender at export-time.)

Conversely, the model from the original application, dropped into the test-program above, works as expected!

It’s really weird. o_0 :/

So, I suppose that my question is this: What could be interfering with the UV-coordinates coming through as expected…?

I’ve tried comparing the materials, and they seem much the same. (And in any case, as noted above, the same model works in the test-program.)

The model in question is part of a larger, more-complex hierarchy. It’s the first model in that hierarchy that has multiple UV-maps, and the first in that hierarchy that uses the relevant shader.

Various nodes above it use other shaders, or have various shader-inputs applied. Some of those shader-inputs are unused by relevant shader.

Any thoughts…?

Is there a third texture applied on a higher level that is affecting the node in question?

Hmm… There might be: I have at least one default texture applied to a node further up the hierarchy, for example.

However, that one is applied as a shader-input, using a custom name (“variationNoiseTex”), rather than being applied via “setTexture” or the like.

Conversely, based on a quick “render.ls()”, I don’t see a TextureAttrib above the node in question… (But it’s possible that I missed it; the scene-hierarchy is quite large.)

You can analyze the “net state” of a NodePath, which includes all the attributes up to the root.

Ah, I think that I looked for a “findNetTexture” method, or something like that–but didn’t think to look for a “getNetState” method! Thank you for pointing that out!

Okay, here is the result:
S:(ColorScaleAttrib CullFaceAttrib CullBinAttrib RescaleNormalAttrib ShaderAttrib)

I’m not seeing a TextureAttrib…

(I will note that the node itself has a MaterialAttrib and a TextureAttrib, as one might expect.)

Okay, I think that I’m going to give up on this: it’s proving frustrating, and this is for me a poor time for frustration, I feel.

For my purposes, I think that I might get away with just scaling the standard set of UVs.

For what it’s worth, some investigation seems to suggest that it might be down to the texture-entry in the egg-file lacking a tag that relates it to the second UV-map. However, I haven’t tested this with my actual application.

I checked the bam file without textures, it doesn’t work either. The strange thing here is that access to the columns of vertex data used to work in the shader earlier.

It may be worth testing early versions, Panda3D or Cg shaders.

I did an investigation thanks to an rdb post about a possible problem with the dot. And yes!, this is the reason. However, I tested with arbitrary columns.

I recorded a bam like this.

Array 0 (0,000,017,135,521,A90, [ vertex(3f) normal(3f) color(4b) color_red(4b) color_green(4b) texcoord(2f) ]):
  row 0:
    vertex -1 -1 0
    normal 0 0 1
    color 0 0 1 1
    color_red 1 0 0 1
    color_green 0 1 0 1
    texcoord 0 0
  row 1:
    vertex 1 -1 0
    normal 0 0 1
    color 0 0 1 1
    color_red 1 0 0 1
    color_green 0 1 0 1
    texcoord 1 0
  row 2:
    vertex 1 1 0
    normal 0 0 1
    color 0 0 1 1
    color_red 1 0 0 1
    color_green 0 1 0 1
    texcoord 1 1
  row 3:
    vertex -1 1 0
    normal -0 0 1
    color 0 0 1 1
    color_red 1 0 0 1
    color_green 0 1 0 1
    texcoord 0 1

Here is my code.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import Shader

vert = """
#version 130

in vec4 p3d_Vertex;

in vec4 color_red;
in vec4 color;
in vec4 color_green;

uniform mat4 p3d_ModelViewProjectionMatrix;

out vec4 color_1;
out vec4 color_2;
out vec4 color_3;

void main()
{

    gl_Position = p3d_ModelViewProjectionMatrix*p3d_Vertex;
    color_1 = color_red;
    color_2 = color;
    color_3 = color_green;
}
"""

frag = """
#version 130

in vec4 color_1;
in vec4 color_2;
in vec4 color_3;

out vec4 color;

void main()
{
    color = color_3;
}
"""

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        card = loader.loadModel("plane.bam")
        card.reparentTo(render)
        card.setPos(0, 5, 0)
        
        shader = Shader.make(Shader.SL_GLSL, vert, frag)
        card.setShader(shader)

app = Game()
app.run()

It works as expected, the problem was with this:
color.red 1 0 0 1

My exporter followed the EGG syntax, now I did it through underscores.
color_red 1 0 0 1

I think the problem with texture coordinates is similar.

plane.bam (1.1 KB)

Add:
This also works if you don’t use a dot.
in vec2 texcoord_carpetMap;
Thus, the texture stage is not required, but you need to make a correction in YABEE.

Add:
Hmm, it seems that the problem is in the EGG loader, so the dot is added after loading…

1 Like

Interesting!

Although I don’t think that my uv-map name had a dot. Unless it was a dot somewhere else in the hierarchy that was messing things up… that would, at least, explain why it seemed to work in one application but not in another… :/

Well, thank you for your investigation! It is helpful! :slight_smile:

As to YABEE, it might indeed be a good idea for me to (further) update my version.

Or, one of these days, switch to a newer version of Blender and a newer exporting method… ^^;

Please note, I made a mistake, the point is added by the EGG loader, just print the vdata of your model file.

1 Like