# Texcoord weirdness using ARB vertex program

Hello everybody,

I’m writing an ARB vertex program to implement simple per-vertex Lambert lighting. I’m calculating the light intensity in the vertex program (0.0…1.0). I first used it as a texture sample coordinate and noticed something wrong, so I reduced it to isolate the problem and added a fragment program to visually debug the values.

This is what I’ve got currently:

``````
!!ARBvp1.0
PARAM mvp[4]    = { state.matrix.mvp };
PARAM light_pos = program.env[0];

TEMP light_vec;
TEMP nlight_vec;

DP4 result.position.x, mvp[0], vertex.position;
DP4 result.position.y, mvp[1], vertex.position;
DP4 result.position.z, mvp[2], vertex.position;
DP4 result.position.w, mvp[3], vertex.position;

# Distance vector from light position to vertex position
SUB light_vec, light_pos, vertex.position;

# By-the-book normalization for light_vec
DP3 nlight_vec.w, light_vec, light_vec;
RSQ nlight_vec.w, nlight_vec.w;
MUL nlight_vec.xyz, nlight_vec.w, light_vec;

# Dot product to compute the intensity of light for the vertex
DP3 result.color.x, nlight_vec, vertex.normal;
DP3 result.texcoord.x, nlight_vec, vertex.normal;

END

``````

As you can see I write the same value to both result.color.x and result.texcoord.x; this is for debug, see the fragment program:

``````
!!ARBfp1.0
MOV result.color.x, fragment.texcoord.x;
MOV result.color.y, fragment.color.x;
MOV result.color.z, fragment.color.x;
END

``````

At this point I would expect the primitives to be rendered in greyscale, because I’m passing what should be three identical values to the r, g and b components of the fragment.

Instead, the less lit fragments are bluish, indicating that the red coordinate (coming from the texcoord) is lower than the others. How is this possible? Both color and texcoord attributes in the vp are filled with the same value, the result of nlight_vec . vertex.normal. I feel like I’m missing something obvious, as it’s been almost 10 years since I last wrote a vertex program, but I couldn’t figure out what so far.

Color is generally interpolated across the polygon at a lower precision than texcoords (note: this may be driver-dependent), so you can expect slightly different output.

I think it’s because texcoords are usually interpolated to be correct in regards to perspective while any other value is just interpolated using screen coordinates.
if you change the line
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
to GL_FASTEST you might see a change in the result if this is the case (you should have it set to GL_NICEST though).
Of cause, this is highly dependent on drivers

Thanks for your answers! I think I’ve figured it out: it was actually expected behaviour. This is the part where the vp writes to the color and texcoord attributes:

``````
...
# Dot product to compute the intensity of light for the vertex
DP3 result.color.x, nlight_vec, vertex.normal;
DP3 result.texcoord.x, nlight_vec, vertex.normal;

``````

Now, by definition, the dot product of two vectors A and B can be negative if their enclosing angle AB is higher than pi/2; in fact, the formal definition of the dot product is:

``````
A.B = |A||B|cos(AB)

``````

The result of the first DP3 instruction is clamped between [0…1] by the driver because only these values are meaningful for a color. The result of the second DP3 instruction instead is used verbatim, because negative values are legal for texcoords (think about GL_REPEAT mode for textures).

So in the end it was my initial assumption that was wrong: I was not writing the same value to color and texcoord attributes, at all! I was seeing bluish fragments with my debug fragment program because the red component was lower, due to texcoord interpolation starting from a lower (negative) value, which wasn’t happening for green and blue component, whose color interpolation started from value 0.

zeoverlord, About switching GL_PERSPECTIVE_CORRECTION_HINT to GL_FASTEST, I tried that and noticed no difference; as you said, hints’ interpretation is highly dependent by drivers, and probably mine just always uses GL_NICEST (I’m on a nVidia GeForce 9600M GT).