【问题标题】:Openlayers accessing low-level WebGL shadersOpenlayers 访问低级 WebGL 着色器
【发布时间】:2018-03-17 19:17:58
【问题描述】:

是否可以像访问简单的 canvas html5 元素一样从 openlayers 访问低级 WebGL?

var context = event.glContext;
var gl = context.getGL();

我试图调整 low-level clipping example in openlayers 以绘制一个简单的三角形,例如 WebGL Fundamentals tutorial 中的三角形,将其与 precompose 事件挂钩,但没有任何运气。

这是挂钩的正确事件吗?

以下代码有一个 openstreetmap 平铺层作为基础和静态图像,并在 this example 之后在顶部重新投影。

这个想法是将顶部的静态图像作为画布来测试低级 WebGL。但唯一发生的事情是无论我将什么点传递给缓冲区,顶层都会剪切底部 OSM 层。

我对 WebGL 的了解非常有限,有人能指出正确的方向吗?

proj4.defs('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 ' +
  '+x_0=400000 +y_0=-100000 +ellps=airy ' +
  '+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 ' +
  '+units=m +no_defs');
var imageExtent = [0, 0, 700000, 1300000];

var img = new ol.layer.Image({
  source: new ol.source.ImageStatic({
    url: 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/' +
      'British_National_Grid.svg/2000px-British_National_Grid.svg.png',
    crossOrigin: '',
    projection: 'EPSG:27700',
    imageExtent: imageExtent
  })
});

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

var map = new ol.Map({
  //layers: [myosm, img],
  layers: [myosm, img],
  target: 'map',
  renderer: /** @type {Array<ol.renderer.Type>} */ (['webgl', 'canvas']),
  view: new ol.View({
    center: ol.proj.transform(
      ol.extent.getCenter(imageExtent), 'EPSG:27700', 'EPSG:3857'),
    zoom: 4
  })
});

var vertexShaderSource = document.getElementById("2d-vertex-shader").text;
var fragmentShaderSource = document.getElementById("2d-fragment-shader").text;

img.on('precompose', function(e) {
  var context = e.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);

  var positionLocation = gl.getAttribLocation(program, 'a_position');
  var positions = [
    0, 0,
    0, 0.5,
    0.7, 0,
  ];
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  context.useProgram(program);
  gl.enableVertexAttribArray(positionLocation);

  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
  gl.drawArrays(gl.TRIANGLES, 0, 24);
});
<link href="https://openlayers.org/en/v4.6.4/css/ol.css" rel="stylesheet"/>
<script src="https://openlayers.org/en/v4.6.4/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script>
<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 id="2d-vertex-shader" type="notjs">
  // an attribute will receive data from a buffer
  attribute vec4 a_position;

  // all shaders have a main function
  void main() {

    // gl_Position is a special variable a vertex shader
    // is responsible for setting
    gl_Position = a_position;
  }
</script>
<script id="2d-fragment-shader" type="notjs">
  // fragment shaders don't have a default precision so we need
  // to pick one. mediump is a good default
  precision mediump float;

  void main() {
    // gl_FragColor is a special variable a fragment shader
    // is responsible for setting
    gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple
  }
</script>

【问题讨论】:

    标签: javascript webgl shader openlayers-3


    【解决方案1】:

    我找到了一种在 openlayers 之上操作低级 WebGL 的方法,方法是使用带有 ImageCanvas 作为源的图像层:

    var canvasLayer = new ol.layer.Image({
      source: new ol.source.ImageCanvas({
        canvasFunction: canvasFunction,
        projection: 'EPSG:3857'
      })
    });
    

    canvasFunction 将是 WebGL 例程的结果(准备并调用 mainWebGL():

    var canvasFunction = function(extent, resolution, pixelRatio, size, projection) {
      var canvas = document.createElement('canvas');
      canvas.setAttribute('width', size[0]);
      canvas.setAttribute('height', size[1]);
      var gl = canvas.getContext("webgl");
      var glOut = mainWebGL(canvas, gl);
      gl.drawArrays(glOut[1], glOut[2], glOut[3]);
      return canvas;
    };
    

    在所附的工作示例中,我举例说明了如何将 WebGL 层限制在地理范围内。

    当然,WebGL 绘图是在 [-1,1] WebGL 坐标中完成的,为了获得合理的地理配准,应该实现一个带有转换矩阵的额外函数(不包括在内,但应该类似于什么可以找到here 用于谷歌地图)。

    // WebGL - Fundamentals
    //from https://webglfundamentals.org/webgl/webgl-fundamentals.html
    
    function main() {
      webglLayer = mainOL();
    }
    
    
    function mainOL() {
      proj4.defs('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 ' +
        '+x_0=400000 +y_0=-100000 +ellps=airy ' +
        '+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 ' +
        '+units=m +no_defs');
      function transform(extent) {
            return ol.proj.transformExtent(extent, 'EPSG:4326', 'EPSG:3857');
          }
    
       var extents = {
         India: transform([68.17665, 7.96553, 97.40256, 35.49401]),
         Argentina: transform([-73.41544, -55.25, -53.62835, -21.83231]),
         Nigeria: transform([2.6917, 4.24059, 14.57718, 13.86592]),
         Sweden: transform([11.02737, 55.36174, 23.90338, 69.10625])
       };
    
      var canvasFunction = function(extent, resolution, pixelRatio, size, projection) {
        var canvas = document.createElement('canvas');
        var canvasWidth = size[0], 
          canvasHeight = size[1];
        canvas.setAttribute('width', canvasWidth);
        canvas.setAttribute('height', canvasHeight);
        var gl = canvas.getContext("webgl");
        var glOut = mainWebGL(canvas, gl);
        gl.drawArrays(glOut[1], glOut[2], glOut[3]);
        return canvas;
      };
    
      var imageExtent = [0, 0, 700000, 1300000];
      var overlay = new ol.layer.Tile({
            extent: extents.India,
            source: new ol.source.TileJSON({
              url: 'https://api.tiles.mapbox.com/v3/mapbox.world-black.json?secure',
              crossOrigin: 'anonymous'
            })
          });
      var canvasLayer = new ol.layer.Image({
        source: new ol.source.ImageCanvas({
          canvasFunction: canvasFunction,
          projection: 'EPSG:3857'
        })
      });
      var myosm = new ol.layer.Tile({
        source: new ol.source.OSM()
      });
    
      var map = new ol.Map({
        //layers: [myosm
        layers: [myosm],
        target: 'map',
        view: new ol.View({
          center: ol.proj.transform(
            ol.extent.getCenter(imageExtent), 'EPSG:27700', 'EPSG:3857'),
          zoom: 4
        })
      });
    
        map.addLayer(overlay);
      map.addLayer(canvasLayer);
    
      canvasLayer.setExtent(extents["India"]);
      overlay.setExtent(extents["India"]);
      return canvasLayer;
    }
    
    
    function createShader(gl, type, source) {
      var shader = gl.createShader(type);
      gl.shaderSource(shader, source);
      gl.compileShader(shader);
      var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
      if (success) {
        return shader;
      }
      gl.deleteShader(shader);
    }
    
    function createProgram(gl, vertexShader, fragmentShader) {
      var program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
      var success = gl.getProgramParameter(program, gl.LINK_STATUS);
      if (success) {
        return program;
      }
      gl.deleteProgram(program);
    }
    
    function mainWebGL(canvas, gl) {
      // Get the strings for our GLSL shaders
      var vertexShaderSource = document.getElementById("2d-vertex-shader").text;
      var fragmentShaderSource = document.getElementById("2d-fragment-shader").text;
    
      // create GLSL shaders, upload the GLSL source, compile the shaders
      var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
      var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
    
      // Link the two shaders into a program
      var program = createProgram(gl, vertexShader, fragmentShader);
    
      // look up where the vertex data needs to go.
      var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
    
      // Create a buffer and put three 2d clip space points in it
      var positionBuffer = gl.createBuffer();
    
      // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
      gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    
      var positions = [
        0, 0,
        0, -0.5,
        0.2, 0,
      ];
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    
      // code above this line is initialization code.
      // code below this line is rendering code.
      // Tell WebGL how to convert from clip space to pixels
    
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    
      // Clear the canvas
      gl.clearColor(0, 0, 0, 0);
      gl.clear(gl.COLOR_BUFFER_BIT);
    
      // Tell it to use our program (pair of shaders)
      gl.useProgram(program);
    
      // Turn on the attribute
      gl.enableVertexAttribArray(positionAttributeLocation);
    
      // Bind the position buffer.
      gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    
      // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
      var size = 2; // 2 components per iteration
      var type = gl.FLOAT; // the data is 32bit floats
      var normalize = false; // don't normalize the data
      var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
      var offset = 0; // start at the beginning of the buffer
      gl.vertexAttribPointer(
        positionAttributeLocation, size, type, normalize, stride, offset)
    
      // draw
      var primitiveType = gl.TRIANGLES;
      var offset = 0;
      var count = 3;
      //gl.drawArrays(primitiveType, offset, count);
      return [gl, primitiveType, offset, count];
      //return canvas;
    }
    main();
    
    <link href="https://openlayers.org/en/v4.6.4/css/ol.css" rel="stylesheet">
    <script src="https://openlayers.org/en/v4.6.4/build/ol.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script>
    <div id="map" class="map"></div>
    
    <script id="2d-vertex-shader" type="notjs">
      // an attribute will receive data from a buffer
      attribute vec4 a_position;
    
      // all shaders have a main function
      void main() {
    
        // gl_Position is a special variable a vertex shader
        // is responsible for setting
        gl_Position = a_position;
      }
    </script>
    <script id="2d-fragment-shader" type="notjs">
      // fragment shaders don't have a default precision so we need
      // to pick one. mediump is a good default
      precision mediump float;
    
      void main() {
        // gl_FragColor is a special variable a fragment shader
        // is responsible for setting
        gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple
      }
    </script>
    <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
    <script src="https://webglfundamentals.org/webgl/resources/webgl-lessons-helper.js"></script>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多