Files
openlayers/src/ol/renderer/WebGL.js

269 lines
9.0 KiB
JavaScript

/**
* @fileoverview WebGL based MapRenderer drawing all the supplied layers in OpenGL
*/
goog.provide('ol.renderer.WebGL');
goog.require('ol.renderer.MapRenderer');
goog.require('ol.layer.Layer');
goog.require('ol.Loc');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.vec.Mat4');
goog.require('goog.webgl');
/**
* Initialization of the native WebGL renderer (canvas, context, layers)
* @constructor
* @param {!Element} container
* @extends {ol.renderer.MapRenderer}
*/
ol.renderer.WebGL = function(container) {
/**
* @private
* @type {!Element}
*/
this.canvas_ = goog.dom.createDom('canvas', 'ol-renderer-webgl-canvas'); // Suppose to have: style: 'width:100%;height:100%;'
/**
* @private
* @type {WebGLRenderingContext}
*/
this.gl_ = (this.canvas_.getContext('experimental-webgl', {
'alpha': false,
'depth': false,
'antialias': true,
'stencil': false,
'preserveDrawingBuffer': false
}));
goog.asserts.assert(!goog.isNull(this.gl_), "The WebGL is not supported on your browser. Check http://get.webgl.org/");
goog.dom.append(container, this.canvas_);
goog.base(this, container);
/**
* @private
* @type {Object.<string, WebGLTexture>}
*/
this.textureCache_ = {};
var gl = this.gl_;
var clearColor = [0, 0, 0]; // hardcoded background color
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
gl.disable(goog.webgl.DEPTH_TEST);
gl.disable(goog.webgl.SCISSOR_TEST);
gl.disable(goog.webgl.CULL_FACE);
var fragmentShader = gl.createShader(goog.webgl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, [
'precision mediump float;',
'',
'uniform sampler2D uTexture;',
'',
'varying vec2 vTexCoord;',
'',
'void main(void) {',
' gl_FragColor = vec4(vec3(texture2D(uTexture, vTexCoord)), 1.);',
'}'
].join('\n'));
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, goog.webgl.COMPILE_STATUS)) {
window.console.log(gl.getShaderInfoLog(fragmentShader));
goog.asserts.assert(gl.getShaderParameter(fragmentShader, goog.webgl.COMPILE_STATUS));
}
var vertexShader = gl.createShader(goog.webgl.VERTEX_SHADER);
gl.shaderSource(vertexShader, [
'attribute vec2 aPosition;',
'attribute vec2 aTexCoord;',
'',
'uniform mat4 uMVPMatrix;',
'',
'varying vec2 vTexCoord;',
'',
'void main(void) {',
' gl_Position = uMVPMatrix * vec4(aPosition, 0.0, 1.0);',
' vTexCoord = aTexCoord;',
'}'
].join('\n'));
if (!gl.getShaderParameter(vertexShader, goog.webgl.COMPILE_STATUS)) {
window.console.log(gl.getShaderInfoLog(vertexShader));
goog.asserts.assert(gl.getShaderParameter(vertexShader, goog.webgl.COMPILE_STATUS));
}
var program = gl.createProgram();
gl.attachShader(program, fragmentShader);
gl.attachShader(program, vertexShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, goog.webgl.LINK_STATUS)) {
window.console.log(gl.getProgramInfoLog(program));
goog.asserts.assert(gl.getProgramParameter(program, goog.webgl.LINK_STATUS));
}
this.mvpMatrixLocation_ = gl.getUniformLocation(program, 'uMVPMatrix');
this.textureLocation_ = gl.getUniformLocation(program, 'uTexture');
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(goog.webgl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]), goog.webgl.STATIC_DRAW);
var texCoordLocation = gl.getAttributeLocation(program, 'aTexCoord');
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, goog.webgl.FLOAT, false, 0, 0);
gl.bindBuffer(goog.webgl.ARRAY_BUFFER, null);
this.positionLocation_ = gl.getAttributeLocation(program, 'aPosition');
gl.enableVertexAttribArray(this.positionLocation_);
this.positionBuffer_ = gl.createBuffer();
};
goog.inherits(ol.renderer.WebGL, ol.renderer.MapRenderer);
/**
* Determine if this renderer type is supported in this environment.
* A static method.
* @returns {boolean} This renderer is supported.
*/
ol.renderer.WebGL.isSupported = function() {
return !goog.isNull( goog.dom.createDom('canvas').getContext('experimental-webgl') );
};
/**
* @param {ol.Tile} tile Tile.
* @protected
*/
ol.renderer.WebGL.prototype.bindTexture = function(tile) {
var gl = this.gl_;
var url = tile.getUrl();
if (url in this.textureCache_) {
gl.bindTexture(gl.TEXTURE_2D, this.textureCache_[url]);
} else {
var texture = gl.createTexture();
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA, goog.webgl.UNSIGNED_BYTE, tile.getImg());
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR);
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.LINEAR);
this.textureCache_[url] = texture;
}
};
/**
* @param {ol.Tile} tile Tile.
* @protected
*/
ol.renderer.WebGL.prototype.handleTileLoad = function(tile) {
this.redraw();
};
/**
* @param {ol.Tile} tile Tile.
* @protected
*/
ol.renderer.WebGL.prototype.handleTileDestroy = function(tile) {
var gl = this.gl_;
var url = tile.getUrl();
if (url in this.textureCache_) {
gl.deleteTexture(this.textureCache_[url]);
delete this.textureCache_[url];
}
};
/**
* @inheritDoc
*/
ol.renderer.WebGL.prototype.draw = function(layers, center, resolution, animate) {
var gl = this.gl_;
var width = this.canvas_.width;
var height = this.canvas_.height;
var bounds = new ol.Bounds(
center.getX() - width * resolution / 2,
center.getY() - height * resolution / 2,
center.getX() + width * resolution / 2,
center.getY() + height * resolution / 2,
center.getProjection());
/** @type {goog.vec.Mat4.Type} */
var cameraMatrix;
goog.vec.Mat4.makeIdentity(cameraMatrix);
goog.vec.Mat4.scale(cameraMatrix, resolution, resolution, 1);
goog.vec.Mat4.translate(cameraMatrix, -center.getX(), -center.getY(), 0);
/** @type {goog.vec.Mat4.Type} */
var positionToViewportMatrix;
goog.vec.Mat4.makeIdentity(positionToViewportMatrix);
goog.vec.Mat4.scale(positionToViewportMatrix, 1 / width, 1 / height, 1);
goog.vec.Mat4.multMat(positionToViewportMatrix, cameraMatrix, positionToViewportMatrix);
/** @type {goog.vec.Mat4.Type} */
var viewportToPositionMatrix;
var inverted = goog.vec.Mat4.invert(positionToViewportMatrix, viewportToPositionMatrix);
goog.asserts.assert(inverted);
/** @type {goog.vec.Mat4.Type} */
var targetPixelToPositionMatrix;
goog.vec.Mat4.makeIdentity(targetPixelToPositionMatrix);
goog.vec.Mat4.translate(targetPixelToPositionMatrix, -1, 1, 0);
goog.vec.Mat4.scale(targetPixelToPositionMatrix, 2 / width, -2 / height, 1);
goog.vec.Mat4.multMat(viewportToPositionMatrix, targetPixelToPositionMatrix, targetPixelToPositionMatrix);
gl.clear(goog.webgl.COLOR_BUFFER_BIT);
gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.positionBuffer_);
gl.uniform1i(this.textureLocation_, 0);
gl.uniformMatrix4fv(this.positionLocation_, false, positionToViewportMatrix);
goog.array.forEach(layers, function(layer) {
if (!(layer instanceof ol.layer.TileLayer)) {
return;
}
var tileLayer = /** @type {ol.layer.TileLayer} */ (layer);
var tileSet = layer.getData(bounds, resolution);
var tiles = tileSet.getTiles();
var i, j, row, tile, tileBounds, positions, texture;
for (i = 0; i < tiles.length; ++i) {
row = tiles[i];
for (j = 0; j < row.length; ++j) {
tile = row[j];
if (!tile.isLoaded()) {
continue;
}
tileBounds = tile.getBounds();
positions = [
tileBounds.getMinX(), tileBounds.getMinY(),
tileBounds.getMaxX(), tileBounds.getMinY(),
tileBounds.getMinX(), tileBounds.getMaxY(),
tileBounds.getMaxX(), tileBounds.getMaxY()
];
gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array(positions), goog.webgl.DYNAMIC_DRAW);
gl.vertexAttribPointer(this.positionLocation_, 2, goog.webgl.FLOAT, false, 0, 0);
this.bindTexture(tile);
gl.drawArrays(goog.webgl.TRIANGLES, 0, 4);
}
}
}, this);
this.renderedLayers_ = layers;
this.renderedCenter_ = center;
this.renderedResolution_ = resolution;
this.renderedAnimate_ = animate;
};
/**
*/
ol.renderer.WebGL.prototype.redraw = function() {
this.draw(this.renderedLayers_, this.renderedCenter_, this.renderedResolution_, this.renderedAnimate_);
};