My curiosity got the better of me and I did a quick study. On an m1 mac at least, sampling with texture() without a slight offset does give a blended value that can vary depending on the value. But the blending and varying stops with a slight offset, so texture() should be a reliable way to read a value with a small offset.
Different platforms might yield different results. I remember a forum thread from a while back where the shader was having a floating point issue on linux that dimitre could not reproduce on a Mac.
This all seems like an artifact of floating point values. Here is the code for the study:
common.frag:
#version 330
uniform sampler2D tex0;
uniform float posY;
uniform float factor;
in vec2 vTexcoord;
out vec4 fragColor;
void main(){
vec2 tc = vTexcoord;
// make 3 regions along y axis
float floorY = floor(tc.y * 3.0);
// a texel is the (width, height) of 1 pixel
vec2 texel = vec2(1.0) / vec2(1920.0 / 1080.0);
vec3 color = vec3(0.0);
if(floorY == 0.0) {
color = texture(tex0, vec2(tc.x, posY - (texel.y * factor))).rgb;
} else if(floorY == 1.0) {
color = texture(tex0, vec2(tc.x, posY)).rgb; // no offset here
} else {
color = texture(tex0, vec2(tc.x, posY + (texel.y * factor))).rgb;
}
fragColor = vec4(color, 1.0);
}
common.vert:
#version 330
uniform mat4 modelViewProjectionMatrix;
in vec4 position;
in vec2 texcoord;
out vec2 vTexcoord;
void main(){
gl_Position = modelViewProjectionMatrix * position;
vTexcoord = texcoord;
}
ofApp.h:
#pragma once
#include "ofMain.h"
#include "ofxGui.h"
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void setImageColors();
ofShader shader;
ofFloatImage image;
ofFbo fbo;
ofParameter<float> posY;
ofParameter<float> factor;
float width;
float height;
ofxPanel panel;
ofEventListener listener;
};
ofApp.cpp:
#include "ofApp.h"
void ofApp::setup(){
ofSetFrameRate(60);
ofToggleFullscreen();
ofDisableArbTex();
width = 1920;
height = 1080;
image.allocate(width, height, OF_IMAGE_COLOR);
fbo.allocate(width, height, GL_RGBA);
shader.load("common.vert", "common.frag");
posY.set("posY", 0.5f, 0.f, 1.f);
factor.set("factor", 0.01f, 0.f, 0.01f);
setImageColors();
panel.setup();
panel.add(posY);
panel.add(factor);
listener = posY.newListener([this](const ofParameter<float>&){
setImageColors();
});
}
//--------------------------------------------------------------
void ofApp::update(){
fbo.begin();
shader.begin();
shader.setUniform1f(posY.getName(), posY.get());
shader.setUniform1f(factor.getName(), factor.get());
image.draw(0.f, 0.f);// tex0 and normalized texcoord
shader.end();
fbo.end();
}
//--------------------------------------------------------------
void ofApp::draw(){
fbo.draw(0.f, 0.f);
// image.draw(0.f, 0.f);
panel.draw();
}
//--------------------------------------------------------------
void ofApp::setImageColors(){
for(size_t j{0}; j < image.getHeight(); ++j){
for(size_t i{0}; i < image.getWidth(); ++i){
if(j < posY.get() * image.getHeight()) {
image.setColor(i, j, ofFloatColor(i / image.getWidth()));
} else {
image.setColor(i, j, ofFloatColor(0.f));
}
}
}
image.update();
}