Shading with GPU accelerated math

by Richard

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/gpu.js/1.10.4/gpu.min.js"></script>
<div class="images">
  <img id="img" crossorigin width="400" src="//cdn.shopify.com/s/files/1/0654/9053/products/untitled-untitled-IMG_7342_e1143013-eda0-4fc8-a813-a030c32bdcf3_grande.jpg?v=1525821958">
</div>
<br>
<label>Custom Image
<input type="file" id="pic">
</label>
<br>
<label>
    Spaces
    <input type="range" step="1" value="4" min="2" max="10" id="spaces">
    <span id="spaces-value"></span>
  </label>
<br>
<label>
    Line Exponentiality
    <input type="range" step="0.1" value="1.4" min="1" max="3" id="exp">
    <span id="exp-value"></span>
  </label>
<br>
<label>
    K
    <input type="range" step="0.1" value="30" min="10" max="200" id="k">
    <span id="k-value"></span>
  </label>
<br>
<label>
    Function
    <select id="function">
    <option value="1">Vertical</option>
    <option value="2">Diagonal</option>
    <option value="3">Circular</option>
    <option value="4">Wave</option>
    </select>
  </label>

JavaScript

const gpu = new GPU();
const image = document.getElementById('img');

function lum(r, g, b) {
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
gpu.addFunction(lum);

function verticalLine(x) {
  return x;
}
gpu.addFunction(verticalLine);

function diagonalLine(x, y) {
  return x + y;
}
gpu.addFunction(diagonalLine);

function reverseDiagonalLine(x, y) {
  return y - x;
}
gpu.addFunction(reverseDiagonalLine);

function circleLine(x, y) {
  return Math.round(Math.sqrt(x * x + y * y));
}
gpu.addFunction(circleLine);

function waveLine(x, y) {
  return Math.round(y / this.constants.k * Math.cos(x / this.constants.k));
}
gpu.addFunction(waveLine);

function shade(img) {
  // Get position
  const x = this.thread.x;
  const y = this.thread.y;

  // Get pixel
  const p = img[y][x];

  // Get luminace
  const l = lum(p[0], p[1], p[2])

  // Threshold color
  const space = l * this.constants.spaces;

  // Exponentially space out lines
  const lineSpacing = Math.round(Math.pow(space, this.constants.exp));

  // Default color
  this.color(1, 1, 1);

  // Apply function
  let f = 1;
  if (this.constants.func === 1) {
    f = verticalLine(x);
  } else if (this.constants.func === 2) {
    f = diagonalLine(x, y);
  } else if (this.constants.func === 3) {
    f = circleLine(x, y);
  } else {
    f = waveLine(x, y);
  }

  if (f % lineSpacing === 0) {
    this.color(0, 0, 0);
  }
}

let first = true;

function update() {
  const render = gpu.createKernel(shade)
    .setGraphical(true)
    .setOutput([image.width, image.height])
    .setConstants({
      spaces: parseInt(document.getElementById('spaces').value),
      exp: parseFloat(document.getElementById('exp').value),
      k: parseFloat(document.getElementById('k').value),
      func: parseFloat(document.getElementById('function').value),
    })
  document.getElementById('spaces-value').innerHTML = document.getElementById('spaces').value
  document.getElementById('exp-value').innerHTML = document.getElementById('exp').value
 ...