Search code examples
openglglsl

Is it possible to fix rounding issues when pixelizing UV coordinates?


I'm working on a blocky voxel game. I implemented smooth shading from the 0fps article, and it worked great.

enter image description here

Today I decided I wanted to make it less-smooth by pixelizing it. Each of the vertices that make up the triangle strip (the quad) have a vec4 shadows. And I just bilinearly interpolate between those 4 shadow points using the UV coordinates that would otherwise be for a texture..

float pixelize(float x) { return floor(x*BLOCK_DIMENSION) / BLOCK_DIMENSION; }
float linear(float u, float a, float b) { return a + u*(b - a); }
float bilinear()
{
    float v0 = linear(pixelize(texUV.x), shadows.x, shadows.y);
    float v1 = linear(pixelize(texUV.x), shadows.z, shadows.w);
    return linear(pixelize(texUV.y), v0, v1);
}

But no matter how much I try to fudge it (adding 0.125f or 0.5f or whatever), I can't get it perfect. The pixelization is shifted by 1 (notice there's 4 shades on the left side, and 3 shades on the right side); the shading isn't symmetric. enter image description here

Is it possible to fix this?


Solution

  • You need to add 0.5 to the result of floor, like so:

    float pixelize(float x) { return (floor(x*BLOCK_DIMENSION) + 0.5) / BLOCK_DIMENSION; }
    

    I bet you tried to add it somewhere else, which is not the same.

    The motivation for this can be understood using the following diagram:

    enter image description here

    The sampling of the second row is asymmetrical within the interval because floor shifts everything down. Adding 0.5 restores that symmetry.