Textures & Samplers

Texture coordinate convention: OpenGL's (0,0) is the bottom-left of the image; most image files start at the top-left — flip on load (stbi_set_flip_vertically_on_load(true)) or flip uv.y.

16.1 Sampling a 2-D texture

uniform sampler2D uAlbedo;
in vec2 vUV;
out vec4 outColor;

void main() {
    vec4 c = texture(uAlbedo, vUV);                 // filtered sample, mip level chosen automatically
    vec4 l = textureLod(uAlbedo, vUV, 2.0);         // explicit mip level
    vec4 o = textureOffset(uAlbedo, vUV, ivec2(1, 0));   // neighbor texel (constant offset)
    vec4 t = texelFetch(uAlbedo, ivec2(gl_FragCoord.xy), 0);  // exact texel, no filtering, integer coords
    ivec2 size = textureSize(uAlbedo, 0);           // dimensions of mip level 0
    vec4 g = textureGrad(uAlbedo, vUV, dFdx(vUV), dFdy(vUV));  // explicit derivatives
    vec4 four = textureGather(uAlbedo, vUV, 0);     // red channel of the 4 texels used by bilinear filtering
    outColor = c;
}

16.2 Texture setup on the host

Texture coordinate convention: OpenGL's (0,0) is the bottom-left of the image; most image files start at the top-left — flip on load (stbi_set_flip_vertically_on_load(true)) or flip uv.y.

GLuint tex; glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);   // trilinear
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);       // REPEAT, CLAMP_TO_EDGE, MIRRORED_REPEAT, CLAMP_TO_BORDER
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 16.0f);  // anisotropic filtering (4.6 / extension)
// Separate sampler objects: glGenSamplers / glSamplerParameteri / glBindSampler(unit, sampler)

16.3 Filtering & mipmapping concepts

Nearest (pixelated) vs linear (bilinear) filtering.

Mipmaps: prefiltered smaller versions; avoid aliasing/shimmering for distant/minified textures; selected via screen-space derivatives.

Trilinear (blend between mips) and anisotropic filtering (oblique surfaces).

Derivatives are undefined in non-uniform control flow → sample before branching, or use textureLod/textureGrad.

16.4 Texture types

uniform samplerCube uSkybox;          vec4 sky = texture(uSkybox, direction);          // cube maps
uniform sampler3D uVolume;            vec4 v = texture(uVolume, vec3(uv, slice));       // 3-D textures (LUTs, volumes)
uniform sampler2DArray uLayers;       vec4 a = texture(uLayers, vec3(uv, layerIndex)); // texture arrays
uniform sampler2DShadow uShadowMap;   float lit = texture(uShadowMap, vec3(uv, depthRef));   // hardware depth compare
uniform sampler2DMS uMSAA;            vec4 s = texelFetch(uMSAA, ivec2(gl_FragCoord.xy), sampleIndex);
uniform usampler2D uIDs;              uint id = texelFetch(uIDs, px, 0).r;              // integer textures
uniform samplerBuffer uData;          vec4 d = texelFetch(uData, index);                // buffer textures

16.5 Texture tricks

// Tiling & scrolling
vec2 uv = vUV * 4.0 + vec2(uTime * 0.1, 0.0);
// Texture atlas: select a sub-rectangle
vec2 atlasUV = tileOffset + fract(uv) * tileSize;
// Triplanar mapping (no UVs needed)
vec3 w = abs(normalize(vNormal)); w /= (w.x + w.y + w.z);
vec4 tri = texture(uTex, vWorldPos.yz) * w.x + texture(uTex, vWorldPos.xz) * w.y + texture(uTex, vWorldPos.xy) * w.z;
// Distortion / heat haze
vec2 offset = (texture(uNoise, vUV + uTime * 0.05).rg - 0.5) * 0.02;
vec4 scene = texture(uScene, vUV + offset);
// Color grading with a 3-D LUT
vec3 graded = texture(uLUT, clamp(color, 0.0, 1.0)).rgb;