Texture sampling NEAREST in P2D render

Hi i work with PShader and i really want to get not smoothed pixels from texture2D. How do i set texture sampling NEAREST in PGraphics in P2D render?

Within glsl, you can use texelFetch() to read non-filtered data from a single pixel. Does that match what you are trying to do?

Or you can ignore the sampling and just always read from exactly the center of the pixels:

PGraphics thing;
PShader shdr;

void setup() {
  size( 800, 800, P2D );
  shdr = new PShader( this, vertSrc, fragSrc );
  thing = createGraphics( 16, 16, P2D );
  thing.noSmooth();

  thing.beginDraw();  // begin/draw pair needed to
  thing.endDraw();    // "init" PGraphics using P2D

  thing.beginDraw();
  thing.background(0);
  thing.loadPixels();
  for( int i=0; i<thing.height; i++ )
    thing.pixels[ i + i*thing.height ] = 0xffffffff;
  thing.updatePixels();
  thing.stroke( 200, 255, 200 );
  thing.line( 0, 5, thing.width, thing.height );
  thing.stroke( 255, 200, 100 );
  thing.noFill();
  thing.circle( 8, 6, 7 );
  thing.stroke( 200, 255, 100 );
  thing.strokeCap( PROJECT );       // needed to make point() visible
  thing.point( 8, 6 );
  thing.endDraw();
}


void draw() {
  if( frameCount % 60 == 0 ) {
    thing.beginDraw();
    thing.stroke( random(100,255), random(100,255), random(100,255) );
    thing.point( random( thing.width ), random( thing.height ) );
    thing.endDraw();
  }

  background( 100 );
  shdr.set( "texture", thing );
  shader(shdr);
  noStroke();
  rect( 60, 80, 16, 16 );
  rect( 60, 180, 64, 64 );
  float s = 384+128*sin(frameCount/30.0);
  rect( 200, 80, s, s );
  resetShader();
}


String[] vertSrc = { """#version 130
uniform mat4 transformMatrix;
uniform mat4 texMatrix;
in vec4 position;
in vec2 texCoord;
varying vec4 vertTexCoord;

void main() {
  gl_Position = transformMatrix * position;
  vertTexCoord = texMatrix * vec4( texCoord, 1.0, 1.0 );
  vertTexCoord.y = 1.0 - vertTexCoord.y;
}
""" };

String[] fragSrc = { """#version 130
uniform sampler2D texture;
in vec4 vertTexCoord;

void main() {
  vec2 texSize = textureSize( texture, 0 );
  vec2 uv = (floor(vertTexCoord.st*texSize) + 0.5) / texSize;
  gl_FragColor = texture2D( texture, clamp(uv, 0., 1.) );
}
""" };

Thanks for your reply! When I try to use textlFetch, I get lots of errors.

#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D texture;

void main() {
  gl_FragColor = texelFetch(texture, ivec2(gl_FragCoord.xy), 0);
}

To be honest, I’m not sure exactly what I’m doing wrong. I’d be very grateful if you could show me the simplest example of how to use it correctly.

Oh, and if this matter, I use APDE not processing for desktop.

Yes, that’s exactly what I tried to do first. But I noticed that the colour of the pixels at the colour boundaries still becomes slightly blurred. For example, it used to be (255,255,255,255) and became (254,254,254,254). After a series of tests, I concluded that this was specifically due to bilinear smooth. Perhaps the float type introduces a small margin of error, and something similar happens when retrieving a pixel from the texture. Yes, I also tried using high-precision floats, but that didn’t solve the problem.

I realise the error is very small, but for my project it’s a problem.

Add

#version 130 core

as the first line of your shader.

PGraphics thing;
PShader shdr;

void setup() {
  size( 800, 800, P2D );
  shdr = new PShader( this, vertSrc, fragSrc );
  thing = createGraphics( 16, 16, P2D );
  thing.noSmooth();

  thing.beginDraw();  // begin/draw pair needed to
  thing.endDraw();    // "init" PGraphics using P2D

  thing.beginDraw();
  thing.background(0);
  thing.loadPixels();
  for( int i=0; i<thing.height; i++ )
    thing.pixels[ i + i*thing.height ] = 0xffffffff;
  thing.updatePixels();
  thing.stroke( 200, 255, 200 );
  thing.line( 0, 5, thing.width, thing.height );
  thing.stroke( 255, 200, 100 );
  thing.noFill();
  thing.circle( 8, 6, 7 );
  thing.stroke( 200, 255, 100 );
  thing.strokeCap( PROJECT );       // needed to make point() visible
  thing.point( 8, 6 );
  thing.endDraw();
}


void draw() {
  if( frameCount % 60 == 0 ) {
    thing.beginDraw();
    thing.stroke( random(100,255), random(100,255), random(100,255) );
    thing.point( random( thing.width ), random( thing.height ) );
    thing.endDraw();
  }

  background( 100 );
  shdr.set( "texture", thing );
  shader(shdr);
  noStroke();
  rect( 60, 80, 16, 16 );
  rect( 60, 180, 64, 64 );
  float s = 384+128*sin(frameCount/30.0);
  rect( 200, 80, s, s );
  resetShader();
}


String[] vertSrc = { """#version 130
uniform mat4 transformMatrix;
uniform mat4 texMatrix;
in vec4 position;
in vec2 texCoord;
out vec4 vertTexCoord;

void main() {
  gl_Position = transformMatrix * position;
  vertTexCoord = texMatrix * vec4( texCoord, 1.0, 1.0 );
  vertTexCoord.y = 1.0 - vertTexCoord.y;
}
""" };

String[] fragSrc = { """#version 130
uniform sampler2D texture;
in vec4 vertTexCoord;
out vec4 fragColor;

void main() {
  vec2 texSize = textureSize( texture, 0 );
//  vec2 uv = (floor(vertTexCoord.st*texSize) + 0.5) / texSize;
//  gl_FragColor = texture2D( texture, clamp(uv, 0., 1.) );
  fragColor = texelFetch( texture, ivec2( vertTexCoord.st * texSize ), 0 );
}
""" };

Ah, sorry, I just saw the APDE part. That means you’re using OpenGL ES. Put

#version 300 es

at the start of your shaders.

You might like to read through https://registry.khronos.org/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf to learn the particulars.

In later versions of glsl, they replaced gl_FragColor with an output vec4 of your own creation, so that’s why my code has my own out vec4 fragColor; line.

Here’s the same code I’ve been posting, but changed to version 300 es:

PGraphics thing;
PShader shdr;

void setup() {
  size( 800, 800, P2D );
  shdr = new PShader( this, vertSrc, fragSrc );
  thing = createGraphics( 16, 16, P2D );
  thing.noSmooth();

  thing.beginDraw();  // begin/draw pair needed to
  thing.endDraw();    // "init" PGraphics using P2D

  thing.beginDraw();
  thing.background(0);
  thing.loadPixels();
  for( int i=0; i<thing.height; i++ )
    thing.pixels[ i + i*thing.height ] = 0xffffffff;
  thing.updatePixels();
  thing.stroke( 200, 255, 200 );
  thing.line( 0, 5, thing.width, thing.height );
  thing.stroke( 255, 200, 100 );
  thing.noFill();
  thing.circle( 8, 6, 7 );
  thing.stroke( 200, 255, 100 );
  thing.strokeCap( PROJECT );       // needed to make point() visible
  thing.point( 8, 6 );
  thing.endDraw();
}


void draw() {
  if( frameCount % 60 == 0 ) {
    thing.beginDraw();
    thing.stroke( random(100,255), random(100,255), random(100,255) );
    thing.point( random( thing.width ), random( thing.height ) );
    thing.endDraw();
  }

  background( 100 );
  shdr.set( "texture", thing );
  shader(shdr);
  noStroke();
  rect( 60, 80, 16, 16 );
  rect( 60, 180, 64, 64 );
  float s = 384+128*sin(frameCount/30.0);
  rect( 200, 80, s, s );
  resetShader();
}


String[] vertSrc = { """#version 300 es
precision highp float;
uniform mat4 transformMatrix;
uniform mat4 texMatrix;
in vec4 position;
in vec2 texCoord;
out vec4 vertTexCoord;

void main() {
  gl_Position = transformMatrix * position;
  vertTexCoord = texMatrix * vec4( texCoord, 1.0, 1.0 );
  vertTexCoord.y = 1.0 - vertTexCoord.y;
}
""" };

String[] fragSrc = { """#version 300 es
precision highp float;
uniform sampler2D texture;
in vec4 vertTexCoord;
out vec4 fragColor;

void main() {
  vec2 texSize = vec2( textureSize( texture, 0 ) );
  fragColor = texelFetch( texture, ivec2( vertTexCoord.st * texSize ), 0 );
}
""" };

Errors:

java.lang.RuntimeException: Cannot compile fragment shader:
0:3: S0022: Symbol ‘texture’ redeclared
0:8: L0001: Symbol ‘texture’ can’t be referenced as a variable
0:9: L0001: Symbol ‘texture’ can’t be referenced as a variable
at processing.core.PGraphics.showException(PGraphics.java:5701)
at processing.opengl.PShader.compileFragmentShader

It seems that Processing creates its own texture variable, so that needs to be removed. As for the other error, I’m not sure what to do about it.

The solution is very simple – we just need to rename the variable, for example to mytexture.

And it seems to work perfectly. The image doesn’t become blurry when zoomed in. Anyway, thanks for your help! I’m really grateful to you!!! @scudly