Texture Lookup Functions

A sampler2D uniform names a texture unit, and a lookup function reads a filtered color from it at coordinates between 0 and 1 (Textures loads the images). GLSL ES 1.00 gave each sampler type its own function names; GLSL ES 3.00 overloads one family on the sampler type:

Texture lookup functions in the two GLSL ES versions
GLSL ES 1.00 (WebGL 1) GLSL ES 3.00 (WebGL 2) Purpose
texture2D(s, uv), textureCube(s, dir) texture(s, uv) filtered sample, automatic mipmap level
texture2DLod(s, uv, lod) (vertex only) textureLod(s, uv, lod) explicit mipmap level, any stage
texture2DProj(s, uvq) textureProj(s, uvq) divide by the last component first
(none) textureGrad, textureOffset, texelFetch, textureSize derivatives, offsets, integer access

Porting is mechanical: gl_FragColor = texture2D(uCover, vUV) * uTint; becomes outColor = texture(uCover, vUV) * uTint;. In a fragment shader the GPU picks the mipmap level from the screen-space derivatives of the coordinates, and an optional third argument biases it. A vertex shader has no derivatives, so there texture() reads the base level. Derivatives are undefined inside divergent branches, so sample before an if, not inside it.

One mipmapped cover read with texture(), a biased texture(), textureLod() and textureProj()HTMLLive
<!doctype html>
<style>
  body { margin: 0; font: 12px system-ui, sans-serif; background: #f7f4ee; color: #333; }
  canvas { display: block; width: 100%; max-width: 600px; }
  .names { display: flex; max-width: 600px; text-align: center; font-family: monospace; font-size: 11px; }
  .names div { flex: 1; padding: 4px 2px; }
</style>
<canvas id="view" width="600" height="220"></canvas>
<div class="names">
  <div>texture(s, uv)</div><div>texture(s, uv, 2.5)</div><div>textureLod(s, uv, 4.0)</div><div>textureProj(s, uvq)</div>
</div>
<script>
// A cover drawn on a 2D canvas: fine text makes the mipmap levels easy to tell apart.
const cover = document.createElement('canvas');
cover.width = cover.height = 256;
const g = cover.getContext('2d');
g.fillStyle = '#5b3f99'; g.fillRect(0, 0, 256, 256);
g.fillStyle = '#e09a10'; g.fillRect(0, 170, 256, 12);
g.fillStyle = '#fff'; g.textAlign = 'center';
g.font = 'bold 30px Georgia, serif'; g.fillText("The", 128, 70);
g.fillText("Clockmaker's", 128, 108); g.fillText('Paradox', 128, 146);
g.font = '14px sans-serif';
for (let i = 0; i < 4; i++) g.fillText('BookNest Classics  ·  BookNest Classics', 128, 206 + i * 14);

const gl = document.getElementById('view').getContext('webgl2');
const vs = `#version 300 es
in vec2 aPos; out vec2 vUV;
void main() { vUV = vec2(aPos.x, 1.0 - aPos.y); gl_Position = vec4(aPos * 2.0 - 1.0, 0.0, 1.0); }`;
const fs = `#version 300 es
precision mediump float;
in vec2 vUV;
uniform sampler2D uCover;
uniform int uMode;
out vec4 outColor;
void main() {
  if (uMode == 0) outColor = texture(uCover, vUV);             // level chosen from derivatives
  else if (uMode == 1) outColor = texture(uCover, vUV, 2.5);   // same, biased 2.5 levels blurrier
  else if (uMode == 2) outColor = textureLod(uCover, vUV, 4.0);// explicit level 4 (16x16 texels)
  else {
    float q = 0.6 + 0.8 * vUV.y;                               // divide by the last component first
    outColor = textureProj(uCover, vec3(vUV * q, q));
    outColor.rgb *= 0.85 + 0.15 * vUV.y;
  }
}`;
const program = gl.createProgram();
for (const [type, src] of [[gl.VERTEX_SHADER, vs], [gl.FRAGMENT_SHADER, fs]]) {
  const s = gl.createShader(type);
  gl.shaderSource(s, src); gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
  gl.attachShader(program, s);
}
gl.linkProgram(program);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,0, 1,0, 1,1, 0,0, 1,1, 0,1]), gl.STATIC_DRAW);
const aPos = gl.getAttribLocation(program, 'aPos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

gl.bindTexture(gl.TEXTURE_2D, gl.createTexture());
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, cover);
gl.generateMipmap(gl.TEXTURE_2D);                               // levels 256, 128, ... 1
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

gl.clearColor(0.97, 0.96, 0.93, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
const uMode = gl.getUniformLocation(program, 'uMode');
for (let i = 0; i < 4; i++) {
  gl.viewport(i * 150 + 8, 5, 134, 210);
  gl.uniform1i(uMode, i);
  gl.drawArrays(gl.TRIANGLES, 0, 6);
}
</script>