diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 5a84444fed7a..265847de3837 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -121,6 +121,14 @@ define([ this._surfaceShaderSet = new GlobeSurfaceShaderSet(); + this._surfaceShaderSet.baseVertexShaderSource = new ShaderSource({ + sources : [GlobeVS] + }); + + this._surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({ + sources : [GlobeFS] + }); + this._surface = new QuadtreePrimitive({ tileProvider : new GlobeSurfaceTileProvider({ terrainProvider : terrainProvider, @@ -240,7 +248,6 @@ define([ * @default false */ this.enableLighting = false; - this._enableLighting = false; /** * The distance where everything becomes lit. This only takes effect @@ -272,7 +279,6 @@ define([ this._oceanNormalMap = undefined; this._zoomedOutOceanSpecularIntensity = 0.5; - this._hasVertexNormals = false; this._lightingFadeDistance = new Cartesian2(this.lightingFadeOutDistance, this.lightingFadeInDistance); var that = this; @@ -872,80 +878,13 @@ define([ } } - // Initial compile or re-compile if uber-shader parameters changed - var hasVertexNormals = terrainProvider.ready && terrainProvider.hasVertexNormals; - var enableLighting = this.enableLighting; - if (!defined(northPoleCommand.shaderProgram) || - !defined(southPoleCommand.shaderProgram) || - modeChanged || - this._hasVertexNormals !== hasVertexNormals || - this._enableLighting !== enableLighting) { - - var getPosition3DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition3DMode(position3DWC); }'; - var getPosition2DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition2DMode(position3DWC); }'; - var getPositionColumbusViewMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionColumbusViewMode(position3DWC); }'; - var getPositionMorphingMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionMorphingMode(position3DWC); }'; - - var getPositionMode; - - switch (mode) { - case SceneMode.SCENE3D: - getPositionMode = getPosition3DMode; - break; - case SceneMode.SCENE2D: - getPositionMode = getPosition2DMode; - break; - case SceneMode.COLUMBUS_VIEW: - getPositionMode = getPositionColumbusViewMode; - break; - case SceneMode.MORPHING: - getPositionMode = getPositionMorphingMode; - break; - } - - var get2DYPositionFractionGeographicProjection = 'float get2DYPositionFraction() { return get2DGeographicYPositionFraction(); }'; - var get2DYPositionFractionMercatorProjection = 'float get2DYPositionFraction() { return get2DMercatorYPositionFraction(); }'; - - var get2DYPositionFraction; - - if (projection instanceof GeographicProjection) { - get2DYPositionFraction = get2DYPositionFractionGeographicProjection; - } else { - get2DYPositionFraction = get2DYPositionFractionMercatorProjection; - } - - var surfaceShaderSet = this._surfaceShaderSet; - - var shaderDefines = []; - - if (enableLighting) { - if (hasVertexNormals) { - shaderDefines.push('ENABLE_VERTEX_LIGHTING'); - } else { - shaderDefines.push('ENABLE_DAYNIGHT_SHADING'); - } - } - - surfaceShaderSet.baseVertexShaderSource = new ShaderSource({ - defines : shaderDefines, - sources : [GlobeVS, getPositionMode, get2DYPositionFraction] - }); - - surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({ - defines : shaderDefines, - sources : [GlobeFS] - }); - - surfaceShaderSet.invalidateShaders(); + !defined(southPoleCommand.shaderProgram)) { var poleShaderProgram = context.replaceShaderProgram(northPoleCommand.shaderProgram, GlobeVSPole, GlobeFSPole, terrainAttributeLocations); northPoleCommand.shaderProgram = poleShaderProgram; southPoleCommand.shaderProgram = poleShaderProgram; - - this._hasVertexNormals = hasVertexNormals; - this._enableLighting = enableLighting; } this._occluder.cameraPosition = frameState.camera.positionWC; @@ -981,6 +920,7 @@ define([ tileProvider.zoomedOutOceanSpecularIntensity = this._zoomedOutOceanSpecularIntensity; tileProvider.hasWaterMask = hasWaterMask; tileProvider.oceanNormalMap = this._oceanNormalMap; + tileProvider.enableLighting = this.enableLighting; surface.update(context, frameState, commandList); diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 58cb3df3424c..f99eb8a090ff 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -2,13 +2,21 @@ define([ '../Core/defined', '../Core/destroyObject', + '../Scene/SceneMode', '../Scene/terrainAttributeLocations' ], function( defined, destroyObject, + SceneMode, terrainAttributeLocations) { "use strict"; + function GlobeSurfaceShader(numberOfDayTextures, flags, shaderProgram) { + this.numberOfDayTextures = numberOfDayTextures; + this.flags = flags; + this.shaderProgram = shaderProgram; + } + /** * Manages the shaders used to shade the surface of a {@link Globe}. * @@ -19,32 +27,45 @@ define([ this.baseVertexShaderSource = undefined; this.baseFragmentShaderSource = undefined; this._attributeLocations = terrainAttributeLocations; - this._shaders = {}; + + this._shadersByTexturesFlags = []; } - GlobeSurfaceShaderSet.prototype.invalidateShaders = function() { - var shaders = this._shaders; - for ( var keyword in shaders) { - if (shaders.hasOwnProperty(keyword)) { - shaders[keyword].destroy(); - } - } + GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, sceneMode, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves, enableLighting, hasVertexNormals, useWebMercatorProjection) { + var flags = sceneMode | + (applyBrightness << 2) | + (applyContrast << 3) | + (applyHue << 4) | + (applySaturation << 5) | + (applyGamma << 6) | + (applyAlpha << 7) | + (showReflectiveOcean << 8) | + (showOceanWaves << 9) | + (enableLighting << 10) | + (hasVertexNormals << 11) | + (useWebMercatorProjection << 12); - this._shaders = {}; - }; + var surfaceShader = surfaceTile.surfaceShader; + if (defined(surfaceShader) && + surfaceShader.numberOfDayTextures === numberOfDayTextures && + surfaceShader.flags === flags) { - function getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) { - return '' + textureCount + (+applyBrightness) + (+applyContrast) + (+applyHue) + (+applySaturation) + (+applyGamma) + (+applyAlpha) + (+showReflectiveOcean) + (+showOceanWaves); - } + return surfaceShader.shaderProgram; + } + + // New tile, or tile changed number of textures or flags. + var shadersByFlags = this._shadersByTexturesFlags[numberOfDayTextures]; + if (!defined(shadersByFlags)) { + shadersByFlags = this._shadersByTexturesFlags[numberOfDayTextures] = []; + } - GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) { - var key = getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves); - var shader = this._shaders[key]; - if (!defined(shader)) { + surfaceShader = shadersByFlags[flags]; + if (!defined(surfaceShader)) { + // Cache miss - we've never seen this combination of numberOfDayTextures and flags before. var vs = this.baseVertexShaderSource.clone(); var fs = this.baseFragmentShaderSource.clone(); - fs.defines.push('TEXTURE_UNITS ' + textureCount); + fs.defines.push('TEXTURE_UNITS ' + numberOfDayTextures); if (applyBrightness) { fs.defines.push('APPLY_BRIGHTNESS'); @@ -72,12 +93,22 @@ define([ fs.defines.push('SHOW_OCEAN_WAVES'); } + if (enableLighting) { + if (hasVertexNormals) { + vs.defines.push('ENABLE_VERTEX_LIGHTING'); + fs.defines.push('ENABLE_VERTEX_LIGHTING'); + } else { + vs.defines.push('ENABLE_DAYNIGHT_SHADING'); + fs.defines.push('ENABLE_DAYNIGHT_SHADING'); + } + } + var computeDayColor = '\ -vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\ -{\n\ - vec4 color = initialColor;\n'; + vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\ + {\n\ + vec4 color = initialColor;\n'; - for (var i = 0; i < textureCount; ++i) { + for (var i = 0; i < numberOfDayTextures; ++i) { computeDayColor += '\ color = sampleAndBlend(\n\ color,\n\ @@ -95,19 +126,78 @@ vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\ } computeDayColor += '\ - return color;\n\ -}'; + return color;\n\ + }'; fs.sources.push(computeDayColor); - shader = context.createShaderProgram(vs, fs, this._attributeLocations); - this._shaders[key] = shader; + var getPosition3DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition3DMode(position3DWC); }'; + var getPosition2DMode = 'vec4 getPosition(vec3 position3DWC) { return getPosition2DMode(position3DWC); }'; + var getPositionColumbusViewMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionColumbusViewMode(position3DWC); }'; + var getPositionMorphingMode = 'vec4 getPosition(vec3 position3DWC) { return getPositionMorphingMode(position3DWC); }'; + + var getPositionMode; + + switch (sceneMode) { + case SceneMode.SCENE3D: + getPositionMode = getPosition3DMode; + break; + case SceneMode.SCENE2D: + getPositionMode = getPosition2DMode; + break; + case SceneMode.COLUMBUS_VIEW: + getPositionMode = getPositionColumbusViewMode; + break; + case SceneMode.MORPHING: + getPositionMode = getPositionMorphingMode; + break; + } + + vs.sources.push(getPositionMode); + + if (sceneMode !== SceneMode.SCENE3D) { + var get2DYPositionFractionGeographicProjection = 'float get2DYPositionFraction() { return get2DGeographicYPositionFraction(); }'; + var get2DYPositionFractionMercatorProjection = 'float get2DYPositionFraction() { return get2DMercatorYPositionFraction(); }'; + + var get2DYPositionFraction; + + if (useWebMercatorProjection) { + get2DYPositionFraction = get2DYPositionFractionMercatorProjection; + } else { + get2DYPositionFraction = get2DYPositionFractionGeographicProjection; + } + + vs.sources.push(get2DYPositionFraction); + } + + var shader = context.createShaderProgram(vs, fs, this._attributeLocations); + surfaceShader = shadersByFlags[flags] = new GlobeSurfaceShader(numberOfDayTextures, flags, shader); } - return shader; + + surfaceTile.surfaceShader = surfaceShader; + return surfaceShader.shaderProgram; }; GlobeSurfaceShaderSet.prototype.destroy = function() { - this.invalidateShaders(); + var shadersByTexturesFlags = this._shadersByTexturesFlags; + for (var textureCount in shadersByTexturesFlags) { + if (shadersByTexturesFlags.hasOwnProperty(textureCount)) { + var shadersByFlags = shadersByTexturesFlags[textureCount]; + if (!defined(shadersByFlags)) { + continue; + } + + for (var flags in shadersByFlags) { + if (shadersByFlags.hasOwnProperty(flags)) { + var shader = shadersByFlags[flags]; + if (defined(shader)) { + shader.shaderProgram.destroy(); + } + } + } + } + } + return destroyObject(this); }; diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 8c423ee9ad2f..ba6039493603 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -131,6 +131,8 @@ define([ this.pickBoundingSphere = new BoundingSphere(); this.pickTerrain = undefined; + + this.surfaceShader = undefined; }; defineProperties(GlobeSurfaceTile.prototype, { @@ -265,8 +267,7 @@ define([ if (defined(this.vertexArray)) { indexBuffer = this.vertexArray.indexBuffer; - this.vertexArray.destroy(); - this.vertexArray = undefined; + this.vertexArray = this.vertexArray.destroy(); if (!indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) { --indexBuffer.referenceCount; @@ -279,8 +280,7 @@ define([ if (defined(this.wireframeVertexArray)) { indexBuffer = this.wireframeVertexArray.indexBuffer; - this.wireframeVertexArray.destroy(); - this.wireframeVertexArray = undefined; + this.wireframeVertexArray = this.wireframeVertexArray.destroy(); if (!indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) { --indexBuffer.referenceCount; diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 085ee9d0f4a1..e48671865475 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -95,6 +95,7 @@ define([ this.hasWaterMask = false; this.oceanNormalMap = undefined; this.zoomedOutOceanSpecularIntensity = 0.5; + this.enableLighting = false; this._quadtree = undefined; this._terrainProvider = options.terrainProvider; @@ -806,6 +807,7 @@ define([ var showReflectiveOcean = tileProvider.hasWaterMask && defined(waterMaskTexture); var oceanNormalMap = tileProvider.oceanNormalMap; var showOceanWaves = showReflectiveOcean && defined(oceanNormalMap); + var hasVertexNormals = tileProvider.terrainProvider.ready && tileProvider.terrainProvider.hasVertexNormals; if (showReflectiveOcean) { --maxTextures; @@ -826,6 +828,8 @@ define([ var southMercatorYLow = 0.0; var oneOverMercatorHeight = 0.0; + var useWebMercatorProjection = false; + if (frameState.mode !== SceneMode.SCENE3D) { var projection = frameState.mapProjection; var southwest = projection.project(Rectangle.southwest(tile.rectangle), southwestScratch); @@ -860,6 +864,8 @@ define([ southMercatorYLow = southMercatorY - float32ArrayScratch[0]; oneOverMercatorHeight = 1.0 / (northMercatorY - southMercatorY); + + useWebMercatorProjection = true; } } @@ -986,7 +992,7 @@ define([ uniformMap.waterMask = waterMaskTexture; Cartesian4.clone(surfaceTile.waterMaskTranslationAndScale, uniformMap.waterMaskTranslationAndScale); - command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(context, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves); + command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(context, frameState.mode, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves, tileProvider.enableLighting, hasVertexNormals, useWebMercatorProjection); command.renderState = renderState; command.primitiveType = PrimitiveType.TRIANGLES; command.vertexArray = surfaceTile.vertexArray;