Greetings,
I have a small issue with OpenGL texturing, which is not related to any GPU in particular. I've reproduced this problem with Radeon GPU and Mali GPU, so it's just a "I don't understand OpenGL" problem. However, I'm putting this in the ARM Mali graphics section, as it seems to be the most adequate section by elimination, and I'd like to know how this problem is called in OpenGL terms.
My problem is that when I use a texture atlas and apply a part of the texture atlas to a quad, it sometimes picks one more pixel from contiguous parts of the texture atlas.
The same quad displayed at different zones of the screen will glitch differently.
The problem tends to disappear when I use GL_NEAREST with GL_TEXTURE_???_FILTER, instead of GL_LINEAR. However, the texture become awfully pixelated then.
Here's an example of the code used :
psychic-octo-waffle/myy.c at master · Miouyouyou/psychic-octo-waffle · GitHub
The code basically generates two cards, divided in one opaque and two transparent parts, at two different parts of the screen (-0.02, 0 and 0.24, 0).
The non-converted atlas can be seen here. It's a set of 13x4 playings cards, 4 suits symbol, 4 turned cards and 4 backgrounds.
So, the normalised dimensions of each card part of the texture atlas are :
I really doubt that these dimensions can generate rounding errors.
When executed, the code produces a window with two turned-around cards on the screen. Each one glitching slightly differently.
Here's the result :
So my questions are :
Before anything else, it's worth checking your coordinates match what OpenGL ES expects. GL pixel and texel centers are at half-points, not integer coordinates.E.g.the pixel coordinate of the bottom-left pixel in a 1920x1080 screen is not (0.0, 0.0) it's at (0.5, 0.5).the M'th unfiltered texel center in a texture of side length N is not at coordinate (M / N), it's at ((0.5 + M) / N)If you try to use integer sample locations you'll end up half a pixel/texel off where you originally intended (which may be what is causing your problem, as filtering will pull from the neighboring pixels).
Before anything else, it's worth checking your coordinates match what OpenGL ES expects. GL pixel and texel centers are at half-points, not integer coordinates.
E.g.
If you try to use integer sample locations you'll end up half a pixel/texel off where you originally intended (which may be what is causing your problem, as filtering will pull from the neighboring pixels).
So, if I understand correctly, OpenGL draws the first texture pixel *around* the 0,0 coordinate ? Meaning that 0,0 is the center of the first picture pixel, not the bottom left ?
In that case, it's better to start texturing one half-pixel from the bottom-left and stop at one half-pixel from the upper right of the quad ?
For example, if I take screen coordinates as a base :
Like this ?
On most "line art" renderers and browsers pixel-perfect is achieved by cheating (snapping vertices to an exact pixel center).
Do you know any example or project's name that does this ? Is this cheat expansive ?
Also you mentioned "converted" with respect to the texture itself - what texture format are you using?
I'm using GL_RGBA4444. The conversion was done with my simple Ruby script that converts RGBAxxxx to another RGBAxxxx. In the current case, I converted the texture from ARGB8888 to RGBA4444. I also take care of executing glPixelStorei(GL_UNPACK_ALIGNMENT, 2); since my latest issues.
I guess I'll just put a single transparent pixel border around each element of the texture atlas for now.
Alright, I still wanted to use my atlas 'as-is' and applying the +(1/(2*tex_width)),-(1/(2*tex_height)) offset solved the problem.
The texture looks nice with GL_LINEAR filtering and with GL_NEAREST filtering too, and gets better with GL_LINEAR + SAMPLE_BUFFERS which is good news.
I updated my code in the example Github repository, so that future readers understand the trick.
I glanced over the OpenGL ES 2.x specification and saw this at chapter 3.5.2:
Denote a datum at pa, pb, or pc as fa, fb, or fc, respectively. Then the value f of a datum at a fragment produced by rasterizing a triangle is given byf = (a*fa/wa + b*fb/wb + c*fc/wc) / (a/wa + b/wb + c/wc)(3.5)where wa, wb and wc are the clip w coordinates of pa, pb, and pc, respectively.a, b, and c are the barycentric coordinates of the fragment for which the data areproduced. a, b, and c must correspond precisely to the exact coordinates of thecenter of the fragment. Another way of saying this is that the data associated witha fragment must be sampled at the fragment’s center.
Denote a datum at pa, pb, or pc as fa, fb, or fc, respectively. Then the value f
of a datum at a fragment produced by rasterizing a triangle is given by
f = (a*fa/wa + b*fb/wb + c*fc/wc) / (a/wa + b/wb + c/wc)
(3.5)
where wa, wb and wc are the clip w coordinates of pa, pb, and pc, respectively.
a, b, and c are the barycentric coordinates of the fragment for which the data are
produced. a, b, and c must correspond precisely to the exact coordinates of the
center of the fragment. Another way of saying this is that the data associated with
a fragment must be sampled at the fragment’s center.
Is it what you were referring to ?
Great, glad you got it sorted.
> Is it what you were referring to ?
That's part of it. There are similar comments related to the texture coordinates and filtering too.