Skip to content

Instantly share code, notes, and snippets.

@BeRo1985
Created December 3, 2023 12:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BeRo1985/01580ec71c093d503217bf6aea221cc3 to your computer and use it in GitHub Desktop.
Save BeRo1985/01580ec71c093d503217bf6aea221cc3 to your computer and use it in GitHub Desktop.
GLSL Octahedral Texture Mapping with Edge Mirroring and Catmull-Rom Interpolation
// GLSL Octahedral Texture Mapping with Edge Mirroring and Catmull-Rom Interpolation (by Benjamin 'BeRo' Rosseaux)
ivec2 wrapOctahedralTexelCoordinates(const in ivec2 texel, const in ivec2 texSize) {
ivec2 wrapped = ((texel % texSize) + texSize) % texSize;
return ((((abs(texel.x / texSize.x) + int(texel.x < 0)) ^ (abs(texel.y / texSize.y) + int(texel.y < 0))) & 1) != 0) ? (texSize - (wrapped + ivec2(1))) : wrapped;
}
vec4 textureCatmullRomCoefficents(const in float v){
float t = v, tt = t * t, ttt = tt * t;
return vec4((tt - (ttt * 0.5)) - (0.5 * t), ((ttt * 1.5) - (tt * 2.5)) + 1.0, ((tt * 2.0) - (ttt * 1.5)) + (t * 0.5), (ttt * 0.5) - (tt * 0.5));
}
// Catmull-Rom in 9 samples - I've also tried just-four-taps variants, but they din't matched to my 16-tap reference implementation exactly.
vec4 textureCatmullRom(const in sampler2D tex, const in vec2 uv, const in int lod){
vec2 texSize = textureSize(tex, lod);
vec2 samplePos = uv * texSize;
vec2 p11 = floor(samplePos - vec2(0.5)) + vec2(0.5);
vec2 t = samplePos - p11, tt = t * t, ttt = tt * t;
vec2 w0 = (tt - (ttt * 0.5)) - (0.5 * t);
vec2 w1 = ((ttt * 1.5) - (tt * 2.5)) + vec2(1.0);
vec2 w2 = ((tt * 2.0) - (ttt * 1.5)) + (t * 0.5);
vec2 w3 = (ttt * 0.5) - (tt * 0.5);
vec2 w4 = w1 + w2;
vec2 p00 = (p11 - vec2(1.0)) / texSize;
vec2 p33 = (p11 + vec2(2.0)) / texSize;
vec2 p12 = (p11 + (w2 / w4)) / texSize;
return (((textureLod(tex, vec2(p00.x, p00.y), float(lod)) * w0.x) +
(textureLod(tex, vec2(p12.x, p00.y), float(lod)) * w4.x) +
(textureLod(tex, vec2(p33.x, p00.y), float(lod)) * w3.x)) * w0.y) +
(((textureLod(tex, vec2(p00.x, p12.y), float(lod)) * w0.x) +
(textureLod(tex, vec2(p12.x, p12.y), float(lod)) * w4.x) +
(textureLod(tex, vec2(p33.x, p12.y), float(lod)) * w3.x)) * w4.y) +
(((textureLod(tex, vec2(p00.x, p33.y), float(lod)) * w0.x) +
(textureLod(tex, vec2(p12.x, p33.y), float(lod)) * w4.x) +
(textureLod(tex, vec2(p33.x, p33.y), float(lod)) * w3.x)) * w3.y);
}
vec4 textureCatmullRomOctahedralMap(const in sampler2D tex, vec3 direction) {
direction = normalize(direction); // just for to make sure that it is normalized
vec2 uv = direction.xy / (abs(direction.x) + abs(direction.y) + abs(direction.z));
uv = fma((direction.z < 0.0) ? ((1.0 - abs(uv.yx)) * vec2((uv.x >= 0.0) ? 1.0 : -1.0, (uv.y >= 0.0) ? 1.0 : -1.0)) : uv, vec2(0.5), vec2(0.5));
ivec2 texSize = textureSize(tex, 0).xy;
vec2 invTexSize = vec2(1.0) / vec2(texSize);
if(any(lessThanEqual(uv, invTexSize * 2.0)) || any(greaterThanEqual(uv, vec2(1.0) - (invTexSize * 2.0)))){
// Handle edges with manual catmull rom interpolation using texelFetch for correct octahedral texel edge mirroring
uv = fma(uv, texSize, vec2(-0.5));
ivec2 baseCoord = ivec2(floor(uv));
vec2 fractionalPart = uv - vec2(baseCoord);
vec4 xCoefficients = textureCatmullRomCoefficents(fractionalPart.x);
vec4 yCoefficients = textureCatmullRomCoefficents(fractionalPart.y);
return (((texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2(-1, -1), texSize), 0) * xCoefficients.x) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 0, -1), texSize), 0) * xCoefficients.y) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 1, -1), texSize), 0) * xCoefficients.z) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 2, -1), texSize), 0) * xCoefficients.w)) * yCoefficients.x) +
(((texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2(-1, 0), texSize), 0) * xCoefficients.x) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 0, 0), texSize), 0) * xCoefficients.y) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 1, 0), texSize), 0) * xCoefficients.z) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 2, 0), texSize), 0) * xCoefficients.w)) * yCoefficients.y) +
(((texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2(-1, 1), texSize), 0) * xCoefficients.x) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 0, 1), texSize), 0) * xCoefficients.y) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 1, 1), texSize), 0) * xCoefficients.z) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 2, 1), texSize), 0) * xCoefficients.w)) * yCoefficients.z) +
(((texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2(-1, 2), texSize), 0) * xCoefficients.x) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 0, 2), texSize), 0) * xCoefficients.y) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 1, 2), texSize), 0) * xCoefficients.z) +
(texelFetch(tex, wrapOctahedralTexelCoordinates(baseCoord + ivec2( 2, 2), texSize), 0) * xCoefficients.w)) * yCoefficients.w);
}else{
// Non-edge texels can be sampled directly with an optimized catmull rom interpolation using just nine bilinear textureLod calls
return textureCatmullRom(tex, uv, 0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment