WebGL Texture Overview
Textures in WebGL are images used to add surface details to 3D objects. Through texture mapping, 2D images can be applied to 3D geometry surfaces, making rendering results more realistic.
Texture Usage Process
1. Create Texture Object
javascriptconst texture = gl.createTexture();
2. Bind Texture
javascript// Bind to 2D texture target gl.bindTexture(gl.TEXTURE_2D, texture);
3. Upload Texture Data
javascript// Method 1: Load from Image object gl.texImage2D( gl.TEXTURE_2D, // Target 0, // Level of detail (mipmap level) gl.RGBA, // Internal format gl.RGBA, // Source format gl.UNSIGNED_BYTE, // Data type image // Image object ); // Method 2: Upload pixel data directly gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, width, // Width height, // Height 0, // Border (must be 0) gl.RGBA, gl.UNSIGNED_BYTE, pixels // Uint8Array pixel data );
4. Configure Texture Parameters
javascript// Texture wrapping 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); // Texture filtering gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
5. Generate Mipmap (Optional)
javascriptgl.generateMipmap(gl.TEXTURE_2D);
Texture Parameters Explained
Texture Wrapping
Controls behavior when texture coordinates exceed [0, 1] range:
| Parameter | Description |
|---|---|
gl.REPEAT | Repeat texture (default) |
gl.CLAMP_TO_EDGE | Extend edge pixels |
gl.MIRRORED_REPEAT | Mirror repeat |
javascript// S axis (horizontal) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // T axis (vertical) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
Visual effects:
shellREPEAT: CLAMP_TO_EDGE: MIRRORED_REPEAT: |ABCD|ABCD| |AAAA|ABCD|DDDD| |ABCD|DCBA|ABCD| |ABCD|ABCD| |AAAA|ABCD|DDDD| |ABCD|DCBA|ABCD|
Texture Filtering
Controls interpolation method during texture sampling:
Magnification Filter (MAG_FILTER)
When texture is magnified (texture pixels < screen pixels):
| Parameter | Description |
|---|---|
gl.NEAREST | Nearest neighbor sampling, pixelated effect |
gl.LINEAR | Bilinear interpolation, smooth effect (recommended) |
Minification Filter (MIN_FILTER)
When texture is minified (texture pixels > screen pixels):
| Parameter | Description |
|---|---|
gl.NEAREST | Nearest neighbor sampling |
gl.LINEAR | Bilinear interpolation |
gl.NEAREST_MIPMAP_NEAREST | Nearest mipmap + nearest sampling |
gl.LINEAR_MIPMAP_NEAREST | Nearest mipmap + linear interpolation |
gl.NEAREST_MIPMAP_LINEAR | Linear between mipmaps + nearest sampling |
gl.LINEAR_MIPMAP_LINEAR | Trilinear filtering (highest quality) |
javascript// Use linear filtering for magnification gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); // Use trilinear mipmap filtering for minification gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
Complete Texture Loading Example
javascriptfunction loadTexture(gl, url) { const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); // Set temporary pixel (used before image loads) const level = 0; const internalFormat = gl.RGBA; const width = 1; const height = 1; const border = 0; const srcFormat = gl.RGBA; const srcType = gl.UNSIGNED_BYTE; const pixel = new Uint8Array([0, 0, 255, 255]); // Blue gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, srcFormat, srcType, pixel); const image = new Image(); image.onload = function() { gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, image); // Check if image dimensions are power of 2 if (isPowerOf2(image.width) && isPowerOf2(image.height)) { // Power of 2, can use mipmap gl.generateMipmap(gl.TEXTURE_2D); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); } else { // Not power of 2, disable mipmap, set wrap to CLAMP_TO_EDGE 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); }; image.src = url; return texture; } function isPowerOf2(value) { return (value & (value - 1)) == 0; }
Using Textures in Shaders
Vertex Shader
glslattribute vec3 a_position; attribute vec2 a_texCoord; uniform mat4 u_mvpMatrix; varying vec2 v_texCoord; void main() { gl_Position = u_mvpMatrix * vec4(a_position, 1.0); v_texCoord = a_texCoord; // Pass texture coordinates to fragment shader }
Fragment Shader
glslprecision mediump float; varying vec2 v_texCoord; uniform sampler2D u_texture; // Texture sampler void main() { gl_FragColor = texture2D(u_texture, v_texCoord); }
JavaScript Code
javascript// Activate texture unit gl.activeTexture(gl.TEXTURE0); // Bind texture gl.bindTexture(gl.TEXTURE_2D, texture); // Set uniform variable (tell shader which texture unit to use) gl.uniform1i(textureLocation, 0); // Use TEXTURE0
Multiple Texture Binding
javascript// Load multiple textures const texture1 = loadTexture(gl, 'texture1.jpg'); const texture2 = loadTexture(gl, 'texture2.png'); // Bind to different texture units during rendering gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture1); gl.uniform1i(texture1Location, 0); gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, texture2); gl.uniform1i(texture2Location, 1);
glsl// Using multiple textures in fragment shader uniform sampler2D u_texture1; uniform sampler2D u_texture2; varying vec2 v_texCoord; void main() { vec4 color1 = texture2D(u_texture1, v_texCoord); vec4 color2 = texture2D(u_texture2, v_texCoord); gl_FragColor = mix(color1, color2, 0.5); // Blend two textures }
Texture Coordinate System
shell(0, 1) -------- (1, 1) | | | Texture Image | | | (0, 0) -------- (1, 0)
Note: WebGL texture coordinate system origin is at bottom-left, while most image formats have origin at top-left.
Performance Optimization Tips
- Use Texture Atlas: Combine multiple small textures into one large texture to reduce binding switches
- Choose appropriate texture sizes:
- Prefer power-of-2 dimensions (supports mipmap)
- Don't use overly large textures (memory and bandwidth overhead)
- Use compressed texture formats: such as DXT, ETC, PVRTC
- Enable mipmap: Improves rendering quality and performance
- Reuse textures: Avoid loading the same texture multiple times