Add initial WebGL renderer skeleton

This commit is contained in:
Tom Payne
2012-06-21 16:39:47 +02:00
parent 435557b425
commit 8448aba616
+184 -61
View File
@@ -8,6 +8,9 @@ goog.require('ol.renderer.MapRenderer');
goog.require('ol.layer.Layer'); goog.require('ol.layer.Layer');
goog.require('ol.Loc'); goog.require('ol.Loc');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.vec.Mat4');
goog.require('goog.webgl'); goog.require('goog.webgl');
/** /**
@@ -36,27 +39,91 @@ ol.renderer.WebGL = function(target) {
'preserveDrawingBuffer': false 'preserveDrawingBuffer': false
})); }));
goog.asserts.assert(!goog.isNull(this.gl_), "The WebGL is not supported on your browser. Check http://get.webgl.org/"); goog.asserts.assert(!goog.isNull(this.gl_), "The WebGL is not supported on your browser. Check http://get.webgl.org/");
var gl = this.gl_;
goog.dom.append(target, this.canvas_); goog.dom.append(target, this.canvas_);
goog.base(this, target);
/**
* @private
* @type {Object.<string, WebGLTexture>}
*/
this.textureCache_ = {};
var gl = this.gl_;
var clearColor = [0, 0, 0]; // hardcoded background color var clearColor = [0, 0, 0]; // hardcoded background color
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1); gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
gl.disable(goog.webgl.DEPTH_TEST); gl.disable(goog.webgl.DEPTH_TEST);
gl.disable(goog.webgl.SCISSOR_TEST); gl.disable(goog.webgl.SCISSOR_TEST);
gl.disable(goog.webgl.CULL_FACE); gl.disable(goog.webgl.CULL_FACE);
goog.base(this, target); 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();
/**
* @type Array.<ol.renderer.LayerRenderer>
* @private
*/
this.renderers_ = [];
}; };
goog.inherits(ol.renderer.WebGL, ol.renderer.MapRenderer); goog.inherits(ol.renderer.WebGL, ol.renderer.MapRenderer);
/** /**
* Determine if this renderer type is supported in this environment. * Determine if this renderer type is supported in this environment.
* A static method. * A static method.
@@ -66,80 +133,136 @@ ol.renderer.WebGL.isSupported = function() {
return !goog.isNull( goog.dom.createDom('canvas').getContext('experimental-webgl') ); return !goog.isNull( goog.dom.createDom('canvas').getContext('experimental-webgl') );
}; };
/**
* @param {Array.<ol.layer.Layer>} layers
* @param {ol.Loc} center
* @param {number} resolution
*/
ol.renderer.WebGL.prototype.draw = function(layers, center, resolution) {
};
/** /**
* @param {ol.layer.Layer} layer * @param {ol.Tile} tile Tile.
* @protected
*/ */
ol.renderer.WebGL.prototype.getRenderer = function(layer) { ol.renderer.WebGL.prototype.bindTexture = function(tile) {
function finder(candidate) { var gl = this.gl_;
return candidate.getLayer() === layer; 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;
} }
return goog.array.find(this.renderers_, finder);
}; };
/** /**
* @param {ol.layer.Layer} layer * @param {ol.Tile} tile Tile.
* @protected
*/ */
ol.renderer.WebGL.prototype.createRenderer = function(layer) { ol.renderer.WebGL.prototype.handleTileLoad = function(tile) {
this.redraw();
}; };
/**
* List of preferred renderer types. Layer renderers have a getType method
* that returns a string describing their type. This list determines the
* preferences for picking a layer renderer.
*
* @type {Array.<string>}
*/
ol.renderer.WebGL.preferredRenderers = ["svg", "canvas", "vml"];
/** /**
* @param {ol.layer.Layer} layer * @param {ol.Tile} tile Tile.
* @returns {Function} * @protected
*/ */
ol.renderer.WebGL.prototype.pickRendererType = function(layer) { ol.renderer.WebGL.prototype.handleTileDestroy = function(tile) {
// maps candidate renderer types to candidate renderers var gl = this.gl_;
var types = {}; var url = tile.getUrl();
if (url in this.textureCache_) {
gl.deleteTexture(this.textureCache_[url]);
delete this.textureCache_[url];
}
};
function picker(Candidate) {
var supports = Candidate.isSupported() && Candidate.canRender(layer); /**
if (supports) { * @inheritDoc
types[Candidate.getType()] = Candidate; */
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;
} }
return supports; var tileLayer = /** @type {ol.layer.TileLayer} */ (layer);
} var tileSet = layer.getData(bounds, resolution);
var Candidates = goog.array.some(ol.renderer.WebGL.registry_, picker); var tiles = tileSet.getTiles();
var i, j, row, tile, tileBounds, positions, texture;
// check to see if any preferred renderers are available for (i = 0; i < tiles.length; ++i) {
var preferences = ol.renderer.WebGL.preferredRenderers; row = tiles[i];
for (j = 0; j < row.length; ++j) {
var Renderer; tile = row[j];
for (var i=0, ii=preferences.length; i<ii; ++i) { if (!tile.isLoaded()) {
Renderer = preferences[i]; continue;
if (Renderer) { }
break; 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;
// if we didn't find any of the preferred renderers, use the first
return Renderer || Candidates[0] || null;
}; };
/**
* @type {Array.<Function>}
* @private
*/
ol.renderer.WebGL.registry_ = [];
/** /**
* @param {Function} Renderer
*/ */
ol.renderer.WebGL.register = function(Renderer) { ol.renderer.WebGL.prototype.redraw = function() {
ol.renderer.WebGL.registry_.push(Renderer); this.draw(this.renderedLayers_, this.renderedCenter_, this.renderedResolution_, this.renderedAnimate_);
}; };