Parameter Qualifiers

The in, out and inout Function Parameters

GLSL functions have no pointers or references. Parameter qualifiers instead say which way values are copied: in (the default) copies the argument in, out copies the parameter back to the caller on return and starts undefined, and inout does both. Adding const in makes the local copy read-only. The function below converts The Quiet Harbor's blue to gray in place and reports the brightness through an out parameter:

Returning two results with inout and out parametersJavaScript
show(`vec3 color = vec3(0.122, 0.373, 0.545);
      float brightness;
      toGray(color, brightness);
      result = vec4(color, brightness);`,
     `void toGray(inout vec3 color, out float brightness) {
        brightness = dot(color, vec3(0.2126, 0.7152, 0.0722));   // Rec. 709 luma weights
        color = vec3(brightness);
      }`);
Output
0.332056, 0.332056, 0.332056, 0.332056

The order in which out values are copied back is undefined, so never pass one variable to two out parameters. Functions must be declared before use and can be overloaded by parameter type.

A toGray() function with inout and out parameters turning BookNest covers grayHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f4ee; }
  canvas { display: block; width: 100%; max-width: 600px; }
</style>
<canvas id="view" width="600" height="300"></canvas>
<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 mediump float;
uniform float uSplit;        // height (in pixels) of the line between colour and gray
out vec4 outColor;

const vec3 covers[6] = vec3[](vec3(0.122, 0.373, 0.545), vec3(0.36, 0.25, 0.6),
  vec3(0.88, 0.6, 0.06), vec3(0.25, 0.49, 0.23), vec3(0.71, 0.27, 0.18), vec3(0.16, 0.62, 0.56));

// inout: the colour is copied in, changed, and copied back.
// out:   brightness starts undefined and is copied back on return.
void toGray(inout vec3 color, out float brightness) {
  brightness = dot(color, vec3(0.2126, 0.7152, 0.0722));   // Rec. 709 luma weights
  color = vec3(brightness);
}

void main() {
  vec2 p = gl_FragCoord.xy;
  int book = int(p.x / 100.0);
  vec2 local = vec2(mod(p.x, 100.0), p.y);
  if (local.x < 14.0 || local.x > 86.0 || p.y < 40.0 || p.y > 280.0) {
    outColor = vec4(0.97, 0.96, 0.93, 1.0);
    return;
  }
  vec3 color = covers[book];
  float brightness;
  toGray(color, brightness);                     // both results come back through parameters
  if (p.y > uSplit) color = covers[book];        // above the line: the original colour
  // A brightness bar along the cover's left spine, read from the out parameter.
  if (local.x < 22.0) color = p.y < 40.0 + brightness * 240.0 ? vec3(0.1) : vec3(0.85);
  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 uSplit = gl.getUniformLocation(program, 'uSplit');
function frame(ms) {
  gl.uniform1f(uSplit, 160.0 + Math.sin(ms * 0.0012) * 110.0);   // sweep the gray line
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>