Textures are used in WebGL to apply images and patterns to 3D models by loading an image, creating a WebGL texture object, binding it, and then passing it to a shader to sample its colors using texture coordinates. This process requires creating the texture in JavaScript, uploading image data to the GPU, and then sampling from the texture in your fragment shader to determine the final color of a pixel.  
1. Load the image

   Create an Image object in JavaScript to load your texture file.
   Set the src property to the image file path and add an event listener for the load event.

Make sure to use a secure web server for loading textures due to security restrictions.

JavaScript

const image = new Image();
image.src = "resources/f-texture.png";
image.addEventListener('load', function() {
 // Proceed to create the texture after the image has loaded
});

2. Create and bind the texture

   Create a WebGL texture object using gl.createTexture().
   Bind the texture to the gl.TEXTURE_2D target so that subsequent operations affect it.

JavaScript

const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

3. Upload the texture data

   Use gl.texImage2D() to upload the pixel data from the loaded image onto the GPU.
   The gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true) line is useful for flipping the image to correct the orientation, as textures are often loaded upside down.

JavaScript

gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

4. Configure texture parameters (optional but recommended)

   Set parameters like filtering and wrapping behavior to control how the texture looks when scaled or when coordinates go beyond the image boundaries.

gl.generateMipmap(gl.TEXTURE_2D) is a good practice to generate a set of mipmaps for better performance and visual quality at different distances.

JavaScript

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.generateMipmap(gl.TEXTURE_2D);

5. Sample the texture in the fragment shader

   Define a sampler2D uniform in your fragment shader to represent the texture.

Provide a vec2 vertex attribute for texture coordinates (often called v_texCoord) to each vertex.

   Use the texture2D() (or just texture()) function to get the color at a given coordinate.

Code

// Vertex Shader
attribute vec2 a_texCoord;
varying vec2 v_texCoord;

void main() {
 // ...
 v_texCoord = a_texCoord;
}

// Fragment Shader
uniform sampler2D u_texture;
varying vec2 v_texCoord;

void main() {
 gl_FragColor = texture2D(u_texture, v_texCoord);
}