5.4 KiB
WebGL2 Pitfalls & Common Errors
Use Cases
- Avoiding common GLSL compilation errors when generating standalone WebGL2 shader pages
- Debugging shader compilation failures
- Ensuring shader templates from ShaderToy work correctly in WebGL2
Critical WebGL2 Rules
1. Fragment Coordinate — Use gl_FragCoord.xy
ERROR: 'fragCoord' : undeclared identifier
In WebGL2 fragment shaders, fragCoord is not a built-in variable. Use gl_FragCoord.xy instead.
// WRONG
void main() {
vec2 uv = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
}
// CORRECT
void main() {
vec2 uv = (2.0 * gl_FragCoord.xy - iResolution.xy) / iResolution.y;
}
2. Shadertoy mainImage — Must Wrap in main()
ERROR: '' : Missing main()
If your fragment shader uses void mainImage(out vec4, in vec2), you must provide a main() wrapper.
// WRONG — only defines mainImage but no main()
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// shader code...
fragColor = vec4(col, 1.0);
}
// CORRECT
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// shader code...
fragColor = vec4(col, 1.0);
}
void main() {
mainImage(fragColor, gl_FragCoord.xy);
}
3. Function Declaration Order — Declare Before Use
ERROR: 'functionName' : no matching overloaded function found
GLSL requires functions to be declared before they are used. Forward declarations or reordering is needed.
// WRONG — getAtmosphere() calls getSunDirection() which is defined after
vec3 getAtmosphere(vec3 dir) {
return extra_cheap_atmosphere(dir, getSunDirection()) * 0.5; // Error!
}
vec3 getSunDirection() {
return normalize(vec3(-0.5, 0.8, -0.6));
}
// CORRECT — reorder functions
vec3 getSunDirection() { // Define first
return normalize(vec3(-0.5, 0.8, -0.6));
}
vec3 getAtmosphere(vec3 dir) { // Now can call getSunDirection()
return extra_cheap_atmosphere(dir, getSunDirection()) * 0.5;
}
4. Macro Limitations — #define Cannot Use Functions
ERROR: Various compilation errors with #define macros
Macros are text substitution and cannot call functions or use parentheses in the same way as C++.
// WRONG
#define SUN_DIR normalize(vec3(0.8, 0.4, -0.6))
#define WORLD_TIME (iTime * speed())
// CORRECT — use const
const vec3 SUN_DIR = vec3(0.756, 0.378, -0.567); // Pre-computed normalized value
const float WORLD_TIME = 1.0;
5. Vector Component Access — Terrain Functions
ERROR: 'terrainM' : no matching overloaded function found
When passing positions to terrain functions that expect vec2, extract the XZ components properly.
// WRONG — terrainM expects vec2, but passing vec3
float calcAO(vec3 pos, vec3 nor) {
float d = terrainM(pos + h * nor); // Error: pos + h*nor is vec3
...
}
// CORRECT — extract xz components
float calcAO(vec3 pos, vec3 nor) {
float d = terrainM(pos.xz + h * nor.xz);
...
}
6. Loop Index — Use Runtime Constants
ERROR: Loop index must be a runtime expression
GLSL ES requires loop indices to be determinable at runtime, not compile-time constants in some contexts.
// WRONG — AA is a #define constant
for (int i = 0; i < AA; i++) { ... }
// CORRECT — use a runtime-safe approach
for (int i = 0; i < 4; i++) { ... } // Or pass as uniform
7. Uniform Usage — Avoid Unused Uniforms
ERROR: Uniform optimized away causes gl.getUniformLocation() to return null
If a uniform is declared but not used, the compiler may optimize it out.
// WRONG — iTime declared but used in a conditional that might be false
uniform float iTime;
if (false) { x = iTime; } // iTime optimized away
// CORRECT — always use the uniform in a way the compiler can't optimize out
uniform float iTime;
float t = iTime * 0.0; // Always use iTime somehow
if (someCondition) { x = t; }
Complete WebGL2 Adaptation Checklist
When generating standalone HTML pages:
- Shader Version:
#version 300 esmust be the very first line - Fragment Output: Declare
out vec4 fragColor; - Entry Point: Wrap
mainImage()invoid main()that callsmainImage(fragColor, gl_FragCoord.xy) - Fragment Coord: Use
gl_FragCoord.xynotfragCoord - Preprocessor: Don't use functions in
#definemacros - Function Order: Declare functions before they are used, or use forward declarations
- Texture: Use
texture()nottexture2D() - Attributes:
attribute→in,varying→in/out
Common Error Messages Reference
| Error Message | Likely Cause | Solution |
|---|---|---|
'fragCoord' : undeclared identifier |
Using fragCoord instead of gl_FragCoord.xy |
Replace with gl_FragCoord.xy |
'' : Missing main() |
No main() function defined |
Add wrapper void main() { mainImage(...); } |
'function' : no matching overloaded function |
Wrong argument types or function order | Check parameter types, reorder functions |
return' : function return is not matching |
Return type mismatch | Verify return expression matches declared return type |
#version must be first |
Leading whitespace in shader source | Use .trim() when extracting from script tags |
Uniform null from getUniformLocation |
Uniform optimized away | Ensure uniform is actually used in shader code |
Further Reading
See reference/webgl-pitfalls.md for additional debugging techniques.