Stik
Stik

Reputation: 607

Using integers in GLSL fragment shaders - experiencing unexplained artifacts

I have a hexagonal grid that I want to texture. I want to use a single texture with 16 distinct subtextures arranged in a 4x4 grid. Each "node" in the grid has an image type, and I want to smoothly blend between them. My approach for implementing this is to render triangles in pairs, and encode the 4 image types on all vertices in the two faces, as well as a set of 4 weighting factors (which are the barycentric coordinates for the two tris). I can then use those two things to blend smoothly between any combination of image types.

4x4 Texture

Here is the fragment shader I'm using. The problems are arising from the use of int types, but I don't understand why. If i only use the first four sub-textures then i can change idx to be float and hardcode the Y-coord to be 0, and then it then works as i expect.

vec2 offset(int idx) {
    vec2 v = vec2(idx % 4, idx / 4);
    return v / 4.0;
}

void main(void) {   
    //
    // divide the incoming UVs into one of 16 regions.  The 
    // offset() function should take an integer from 0..15 
    // and return the offset to that region in the 4x4 map
    //
    vec2 uv = v_uv / 4.0;

    //
    // The four texture regions involved at 
    // this vertex are encoded in vec4 t_txt.  The same
    // values are stored at all vertices, so this doesn't 
    // vary across the triangle
    //
    int ia = int(v_txt.x);
    int ib = int(v_txt.y);
    int ic = int(v_txt.z);
    int id = int(v_txt.w);
    
    //
    // Use those indices in the offset function to get the
    // texture sample at that point
    //      
    vec4 ca = texture2D(txt, uv + offset(ia)); 
    vec4 cb = texture2D(txt, uv + offset(ib)); 
    vec4 cc = texture2D(txt, uv + offset(ic)); 
    vec4 cd = texture2D(txt, uv + offset(id)); 
    
    //
    // Merge them with the four factors stored in vec4 v_tfact.
    // These vary for each vertex
    //
    fragcolour = ca * v_tfact.x 
               + cb * v_tfact.y 
               + cc * v_tfact.z 
               + cd * v_tfact.w;
    
}

Here is what's happening:

artifacts

(My "pair of triangles" are actually about 20 and you can see their structure in the artifacts, but the effect is the same)

This artifacting behaves a bit like z-fighting: moving the scene around makes it all shimmer and shift wildly.

Why doesn't this work as I expect?

One solution I can fall back on is to simply use a 1-dimensional texture map, with all 16 sub-images in a horizontal line, then i can switch everything to floating point since I won't need the modulo/integer-divide process to map idx->x,y, but this feels clumsy and I'd at least like to understand what's going on here.

Here is what it should look like, albeit with only 4 of the sub-images in use:

expected

Upvotes: 1

Views: 1930

Answers (1)

Rabbid76
Rabbid76

Reputation: 210878

See OpenGL Shading Language 4.60 Specification - 5.4.1. Conversion and Scalar Constructors

When constructors are used to convert a floating-point type to an integer type, the fractional part of the floating-point value is dropped.

Hence int(v_txt.x) does not round v_txt.x, it truncates v_txt.x

You have to round the values to the nearest integer before constructing an integral value:

int ia = int(round(v_txt.x));
int ib = int(round(v_txt.y));
int ic = int(round(v_txt.z));
int id = int(round(v_txt.w));

Alternatively add 0.5 before constructing the integral value:

int ia = int(v_txt.x + 0.5);
int ib = int(v_txt.y + 0.5);
int ic = int(v_txt.z + 0.5);
int id = int(v_txt.w + 0.5);

Upvotes: 3

Related Questions