Undefined behavior on nvidia graphics cards

I’m running a R9 390 and using openGL 3.3.
I create a glTexImage3D texture array and use its layers as different textures of the same size.
In the vertex shader I pass
layout(location = 4) in float TextureSelected;
which I pass to the fragment shader as
out float ts;
In the fragment shader I select the desired texture as

int tnum = int(ts);
vec3 MaterialDiffuseColor = texture(DiffArray, vec3(UV, tnum)).rgb;

And it works great. Thus, I use one shader and one program call to draw an array of vertices with different textures (layers).
But on the GTX 1650 the textures are flickering and basically it’s 2 values - the current texture and the previous one (-1). That is, relatively speaking, we have 2 triangles that use 2 different textures (layers) and they flicker.

It turned out that the data was distorted when transferring data from the vertex shader to the fragment one.
I now immediately pass integers, but with the flat modifier.
In the vertex shader

flat out int tnum;

In the fragment shader

flat in int tnum;

I don’t know why the float transmission is distorted, but here is what ChatGPT answered about the flat

In OpenGL GLSL shaders, integer varying variables must have the “flat” qualifier to inform OpenGL that they should be interpolated flatly between vertices and fragments. This is necessary because integer variables cannot be linearly interpolated like floating-point numbers.

I would like to know where I made mistakes with float) But in general, you can close the topic since the problem is solved.

The most likely reason is that the TextureSelected attribute doesn’t have the same value for all vertices of a triangle, so it gets interpolated.

Attributes are per-vertex. If you want per-triangle data, either each triangle must have its own set of vertices (i.e. no vertex is shared between triangles), or you need to order the vertices of each triangle so that triangles which don’t share an attribute don’t share a provoking vertex, assign attribute values to the provoking vertex for each triangle, and use flat-qualified variables in the shader.

The latter option is non-trivial for arbitrary meshes, as these typically have roughly twice as many triangles as vertices, so you may have to duplicate vertex positions just so you have enough vertices to hold the per-triangle attributes.

In this case, I’m using unique 3 vertices for each triangle. I don’t use vertex indexing.

My guess is that I misunderstand how data is passed between the vertex and fragment shaders. That is, technically I’m doing it right, but since the data is distorted, I don’t know the nuances. Let’s say my ts passes the value 2.0 to the fragment shader. What value will be after receiving, and how will it react after executing the following command?
tnum = int(ts);
Logically, I should get 2. But in practice, it rounds the wrong way and flickers. The approach described in the second post solves this problem.

If you need to do something similar to this in future, use round, e.g.
tnum = int(round(ts));

A plain constructor is equivalent to
tnum = int(floor(ts));
(at least for positive values). floor will convert 2.001 to 2 but 1.999 to 1, so it’s not a good choice for converting a value which is an integer plus or minus some small error.

If an attribute value is an integer, in OpenGL 3.0 and later you can just use an integer attribute, e.g.

layout(location = 4) in int TextureSelected;

In that case, the attribute array must be specified with glVertexAttribIPointer rather than glVertexAttribPointer (similarly, glVertexAttribIFormat or glVertexArrayAttribIFormat rather than glVertexAttribFormat or glVertexArrayAttribFormat).

1 Like