Working with Textures

Learning Objectives: By the end of this lesson, you will be able to:

  • Apply textures to 2D objects using shader programming
  • Master UV mapping concepts for 2D graphics
  • Implement texture sampling and filtering techniques
  • Create dynamic texture effects and transformations

1. Welcome and Introduction (5 minutes)

Icebreaker Activity

Imagine you’re decorating a room - you wouldn’t just paint every wall the same solid color, would you? You might use wallpaper, hang paintings, or add decorative elements to create visual interest. Textures in 2D graphics serve the same purpose! They add detail, personality, and realism to otherwise flat surfaces. Have you ever wondered how games make simple 2D sprites look so detailed and rich? The answer lies in the powerful combination of textures and shaders!

Lesson Overview

  • Duration: 90 minutes
  • Prerequisites: Basic shader syntax knowledge, understanding of UV coordinates
  • Materials Needed: Shader development environment, sample texture images (provided or downloaded)

2. Learning Objectives and Session Outline (5 minutes)

What We’ll Cover Today:

  1. Texture Fundamentals - Understanding what textures are and how they work in 2D
  2. UV Mapping for 2D - Coordinate systems and texture mapping techniques
  3. Texture Sampling - Reading pixel data from textures efficiently
  4. Filtering and Quality - Making textures look crisp and smooth
  5. Dynamic Texture Effects - Animating and transforming textures in real-time

Success Criteria:

By the end of this session, you should be able to load and display textures in shaders, manipulate UV coordinates for different effects, and understand how texture filtering affects visual quality.

3. Foundation Knowledge (15-20 minutes)

Core Concepts

What Are Textures in 2D Graphics?

Textures are images that provide surface detail to 2D objects. Think of them as digital wallpaper that can be applied to sprites, backgrounds, or UI elements. Unlike 3D graphics where textures wrap around complex geometry, 2D textures typically map to flat rectangular surfaces.

Real-World Analogy: Consider a movie poster. The poster itself is a flat rectangle (like a 2D sprite), but the image printed on it (the texture) contains all the visual detail - characters, text, colors, and effects. The UV mapping process is like deciding how to stretch or position that image on the poster.

Why This Matters: Textures are essential for 2D graphics because they:

  • Provide detailed visual information without complex geometry
  • Enable efficient memory usage through texture atlases
  • Allow for dynamic visual effects through shader manipulation
  • Support scalable graphics through procedural modifications

UV Coordinate System

UV coordinates are a 2D coordinate system used to map textures onto surfaces. The name comes from using U and V as axis labels (to distinguish from X, Y, Z spatial coordinates).

Key Properties:

  • U axis: Horizontal (left to right: 0.0 to 1.0)
  • V axis: Vertical (bottom to top: 0.0 to 1.0)
  • Origin (0,0): Bottom-left corner
  • Maximum (1,1): Top-right corner

Important Note: Some graphics systems use a flipped V axis where (0,0) is top-left and (1,1) is bottom-right. Always check your platform’s convention!

Key Terminology

  • Texture Sampling: The process of reading color values from a texture at specific coordinates
  • Texture Filtering: Methods for determining pixel colors when texture coordinates don’t align perfectly with texture pixels
  • Mipmap: Pre-calculated, lower-resolution versions of a texture for efficient rendering at different distances
  • Texture Atlas: A large texture containing multiple smaller textures or sprites
  • Wrapping Mode: How the texture behaves when UV coordinates go outside the 0.0-1.0 range
  • Texel: A texture pixel - the smallest unit of a texture

4. Detailed Explanation and Examples (25-30 minutes)

Understanding Texture Sampling

Texture sampling is the process of reading color values from a texture using UV coordinates. The GPU performs this operation millions of times per second, making it highly optimized.

Basic Sampling Process

  1. Input: UV coordinates (usually 0.0 to 1.0)
  2. Mapping: Convert UV coordinates to texture pixel locations
  3. Filtering: Determine the final color using nearby pixels
  4. Output: RGBA color value

Texture Coordinate Transformation

// Basic UV coordinate transformations
vec2 uv = v_uv;

// Scaling (make texture appear larger/smaller)
uv *= 2.0;  // Makes texture appear smaller (repeats 2x2 times)
uv *= 0.5;  // Makes texture appear larger (shows 1/4 of texture)

// Translation (move texture around)
uv += vec2(0.5, 0.0);  // Shift right by half
uv += vec2(0.0, 0.5);  // Shift up by half

// Rotation (around center)
float angle = radians(45.0);
mat2 rotation = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
uv = rotation * (uv - 0.5) + 0.5;

Filtering Methods

Nearest Neighbor (Point Filtering):

  • Samples the closest texel
  • Creates sharp, pixelated appearance
  • Best for pixel art and retro aesthetics
  • Fastest performance

Linear Filtering (Bilinear):

  • Interpolates between nearby texels
  • Creates smooth appearance
  • Standard for most applications
  • Good balance of quality and performance

Trilinear Filtering:

  • Linear filtering plus mipmap interpolation
  • Smoothest appearance at all distances
  • Higher performance cost
  • Best for varying distances

Practical Applications

Sprite Rendering:

  • Character animations using sprite sheets
  • UI elements with texture-based graphics
  • Background elements and environment art

Visual Effects:

  • Particle systems with texture-based particles
  • Overlay effects (dirt, scratches, film grain)
  • Animated textures for water, fire, or magical effects

Performance Optimization:

  • Texture atlases to reduce draw calls
  • Compressed texture formats for mobile
  • Procedural texture generation

Common Misconceptions

  1. Misconception: “Higher resolution textures always look better” Reality: Texture resolution should match the display size; oversized textures waste memory and performance

  2. Misconception: “UV coordinates must always stay within 0.0-1.0” Reality: Coordinates outside this range create interesting effects through wrapping modes

  3. Misconception: “Texture filtering is just for 3D graphics” Reality: 2D graphics benefit greatly from proper filtering, especially for scaled sprites

5. Live Coding Demonstration (20-25 minutes)

Example 1: Basic Texture Sampling

// Simple texture display shader
precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture;

void main() {
    // Sample the texture at the current UV coordinate
    vec4 texColor = texture2D(u_texture, v_uv);
    
    // Output the texture color directly
    gl_FragColor = texColor;
}

Explanation:

  • uniform sampler2D u_texture: Declares a texture uniform that will be passed from the application
  • texture2D(u_texture, v_uv): Samples the texture at the given UV coordinates
  • This shader simply displays the texture without any modifications

Example 2: UV Coordinate Manipulation

// Texture tiling and offset shader
precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture;
uniform vec2 u_tiling;    // How many times to repeat the texture
uniform vec2 u_offset;    // How much to offset the texture
uniform float u_time;     // For animation

void main() {
    // Apply tiling and offset
    vec2 uv = v_uv * u_tiling + u_offset;
    
    // Add scrolling animation
    uv.x += u_time * 0.1;  // Scroll horizontally over time
    
    // Sample the texture with modified coordinates
    vec4 texColor = texture2D(u_texture, uv);
    
    gl_FragColor = texColor;
}

Key Points:

  • v_uv * u_tiling: Scales UV coordinates to create texture repetition
  • + u_offset: Shifts the texture position
  • u_time * 0.1: Creates scrolling animation by offsetting based on time

Example 3: Texture Blending and Effects

// Multi-texture blending with color manipulation
precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
uniform float u_blend_factor;
uniform vec3 u_tint_color;
uniform float u_brightness;

void main() {
    // Sample both textures
    vec4 tex1 = texture2D(u_texture1, v_uv);
    vec4 tex2 = texture2D(u_texture2, v_uv);
    
    // Blend the textures
    vec4 blended = mix(tex1, tex2, u_blend_factor);
    
    // Apply color tinting
    blended.rgb *= u_tint_color;
    
    // Apply brightness adjustment
    blended.rgb *= u_brightness;
    
    gl_FragColor = blended;
}

Example 4: Procedural UV Manipulation

// Wavy texture distortion effect
precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture;
uniform float u_time;
uniform float u_wave_strength;

void main() {
    vec2 uv = v_uv;
    
    // Create wave distortion
    float wave = sin(uv.y * 10.0 + u_time * 2.0) * u_wave_strength;
    uv.x += wave;
    
    // Sample texture with distorted coordinates
    vec4 texColor = texture2D(u_texture, uv);
    
    gl_FragColor = texColor;
}

Debugging Demonstration

// Common mistake: Incorrect UV coordinate range
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_texture;

void main() {
    // Wrong: UV coordinates outside 0-1 range without considering wrapping
    vec2 uv = v_uv * 5.0;  // This may not behave as expected
    vec4 texColor = texture2D(u_texture, uv);
    gl_FragColor = texColor;
}

// Better approach: Understand wrapping behavior
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_texture;

void main() {
    vec2 uv = v_uv * 5.0;  // Intentional tiling
    // Or use fract() for guaranteed 0-1 range
    // vec2 uv = fract(v_uv * 5.0);
    
    vec4 texColor = texture2D(u_texture, uv);
    gl_FragColor = texColor;
}

6. Hands-On Practice (30-40 minutes)

Exercise 1: Basic Texture Display (10 minutes)

Objective: Load and display a texture with proper UV mapping

Instructions:

  1. Create a fragment shader that samples a texture
  2. Experiment with different UV coordinate modifications
  3. Try scaling the texture to see tiling effects

Starter Code:

precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture;

void main() {
    // Modify UV coordinates here
    vec2 uv = v_uv;
    
    // Sample texture
    vec4 texColor = texture2D(u_texture, uv);
    
    gl_FragColor = texColor;
}

Challenges:

  • Make the texture repeat 2x2 times
  • Flip the texture horizontally
  • Create a “zoom” effect by scaling UV coordinates

Exercise 2: Animated Texture Effects (15 minutes)

Objective: Create time-based texture animations

Requirements:

  • Implement texture scrolling (moving the texture over time)
  • Create a pulsing effect that scales the texture
  • Add rotation animation around the center

Starter Code:

precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture;
uniform float u_time;

void main() {
    vec2 uv = v_uv;
    
    // Add your animation effects here
    
    vec4 texColor = texture2D(u_texture, uv);
    gl_FragColor = texColor;
}

Extension Ideas:

  • Combine multiple animation effects
  • Create a “breathing” texture that expands and contracts
  • Implement spiral rotation

Exercise 3: Multi-Texture Blending (15 minutes)

Objective: Blend multiple textures with interactive controls

Challenge: Create a shader that:

  • Samples two different textures
  • Blends them based on a uniform parameter
  • Applies different UV transformations to each texture
  • Adds color tinting to the final result

Advanced Challenge: Use the UV coordinates themselves to control the blend factor, creating a gradient blend across the surface.

7. Common Pitfalls and Troubleshooting (10 minutes)

Frequent Mistakes

  1. Problem: Texture appears stretched or distorted Cause: Aspect ratio mismatch between texture and display surface Solution: Adjust UV coordinates to maintain proper aspect ratio or use textures with matching proportions

  2. Problem: Texture looks pixelated even with linear filtering Cause: UV coordinates scaling below texture resolution Solution: Use appropriate texture resolution for the display size or implement proper mipmap levels

  3. Problem: Animated textures appear to “jump” or “stutter” Cause: Large time steps or inappropriate scaling factors Solution: Use fractional time values and smooth animation curves

Debugging Tips

  • Visualize UV coordinates: gl_FragColor = vec4(v_uv, 0.0, 1.0); shows UV as red-green gradient
  • Test with simple textures: Use a checkerboard pattern to easily see UV mapping issues
  • Check texture binding: Ensure textures are properly loaded and bound in your application
  • Validate coordinate ranges: Use fract() to visualize coordinate wrapping behavior

Platform-Specific Considerations

Mobile Devices:

  • Use compressed texture formats (ETC2, ASTC) for better performance
  • Limit texture sizes to reduce memory usage
  • Consider NPOT (Non-Power-of-Two) texture limitations on older devices

Web Browsers:

  • Check WebGL texture size limits
  • Handle texture loading asynchronously
  • Consider CORS issues with external texture files

8. Best Practices and Professional Tips (5 minutes)

Texture Optimization

// Efficient texture sampling patterns
precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture;

void main() {
    // Cache texture samples when using multiple times
    vec4 texColor = texture2D(u_texture, v_uv);
    
    // Use built-in functions for common operations
    vec3 desaturated = mix(texColor.rgb, vec3(dot(texColor.rgb, vec3(0.299, 0.587, 0.114))), 0.5);
    
    gl_FragColor = vec4(desaturated, texColor.a);
}

Memory Management

  • Use texture atlases: Combine multiple sprites into single textures
  • Choose appropriate formats: RGB for opaque textures, RGBA only when transparency is needed
  • Implement texture streaming: Load textures on-demand for large applications
  • Consider compression: Use platform-appropriate compressed formats

Performance Considerations

  • Minimize texture switches: Group objects by texture to reduce state changes
  • Use appropriate filtering: Point filtering for pixel art, linear for smooth graphics
  • Profile texture memory: Monitor VRAM usage and optimize accordingly
  • Batch similar operations: Process multiple sprites with the same shader

9. Summary and Review (5 minutes)

Key Takeaways

  1. Textures add visual richness to 2D graphics without requiring complex geometry
  2. UV coordinates control texture mapping and enable powerful transformation effects
  3. Texture sampling and filtering determine the final visual quality and performance
  4. Dynamic texture effects can create engaging animations and visual interest

Quick Review Questions

  1. What do the U and V axes represent in UV coordinates?
  2. How does texture filtering affect the appearance of scaled textures?
  3. What happens when UV coordinates go outside the 0.0-1.0 range?
  4. Why might you use a texture atlas instead of individual texture files?

Answers:

  1. U is horizontal (left-right), V is vertical (bottom-top) in the texture coordinate system
  2. Filtering determines how pixels are interpolated - nearest gives sharp pixels, linear gives smooth blending
  3. Behavior depends on wrapping mode - can repeat, clamp, or mirror the texture
  4. Texture atlases reduce the number of texture switches, improving rendering performance

For Instructors

Teaching Tips

  • Use visual examples extensively: Show the difference between filtering modes, wrapping behaviors, etc.
  • Provide good sample textures: Include checkerboards, gradients, and interesting patterns for testing
  • Demonstrate common issues: Show what happens with incorrect UV mapping or poor filtering choices
  • Connect to real-world examples: Reference familiar games or applications that use these techniques

Common Student Questions

  • “Why do my textures look blurry?” Usually a filtering or resolution mismatch issue
  • “How do I make textures tile seamlessly?” Explain seamless texture creation and UV coordinate scaling
  • “Can I use multiple textures in one shader?” Yes, through multiple sampler2D uniforms

Assessment Ideas

  • Texture effect gallery: Students create a collection of different texture manipulation effects
  • Problem-solving exercises: Provide visual targets for students to recreate using texture techniques
  • Performance analysis: Compare different approaches and discuss trade-offs

Adaptation Notes

  • For artists: Emphasize the creative possibilities and visual impact
  • For programmers: Focus on the technical aspects and optimization considerations
  • Online delivery: Ensure all students have access to appropriate sample textures
  • Time management: Basic concepts (20 min), live coding (25 min), hands-on practice (35 min), review (10 min)