Histogram in WebGL

by Subhasish2015

HTML

<script id="hist-vs" type="not-js">
attribute float pixelId;
uniform vec2 u_resolution;
uniform sampler2D u_texture;
void main() {
  vec2 pixel = vec2(mod(pixelId, u_resolution.x), floor(pixelId / u_resolution.x));
  vec2 uv = (pixel + 0.5) / u_resolution;
  vec4 color = texture2D(u_texture, uv);
  float colorSum = (color.r + color.g + color.b) / 3.0 ; 
  gl_Position = vec4((colorSum * 255.0 + 0.5) / 256.0 * 2.0 - 1.0, 0.5, 0, 1);
  gl_PointSize = 1.0;
}
</script>
<script id="hist-fs" type="not-js">
precision mediump float;

void main() {
  gl_FragColor = vec4(1);
}
</script>
<script id="max-fs" type="not-js">
precision mediump float;
uniform sampler2D u_texture;
void main() {
  vec4 maxColor = vec4(0);
  for (int i = 0; i < 256; i++) {
    vec2 uv = vec2((float(i) + 0.5) / 256.0, 0.5);
    maxColor = max(maxColor, vec4(texture2D(u_texture, uv).rgb, 1));
  }
  gl_FragColor = maxColor;
}
</script>
<script id="show-vs" type="not-js">
attribute vec4 position;
void main() {
  gl_Position = position;
}
</script>
<script id="show-fs" type="not-js">
precision mediump float;    
uniform sampler2D u_histTexture;
uniform vec2 u_resolution;
uniform sampler2D u_maxTexture;    
void main() {
  vec3 maxColor = texture2D(u_maxTexture, vec2(0)).rgb;
  vec2 uv = gl_FragCoord.xy / u_resolution;
  vec3 hist = texture2D(u_histTexture, uv).rgb;
  gl_FragColor = vec4(step(uv.yyy, hist / maxColor) * uv.x, 1);
}
</script>
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>

CSS

img,
canvas {
  border: 1px solid black;
  margin: 5px;
}

JavaScript

"use strict";

var canvas = document.createElement("canvas");
canvas.width = 256;
canvas.height = 120;
var m4 = twgl.m4;
var gl = canvas.getContext("webgl");
var ext = gl.getExtension("OES_texture_float");
if (!ext) {
  alert("requires OES_texture_float");
}

twgl.createTexture(gl, {
  //src: "https://i.imgur.com/9Y3sd8S.png",
  src: "https://farm1.staticflickr.com/293/18414763798_cb8ebded43_m_d.jpg",
  // required link: https://www.flickr.com/photos/greggman/18414763798/in/album-72157653822314919/
  min: gl.NEAREST,
  mag: gl.NEAREST,
  wrap: gl.CLAMP_TO_EDGE,
  crossOrigin: "",
}, function(err, tex, img) {
  log("img");
  document.body.appendChild(img);
  log("histogram");
  document.body.appendChild(canvas);

  var quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
  var histProgramInfo = twgl.createProgramInfo(gl, ["hist-vs", "hist-fs"]);
  var pixelIds = [];
  var numIds = img.width * img.height;

  // Just fill a buffer with an incrementing count. If we wanted to make this
  // generic we'd re-use this buffer and just make it bigger if we were
  // processing a bigger image
  for (var i = 0; i < numIds; ++i) {
    pixelIds.push(i);
  }
  var pixelIdBufferInfo = twgl.createBufferInfoFromArrays(gl, {
    pixelId: {
      size: 1,
      data: new Float32Array(pixelIds),
    },
  });

  // make a 256x1 RGBA floating point texture and attach to a framebuffer
  var sumFbi = twgl.createFramebufferInfo(gl, [{
    type: gl.FLOAT,
    min: gl.NEAREST,
    mag: gl.NEAREST,
    wrap: gl.CLAMP_TO_EDGE,
  }, ], 256, 1);
  if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
    alert("can't render to floating point texture");
  }

  // Render sum of each color

  // we're going to render a gl.POINT for each pixel in the source image
  // That point will be positioned based on the color of the source image
  // we're just going to render vec4(1,1,1,1). This blend function will
  // mean each time we render to a specific point that point...