javascript - Openlayers 访问低级 WebGL 着色器

标签 javascript webgl shader openlayers-3

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

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

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

这是应该 Hook 的正确事件吗?

下面的代码有一个 openstreetmap 图 block 图层作为基础和静态图像,在 this example 之后顶部有重投影。 .

这个想法是操纵顶部静态图像作为 Canvas 来测试低级 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>

最佳答案

我找到了一种通过使用以 ImageCanvas 作为源的图像层来在 openlayers 之上操作低级 WebGL 的方法:

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>

关于javascript - Openlayers 访问低级 WebGL 着色器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49052419/

相关文章:

opengl - GLSL-为什么保留按位运算符?什么是使用按位运算的好选择(模拟按位的浮点运算)

javascript - 如何使用ajax将新页面加载到div

Javascript 正则表达式匹配字母或 -

javascript - 加载本地文件时出现"Cross origin requests are only supported for HTTP."错误

javascript - OpenGL将汽车朝圆形路径旋转

c++ - 如何将这些 OpenGL 着色器转换为适用于 Android NDK 的 GLES3 的 OpenGL ES 着色器?

glsl - 如何在 GLSL 结构体中初始化数组

javascript - 如何仅限制网格中的jqgrid文本区域高度

opengl-es - 如何在 webgl 渲染 channel 之间传递高精度 uv 坐标?

GLSL:找不到我的自定义函数