Optimizing the use of a custom Geometry

Agreed, that’s all you need, but how does one get that? The coordinate system of uv passed in is in the range of 0…1. But as you note, one needs +x,-x, +y,-y (or -z,+z, depending on how you term the coordinates). From the example I referenced:

uniform sampler2D unit_wave
noperspective in vec2 tex_coord;
const vec2 size = vec2(2.0,0.0);
const ivec3 off = ivec3(-1,0,1);

vec4 wave = texture(unit_wave, tex_coord);
float s11 = wave.x;
float s01 = textureOffset(unit_wave, tex_coord, off.xy).x;
float s21 = textureOffset(unit_wave, tex_coord, off.zy).x;
float s10 = textureOffset(unit_wave, tex_coord, off.yx).x;
float s12 = textureOffset(unit_wave, tex_coord, off.yz).x;
vec3 va = normalize(vec3(size.xy,s21-s01));
vec3 vb = normalize(vec3(size.yx,s12-s10));
vec4 bump = vec4( cross(va,vb), s11 );

So textureOffset returns the samples from the heightmap by passing unit coords to texturOffset. Perhaps there is some other way of getting the adjacent samples in the heightmap given uv coordinates. Perhaps computing them in the CPU and passing them in as a uniform?