From 8448aba616dfc10ef268002c828230c779fcc898 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 21 Jun 2012 16:39:47 +0200 Subject: [PATCH] Add initial WebGL renderer skeleton --- src/ol/renderer/WebGL.js | 257 +++++++++++++++++++++++++++++---------- 1 file changed, 190 insertions(+), 67 deletions(-) diff --git a/src/ol/renderer/WebGL.js b/src/ol/renderer/WebGL.js index 0f7b2bbedb..6f7a385e63 100644 --- a/src/ol/renderer/WebGL.js +++ b/src/ol/renderer/WebGL.js @@ -8,6 +8,9 @@ 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'); /** @@ -17,13 +20,13 @@ goog.require('goog.webgl'); * @extends {ol.renderer.MapRenderer} */ ol.renderer.WebGL = function(target) { - + /** * @private * @type {!Element} */ this.canvas_ = goog.dom.createDom('canvas', 'ol-renderer-webgl-canvas'); // Suppose to have: style: 'width:100%;height:100%;' - + /** * @private * @type {WebGLRenderingContext} @@ -36,28 +39,92 @@ ol.renderer.WebGL = function(target) { 'preserveDrawingBuffer': false })); 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.base(this, target); + + /** + * @private + * @type {Object.} + */ + 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); - - goog.base(this, target); - - /** - * @type Array. - * @private - */ - this.renderers_ = []; + + 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. @@ -66,80 +133,136 @@ ol.renderer.WebGL.isSupported = function() { return !goog.isNull( goog.dom.createDom('canvas').getContext('experimental-webgl') ); }; -/** - * @param {Array.} 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) { - function finder(candidate) { - return candidate.getLayer() === layer; +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; } - 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.} - */ -ol.renderer.WebGL.preferredRenderers = ["svg", "canvas", "vml"]; /** - * @param {ol.layer.Layer} layer - * @returns {Function} + * @param {ol.Tile} tile Tile. + * @protected */ -ol.renderer.WebGL.prototype.pickRendererType = function(layer) { - // maps candidate renderer types to candidate renderers - var types = {}; +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]; + } +}; - function picker(Candidate) { - var supports = Candidate.isSupported() && Candidate.canRender(layer); - if (supports) { - types[Candidate.getType()] = Candidate; + +/** + * @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; } - return supports; - } - var Candidates = goog.array.some(ol.renderer.WebGL.registry_, picker); - - // check to see if any preferred renderers are available - var preferences = ol.renderer.WebGL.preferredRenderers; - - var Renderer; - for (var i=0, ii=preferences.length; i} - * @private - */ -ol.renderer.WebGL.registry_ = []; /** - * @param {Function} Renderer */ -ol.renderer.WebGL.register = function(Renderer) { - ol.renderer.WebGL.registry_.push(Renderer); +ol.renderer.WebGL.prototype.redraw = function() { + this.draw(this.renderedLayers_, this.renderedCenter_, this.renderedResolution_, this.renderedAnimate_); };