[Solved] Edge artifacts when random-access sampling a texture in a shader on Linux


#1

Edit: This texture sampling bug only seems to occur on Linux. On both Windows 10 and Android, I can set point sampling with ((PGraphicsOpenGL)g).textureSampling(2); and get clean images, but on Linux, that line has no effect. Is this a bug in JOGL?

Edit 2: Apparently this is a known bug from two years ago. We need to get textureSampling added to the Processing API so it can be officially supported (and fixed).

Edit 3: I hackily solved this problem by using NVIDIA’s X Server Settings application to “Override Application Setting” for “Anisotropic Filtering” under the “Antialiasing Settings”. Along with textureSampling(2), my mis-colored tile edges go away and the images now look the same on Linux as they do on Windows and Android.

Original post:

I want to be able to pass an array of data to a shader and sample it random-access to render tiles. Ultimately, the program will run on Android, so I’m stuck using OpenGL ES shader version 1 and can only pass the data as texture images. Passing and reading the data seems to work fine except along the edges of the tiles where I’m getting weird sampling artifacts that I can’t understand.

The code below draws a tiled surface in which each tile’s color is sampled pseudo-randomly from a 1-D n x 1 texture image. Sampling the texture data array looks okay in the interiors, but gives artifacts along the edges of the tiles as shown in the top half of this picture.

On the left half of the picture, tile colors are sampled from the “data” texture discontiguously both horizontally and vertically. If I sample the data texture more contiguously, as is done on the right half of the image, the artifacts between the now horizontally adjacent texels go away (or at least aren’t noticeable) except where the last (brown) and first (black) tiles abut. On the bottom half of the picture, the tiles are colored using computed values from within the shader and so show no artifacts at all, discounting the jaggies that I’ll deal with separately.

Where are these artifacts coming from? I’ve tried adding hints to disable mipmapping and to set the textureSampling to point sampling, but neither one made any difference. On the other hand, I do NOT get these artifacts on Android where, for now, I’m guessing textures are always point-sampled. How can I ensure my data texture is always point-sampled on desktop Processing? Is that even what is causing the artifacts?

Here’s the code. Mouse drag on the left edge to zoom or elsewhere to pan the image.

enum UIState { NEUTRAL, PANNING, SCALING; }
UIState uiState;
float viewX=0.0, viewY=0.0, viewScale;

void mousePressed() {
  if( mouseX < 0.1 * width ) uiState = UIState.SCALING;
  else uiState = UIState.PANNING;
}

void mouseDragged() {
  if( uiState == UIState.PANNING ) {
    viewX += (pmouseX - mouseX) / viewScale;
    viewY += (pmouseY - mouseY) / viewScale;
  } else if( uiState == UIState.SCALING )
    viewScale *= 1.0 + float(pmouseY - mouseY) / height * 2.0;
}

void mouseReleased() { uiState = UIState.NEUTRAL; }


PShader fragshader;

void setup() {
  //fullScreen(P2D);
  size(640, 480, P2D);
  noSmooth();
  hint(DISABLE_DEPTH_MASK);
  hint(DISABLE_TEXTURE_MIPMAPS);
  ((PGraphicsOpenGL)g).textureSampling(2);

  fragshader = loadShader( "texture.frag" );
  fragshader.set( "screenRes", (float)width, (float)height );
  int nCells = 16;
  fragshader.set( "nCells", (float)nCells );
  
  PImage data = createImage( nCells, 1, ARGB );
  data.loadPixels();
  data.pixels[0] = color( 0 );
  for( int ic=1; ic<nCells; ic++ ) {
    data.pixels[ic] = color( random(256), random(256), random(256) );
  }
  data.pixels[6] = color( 255 );
  data.pixels[9] = color( 255 );
  data.updatePixels();
  fragshader.set( "data", data );

  viewScale = min( width, height ) / 8.0;
}

void draw() {
  background( 0 );
  noStroke();
  fragshader.set( "viewPos", viewX, viewY );
  fragshader.set( "viewScale", viewScale );
  filter( fragshader );
//  shader( fragshader );
//  rectMode( CORNER );
//  rect( 0, 0, width, height );
}

and the shader (save as “texture.frag” in the data/ directory)

#ifdef GL_ES
precision highp float;
precision mediump int;
#endif

uniform vec2 screenRes;
uniform float nCells;
uniform sampler2D data;
uniform vec2 viewPos;
uniform float viewScale;

vec4 localColor( float idx ) {
   return vec4( fract( idx*0.618 ), fract( idx*0.781 ), fract( idx* 0.933 ), 1. ) ;
}

void main() {
   vec2 p = gl_FragCoord.xy;
   p.y = screenRes.y - p.y - 1.;
   p -= screenRes/2.;
   vec2 q = p;

   /**/
   p /= screenRes.y;
   float r = length(p);
   float a = atan( p.y, p.x );
   r =  tan(r);
   p = vec2( cos(a), sin(a) ) * r;
   p *= screenRes.y;
   /**/
   p /= viewScale;
   p += viewPos+0.5;

   vec2 ij = floor( p );

   float idx = mod( mod( 5.*ij.y+ij.x, nCells )*7., nCells);
   if( q.x > 0. ) idx = mod( 5.*ij.y+ij.x, nCells );

   vec4 tileColor = texture2D( data, vec2( (idx+0.5)/nCells, 0.5 ) );
   if( q.y > 0 ) tileColor = localColor( idx );

   gl_FragColor = vec4( tileColor );
}