2

I am rendering 2 triangles to make a square, using a single draw call with GL_TRIANGLE_STRIP.

I calculate position and uv in the shader with:

vec2 uv = vec2(gl_VertexID >> 1, gl_VertexID & 1);
vec2 position = uv * 333.0f;
float offset = 150.0f;
mat4 model = mat4(1.0f);
model[3][1] = offset;
gl_Position = projection * model * position;

projection is a regular orthographic projection that matches screen size.

In the fragment shader I want to draw each first line of pixels with blue and each second line of pixels with red color.

int v = int(uv.y * 333.0f);
if (v % 2 == 0) {
    color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
} else {
    color = vec4(0.0f, 0.0f, 1.0f, 1.0f);
}

This works ok, however if I use offset that will give me a subpixel translation:

offset = 150.5f;

The 2 triangles don't get matching uvs as seen in this picture:

2 neighboring triangles get different uv

What am I doing wrong?

Rhu Mage
  • 667
  • 1
  • 8
  • 21

1 Answers1

3

Attribute interpolation is done only with a finite precision. That means that due to round-off errors, even a difference in 1 ulp (unit-last-place, i.e. least significant digit) can cause the result rounded to the other direction. Since the order of operations in the hardware interpolation unit can be different between the two triangles, the values prior to rounding can be slightly different. OpenGL does not provide any guarantees about that. For example you might be getting 1.499999 in the upper triangle and 1.50000 in the lower triangle. Consequently when you add an offset of 0.5 then 1.99999 will be rounded down to 1.00000 but 2.000000 will be rounded to 2.00000.

If pixel perfect results are important to you I suggest you calculate uv coordinates manually from the gl_FragCoord.xy. In a case of an axis-aligned rectangle, as in your example, it is straightforward to do.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • But the triangles are using the exact same numbers, how could there be round-off errors? – Rhu Mage May 24 '19 at 20:12
  • @RhuMage: To interpolate the attributes OpenGL has to calculate the barycentric coordinates of each fragment within the triangle. The barycentric coordinates of the same fragment, naturally, is different for the two triangles, so the calculations aren't really the same: for the first triangle it has to interpolate `uv` from vertices `0,1,2`, for the second from vertices `2,0,3`. See [how OpenGL interpolates attributes](https://stackoverflow.com/a/24460895/277176), it talks about the general perspective case but the pipeline still does the same math for 2d projections, just with constant `w=1`. – Yakov Galka May 25 '19 at 00:42
  • Is it possible this is a driver bug? I tested on 5 nvidia cards and 3 amd cards all with latest drivers. 2 out of 3 amd had this problem while none of nvidia had it. – Rhu Mage May 27 '19 at 06:15
  • 2
    @RhuMage: It's not a bug -- there's nothing that guarantees that in the spec so it can happen. I experienced that on nvidia cards too. Just use `gl_FragCoord`. – Yakov Galka May 27 '19 at 17:12