Best Practices & Style

39.1 Code style

Always specify #version and the profile; enable only the extensions you need.

Consistent naming: a prefix for attributes (aPos), v for varyings (vNormal), u for uniforms (uTime), o/out for outputs, UPPER_CASE macros and constants.

Use explicit layout(location/binding) qualifiers — removes host-side lookups and mismatches.

Write floats as floats (1.0, not 1) — required in GLSL ES.

Keep functions small and reusable (lighting.glsl, noise.glsl); comment coordinate spaces (// world space).

Normalize interpolated vectors in the fragment shader.

Use const for compile-time constants; make loop bounds constant where possible.

Format with clang-format (works reasonably for GLSL) and validate with glslangValidator in CI.

39.2 Correctness checklist

Every vector used in dot/reflect is normalized when it should be.

All lighting vectors are in the same space (world or view).

Color textures are sRGB; data textures are linear; output is gamma-encoded exactly once.

HDR values are tone-mapped before display.

No reads of uninitialized variables; outputs written on all paths.

No pow(negative, y), sqrt(negative), normalize(vec3(0)), division by zero, acos outside [-1, 1] (clamp first).

Texture sampling with derivatives happens in uniform control flow.

Buffer layouts (std140/std430) match host structs exactly.

Integer varyings are flat.

Precision qualifiers are adequate on mobile.

39.3 Performance checklist

Profile before optimizing; know whether you're vertex, fragment, bandwidth, or CPU bound.

Reduce overdraw and full-screen passes; render expensive effects at lower resolution.

Use texture compression, mipmaps, and channel packing.

Batch, instance, and use indirect drawing.

Prefer compute shaders for data-parallel work that doesn't need rasterization.

Cache compiled programs/pipelines; avoid compiling shaders mid-frame (stutter).