1
votes

I see an issue where I can correctly draw a textured polygon if all vertices remain onscreen within a perspective projection, BUT if I scale the quad large enough such that one more more vertices fall 'too far behind' the viewing volume, then the resulting OpenGL drawing is incorrect (see screenshots). The texture mapping becomes skewed, and it appears as though the offscreen vertex 'moved' and becomes distorted. I am using GLES 2.0 code on a GLES 3.0 compliant driver (details at bottom). I will now explain my test in more detail --

I am drawing a GL_TRIANGLE_STRIP 'square' made of two simple polygons, and applying simplistic texture map on the quad (the texture maps perfectly onto the quad once only, with no repeats. UV coordinates 0->1 mapped as you would expect.

Seperately, I am using GLM functions to give me helpers for matrix transformations, and subsequently using glm::PerspectiveFov() to set up the viewing volume (frustum). The result is a camera looking slightly downward onto the quad, creating a 'ground' like surface with a checker patterned texture on it.

correctly drawn view of quad screenshot

From here, if I increase the scale transformation factor on the quad any further, or rotate the camera such that the offscreen corner vertices lie 'further back' from the viewing area, I see suddenly extreme weird behavior, as though the texture mapping changes, or if one vertex of the polygon is shifting incorrectly. See here:

polygon texture mapping becomes wrong

For the camera rotation I am using glm::lookAt() -- effectively I move the 'eye' position while keeping the target at 0,0 in the scene (center of the polygon).

Also note that when I cross this threshold, I see the red debug line I draw connecting all the vertices suddenly shift its orientation from where it should be.

Does anyone know how this problem originates? Is there a way to solve it so I can draw a big huge quad and have vertices offscreen without these problems/artifacts? Thanks!!!

GPU Info:

GL Vendor: Vivante Corporation

GL Renderer: Vivante GC2000

GL Version: OpenGL ES 3.0 V5.0.11.p8.41671

GLSL Version: OpenGL ES GLSL ES 3.00

1
At the risk of sounding unhelpful: don't draw a big huge quad. Break it up.Rook
sounds like a driver bug to me. Especially since you're dealing with a mobile GPU which are not known to have the highest quality OpenGL implementations. Have you tried running your code on the GPU of another vendor?PeterT
I looks like the Vivante part is failing to correct the vertex interpolation for the depth projection of the triangle, so you're getting a flat interpolation rather than a perspective corrected one. Unlikely there is much you are doing wrong here ...solidpixel
Hey guys I think you're right about there being a driver bug. Other GPUs don't reproduce this exact effect, but I now that I have reviewed, I believe I have seen a similar thing on the Atom Z530 bundled with a PVR SGX and an ancient driver implementation, but that was with lines drawing incorrectly when verts were outside viewing volume. I think I'll try decimating the quad into a grid, as Rook pointed out (I was thinking that already, but I wanted to borrow some eyes/brains first). :)adowdy

1 Answers

0
votes

Some research and comments on my original question have led me to believe that the observed effect is the result of a bug in the OpenGL driver implementation on the Vivante GPU GC2000. Apparently such bugs are common on embedded GPU hardware drivers - a problem exacerbated by the fact that the source code for such ES implementations is never available.

To solve this one via a workaround, I was able to take the dimensions of my original square and instead create a grid array of textured squares so that all polygons end up being small enough to 'fit' close enough to the viewing area (or alternatively be completely clipped, avoiding the bug behavior). Code in C++:

// measurements we will add as we move along the grid
float tile_size = 1.0 / num_grid_subdivisions; // square width 1
float tile_uv_dist = 1.0 / num_grid_subdivisions; // assume 0->1 texture mapping
XY curr_bl = XY(-0.5, -0.5); // quad from -0.5 to 0.5 in x and y (1x1)
float cu = 0; //current texture coordinates in x dimension
float cv = 0; //current texture coordinates in y dimension
for (int row = 0; row < num_grid_subdivisions; ++row)
{
        for (int row_item = 0; row_item < num_grid_subdivisions; ++row_item)
        {
            // GL_TRIANGLES to keep simple, but could use STRIP later
            VertXYUV bl(curr_bl, cu, cv); // bottomleft
            // if we know bottomleft, we know the rest of the points of the square
            VertXYUV tl(XY(curr_bl.x, curr_bl.y + tile_size), cu, cv + tile_uv_dist);
            VertXYUV br(XY(curr_bl.x + tile_size, curr_bl.y), cu+ tile_uv_dist, cv );
            VertXYUV tr(XY(curr_bl.x + tile_size, curr_bl.y + tile_size),
            cu + tile_uv_dist, cv + tile_uv_dist);
            // our square tile is two triangle polygons
            AddVert(bl); AddVert(tl); AddVert(br); // triangle 1
            AddVert(br); AddVert(tl); AddVert(tr); // triangle 2

            // current info should always be tracking 'bl' of current tile
            // increment row item, moving across to the right (+x)
            cu += tile_uv_dist;
            curr_bl.x += tile_size;
        }

        // current info should always be tracking 'bl' of current tile
        // incrementing row, moving up (+y)
        cv += tile_uv_dist;
        cu = 0; // reset x space texture coordinate back to left side (0)
        curr_bl.y += tile_size;
        curr_bl.x = grid_bl.x;
}