Layer Clipping with WebGL

Layer WebGL clipping example.

This example shows how to use the precompose and postcompose rendering hooks to clip layers using WebGL.

<!DOCTYPE html>
<html>
  <head>
    <title>Layer Clipping with WebGL</title>
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.4/css/ol.css" type="text/css">
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src=""></script>
    <script src="https://openlayers.org/en/v4.6.4/build/ol.js"></script>
  </head>
  <body>
    <div id="map" class="map"></div>
    <div id="no-webgl" class="alert alert-danger" style="display: none">
      This example requires a browser that supports <a href="http://get.webgl.org/">WebGL</a>.
    </div>
    <script>
      if (!ol.has.WEBGL) {
        var info = document.getElementById('no-webgl');
        /**
         * display error message
         */
        info.style.display = '';
      } else {

        var osm = new ol.layer.Tile({
          source: new ol.source.OSM()
        });

        var map = new ol.Map({
          layers: [osm],
          renderer: /** @type {Array<ol.renderer.Type>} */ (['webgl', 'canvas']),
          target: 'map',
          controls: ol.control.defaults({
            attributionOptions: {
              collapsible: false
            }
          }),
          view: new ol.View({
            center: [0, 0],
            zoom: 2
          })
        });

        var fragmentShaderSource = [
          'precision mediump float;',
          'void main() {',
          '}'
        ].join('');

        var vertexShaderSource = [
          'attribute vec2 a_position;',
          'void main() {',
          '  gl_Position = vec4(a_position, 0, 1);',
          '}'
        ].join('');

        osm.on('precompose', function(event) {
          var context = event.glContext;

          var gl = context.getGL();
          var program = gl.createProgram();

          var vertexShader = gl.createShader(gl.VERTEX_SHADER);
          gl.shaderSource(vertexShader, vertexShaderSource);
          gl.compileShader(vertexShader);
          gl.attachShader(program, vertexShader);

          var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
          gl.shaderSource(fragmentShader, fragmentShaderSource);
          gl.compileShader(fragmentShader);
          gl.attachShader(program, fragmentShader);

          gl.linkProgram(program);
          context.useProgram(program);

          var positionLocation = gl.getAttribLocation(program, 'a_position');

          gl.enable(gl.STENCIL_TEST);
          gl.colorMask(false, false, false, false);
          gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
          gl.stencilFunc(gl.ALWAYS, 1, 0xff);

          var buffer = gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            // first band
            -1.0, -1.0, -0.75, -1.0, -1.0, 1.0,
            -1.0, 1.0, -0.75, -1.0, -0.75, 1.0,
            // second band
            -0.5, -1.0, -0.25, -1.0, -0.5, 1.0,
            -0.5, 1.0, -0.25, -1.0, -0.25, 1.0,
            // third band
            0.0, -1.0, 0.25, -1.0, 0.0, 1.0,
            0.0, 1.0, 0.25, -1.0, 0.25, 1.0,
            // forth band
            0.5, -1.0, 0.75, -1.0, 0.5, 1.0,
            0.5, 1.0, 0.75, -1.0, 0.75, 1.0
          ]), gl.STATIC_DRAW);

          gl.enableVertexAttribArray(positionLocation);
          gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
          gl.drawArrays(gl.TRIANGLES, 0, 24);

          gl.bindBuffer(gl.ARRAY_BUFFER, null);
          gl.deleteBuffer(buffer);

          gl.colorMask(true, true, true, true);
          gl.stencilFunc(gl.NOTEQUAL, 0, 0xff);
          gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
        });

        osm.on('postcompose', function(event) {
          var context = event.glContext;
          var gl = context.getGL();
          gl.disable(gl.STENCIL_TEST);
        });
      }
    </script>
  </body>
</html>