WebGL Shaders Overview
Shaders are small programs that run on the GPU to process various stages of graphics rendering. WebGL uses GLSL (OpenGL Shading Language) to write shader code.
Types of Shaders
WebGL primarily has two types of shaders:
- Vertex Shader
- Fragment Shader
Vertex Shader
Function
- Processes data for each vertex
- Responsible for coordinate transformations
- Converts 3D coordinates to clip space coordinates
Inputs
attribute: Vertex attributes (position, color, normal, texture coordinates, etc.)uniform: Uniform variables (transformation matrices, lighting parameters, etc.)
Outputs
gl_Position: Vertex position in clip space (required)varying: Interpolated data passed to the fragment shader
Code Example
glsl// Vertex shader attribute vec3 a_position; attribute vec3 a_color; attribute vec2 a_texCoord; uniform mat4 u_modelMatrix; uniform mat4 u_viewMatrix; uniform mat4 u_projectionMatrix; varying vec3 v_color; varying vec2 v_texCoord; void main() { // Coordinate transformation: Model space → World space → Camera space → Clip space mat4 mvp = u_projectionMatrix * u_viewMatrix * u_modelMatrix; gl_Position = mvp * vec4(a_position, 1.0); // Pass data to fragment shader v_color = a_color; v_texCoord = a_texCoord; }
Fragment Shader
Function
- Processes each fragment (potential pixel)
- Calculates final pixel color
- Performs texture sampling, lighting calculations, etc.
Inputs
varying: Interpolated data from vertex shaderuniform: Uniform variables (textures, material parameters, etc.)
Outputs
gl_FragColor: Final pixel color (WebGL 1.0)out vec4 fragColor: Output variable (WebGL 2.0)
Code Example
glsl// Fragment shader precision mediump float; varying vec3 v_color; varying vec2 v_texCoord; uniform sampler2D u_texture; uniform float u_opacity; void main() { // Texture sampling vec4 texColor = texture2D(u_texture, v_texCoord); // Blend vertex color with texture color vec3 finalColor = v_color * texColor.rgb; // Set final color gl_FragColor = vec4(finalColor, texColor.a * u_opacity); }
Vertex Shader vs Fragment Shader Comparison
| Feature | Vertex Shader | Fragment Shader |
|---|---|---|
| Execution Frequency | Executed once per vertex | Executed once per fragment |
| Primary Task | Coordinate transformation | Color calculation |
| Required Output | gl_Position | gl_FragColor |
| Typical Operations | Matrix multiplication, vertex lighting | Texture sampling, pixel-level lighting |
| Performance Impact | Less impact when few vertices | Usually has greater performance impact |
| Precision Requirements | Typically uses highp | Often uses mediump for optimization |
Shader Variable Types
attribute (Vertex shader only)
glslattribute vec3 a_position; // Vertex position attribute vec2 a_texCoord; // Texture coordinates attribute vec3 a_normal; // Normal vector
uniform (Available in both shaders)
glsluniform mat4 u_matrix; // Transformation matrix uniform sampler2D u_texture; // Texture sampler uniform vec3 u_lightColor; // Light color
varying (Passing data from vertex to fragment)
glsl// Vertex shader varying vec2 v_texCoord; // Fragment shader varying vec2 v_texCoord; // Same name variable receives interpolated data
Shader Compilation and Linking Process
javascript// 1. Create shader objects const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); // 2. Set source code gl.shaderSource(vertexShader, vertexSource); gl.shaderSource(fragmentShader, fragmentSource); // 3. Compile shaders gl.compileShader(vertexShader); gl.compileShader(fragmentShader); // 4. Create program object const program = gl.createProgram(); // 5. Attach shaders gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); // 6. Link shader program gl.linkProgram(program); // 7. Use program gl.useProgram(program);
Performance Optimization Tips
- Reduce varying variables: Minimize data transfer between vertex and fragment shaders
- Calculate in vertex shader: Compute at vertex level rather than fragment level when possible
- Use appropriate precision: Use
mediumporlowpon mobile devices - Avoid branching: GPUs are not good at handling conditional branches
- Precompute constants: Use uniforms instead of repeated calculations