Math Functions

Angle, Trigonometry and Exponential Functions

These built-ins take radians and work per component on float and vec2 to vec4: radians(), degrees(), sin(), cos(), tan(), asin(), acos(), atan() and, new in GLSL ES 3.00, sinh(), cosh(), tanh() and their inverses. atan(y, x) with two arguments returns the full-circle angle of a direction. The exponential group is pow(), exp(), log(), exp2(), log2(), sqrt() and inversesqrt(), the fast 1/sqrt(x) behind normalize():

Angle, trigonometric and exponential functions on the GPUJavaScript
show('float a = radians(180.0); result = vec4(a, degrees(a), sin(a / 2.0), atan(1.0, -1.0));');
show('result = vec4(pow(2.0, 10.0), exp(1.0), log2(1024.0), inversesqrt(4.0));');
show('result = vec4(sinh(1.0), tanh(10.0), sin(vec2(0.0, radians(90.0))));');
Output
3.141593, 180, 1, 2.356194
1024, 2.718282, 10, 0.5
1.175201, 1, 0, 1

atan(1.0, -1.0) is 3π/4, an angle in the upper-left quadrant that the one-argument atan(y / x) could not tell apart from the lower-right one. The last call shows per-component evaluation: sin() of a vec2 returns a vec2, which fills the final two slots. The specification leaves the precision of the trigonometric functions undefined and pow(x, y) undefined for x < 0, so keep angles small and bases positive when results must match across GPUs. Directional and Point Lighting uses pow() for specular highlights.

atan(y, x), sin and cos, pow and tanh plotted by a fragment shaderHTMLLive
<!doctype html>
<style>
  body { margin: 0; font: 12px system-ui, sans-serif; background: #f7f4ee; color: #333; }
  canvas { display: block; width: 100%; max-width: 600px; }
  .names { display: flex; max-width: 600px; text-align: center; font-family: monospace; font-size: 11px; }
  .names div { flex: 1; padding: 4px 2px; }
</style>
<canvas id="view" width="600" height="150"></canvas>
<div class="names">
  <div>atan(y, x) as hue</div><div>sin(x), cos(x)</div><div>pow(x, 0.5 | 1 | 2 | 4)</div><div>tanh(x), sinh(x)</div>
</div>
<script>
const gl = document.getElementById('view').getContext('webgl2');
const vs = `#version 300 es
void main() { gl_Position = vec4(gl_VertexID == 1 ? 3.0 : -1.0, gl_VertexID == 2 ? 3.0 : -1.0, 0.0, 1.0); }`;
const fs = `#version 300 es
precision highp float;
uniform float uTime;
out vec4 outColor;
const float PI = 3.14159265;

// 1 near the curve y = f, using screen-space derivatives for an even line width.
float curve(float y, float f) { return 1.0 - smoothstep(0.0, 1.5, abs(y - f) / fwidth(y - f)); }

void main() {
  int tile = int(gl_FragCoord.x / 150.0);
  vec2 uv = vec2(mod(gl_FragCoord.x, 150.0), gl_FragCoord.y) / vec2(140.0, 150.0);  // 0..1
  vec3 color = vec3(0.97, 0.96, 0.93);
  if (uv.x > 1.0) { outColor = vec4(color, 1.0); return; }
  vec2 p = uv * 2.0 - 1.0;                                   // -1..1
  if (tile == 0) {
    float angle = atan(p.y, p.x) + uTime;                    // full circle: -PI..PI
    vec3 hue = 0.5 + 0.5 * cos(angle + vec3(0.0, 2.1, 4.2)); // cos() works per component
    color = length(p) < 0.9 ? mix(color, hue, smoothstep(0.2, 0.25, length(p))) : color;
  } else if (tile == 1) {
    float x = p.x * PI + uTime;
    color = mix(color, vec3(0.12, 0.37, 0.55), curve(p.y, 0.8 * sin(x)));
    color = mix(color, vec3(0.71, 0.27, 0.18), curve(p.y, 0.8 * cos(x)));
  } else if (tile == 2) {
    const float k[4] = float[](0.5, 1.0, 2.0, 4.0);
    for (int i = 0; i < 4; i++)
      color = mix(color, vec3(0.2 * float(i), 0.4, 0.6 - 0.12 * float(i)), curve(uv.y, pow(uv.x, k[i])));
  } else {
    float x = p.x * 3.0;
    color = mix(color, vec3(0.25, 0.49, 0.23), curve(p.y, tanh(x)));
    color = mix(color, vec3(0.88, 0.6, 0.06), curve(p.y, sinh(x) / 10.0));
  }
  if (abs(p.x) < 0.006 || abs(p.y) < 0.008) color *= 0.85;   // axes
  outColor = vec4(color, 1.0);
}`;
const program = gl.createProgram();
for (const [type, src] of [[gl.VERTEX_SHADER, vs], [gl.FRAGMENT_SHADER, fs]]) {
  const s = gl.createShader(type);
  gl.shaderSource(s, src); gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
  gl.attachShader(program, s);
}
gl.linkProgram(program);
gl.useProgram(program);
gl.bindVertexArray(gl.createVertexArray());
const uTime = gl.getUniformLocation(program, 'uTime');
function frame(ms) {
  gl.uniform1f(uTime, ms * 0.001);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>