diff --git a/CHANGES.md b/CHANGES.md index 0416e828a01d..dd0bf89269a6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ Beta Releases * Added `CorridorOutlineGeometry`. * Improved runtime generation of GLSL shaders. * Added new built-in GLSL functions `czm_getLambertDiffuse` and `czm_getSpecular`. +* Made sun size accurate. +* Added `Scene.sunBloom` to enable/disable the bloom filter on the sun. The bloom filter should be disabled for better frame rates on mobile devices. ### b20 - 2013-09-03 diff --git a/Source/Core/Math.js b/Source/Core/Math.js index 270eeb1de24e..8917e36f2b68 100644 --- a/Source/Core/Math.js +++ b/Source/Core/Math.js @@ -165,11 +165,11 @@ define([ CesiumMath.GRAVITATIONALPARAMETER = 3.986004418e14; /** - * Radius of the sun in meters: 6.995e8 + * Radius of the sun in meters: 6.955e8 * @type {Number} * @constant */ - CesiumMath.SOLAR_RADIUS = 6.995e8; + CesiumMath.SOLAR_RADIUS = 6.955e8; /** * 64 * 1024 diff --git a/Source/Scene/Material.js b/Source/Scene/Material.js index c9631e7fcfdf..fc9b6b40e0fe 100644 --- a/Source/Scene/Material.js +++ b/Source/Scene/Material.js @@ -9,6 +9,7 @@ define([ '../Core/combine', '../Core/defaultValue', '../Core/defined', + '../Core/defineProperties', '../Core/destroyObject', '../Core/Cartesian2', '../Core/Matrix2', @@ -50,6 +51,7 @@ define([ combine, defaultValue, defined, + defineProperties, destroyObject, Cartesian2, Matrix2, @@ -396,9 +398,11 @@ define([ this._count = undefined; initializeMaterial(description, this); - Object.defineProperty(this, 'type', { - value : this.type, - writable : false + defineProperties(this, { + type : { + value : this.type, + writable : false + } }); if (!defined(Material._uniformList[this.type])) { diff --git a/Source/Scene/Primitive.js b/Source/Scene/Primitive.js index 1ee290c1479f..62c206e9e5bd 100644 --- a/Source/Scene/Primitive.js +++ b/Source/Scene/Primitive.js @@ -2,6 +2,7 @@ define([ '../Core/defaultValue', '../Core/defined', + '../Core/defineProperties', '../Core/DeveloperError', '../Core/destroyObject', '../Core/Matrix4', @@ -29,6 +30,7 @@ define([ ], function( defaultValue, defined, + defineProperties, DeveloperError, destroyObject, Matrix4, @@ -878,16 +880,23 @@ define([ var perInstanceAttributes = this._perInstanceAttributeIndices[index]; var attributes = {}; + var properties = {}; + var hasProperties = false; for (var name in perInstanceAttributes) { if (perInstanceAttributes.hasOwnProperty(name)) { - Object.defineProperty(attributes, name, { + hasProperties = true; + properties[name] = { get : createGetFunction(name, perInstanceAttributes), set : createSetFunction(name, perInstanceAttributes, this._dirtyAttributes) - }); + }; } } + if (hasProperties) { + defineProperties(attributes, properties); + } + this._lastPerInstanceAttributeIndex = index; return attributes; diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 89a1cd23e450..d757dd0eb46e 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -127,7 +127,7 @@ define([ this._shaderFrameCount = 0; - this._sunPostProcess = new SunPostProcess(); + this._sunPostProcess = undefined; this._commandList = []; this._frustumCommandsList = []; @@ -168,6 +168,15 @@ define([ */ this.sun = undefined; + /** + * Uses a bloom filter on the sun when enabled. + * + * @type {Boolean} + * @default true + */ + this.sunBloom = true; + this._sunBloom = undefined; + /** * The background color, which is only visible if there is no sky box, i.e., {@link Scene#skyBox} is undefined. * @@ -657,12 +666,25 @@ define([ var context = scene._context; var us = context.getUniformState(); + if (defined(scene.sun) && scene.sunBloom !== scene._sunBloom) { + if (scene.sunBloom) { + scene._sunPostProcess = new SunPostProcess(); + } else { + scene._sunPostProcess = scene._sunPostProcess.destroy(); + } + + scene._sunBloom = scene.sunBloom; + } else if (!defined(scene.sun) && defined(scene._sunPostProcess)) { + scene._sunPostProcess = scene._sunPostProcess.destroy(); + scene._sunBloom = false; + } + var skyBoxCommand = (frameState.passes.color && defined(scene.skyBox)) ? scene.skyBox.update(context, frameState) : undefined; var skyAtmosphereCommand = (frameState.passes.color && defined(scene.skyAtmosphere)) ? scene.skyAtmosphere.update(context, frameState) : undefined; var sunCommand = (frameState.passes.color && defined(scene.sun)) ? scene.sun.update(context, frameState) : undefined; var sunVisible = isSunVisible(sunCommand, frameState); - if (sunVisible) { + if (sunVisible && scene.sunBloom) { passState.framebuffer = scene._sunPostProcess.update(context); } @@ -670,7 +692,7 @@ define([ Color.clone(clearColor, clear.color); clear.execute(context, passState); - if (sunVisible) { + if (sunVisible && scene.sunBloom) { scene._sunPostProcess.clear(context, scene.backgroundColor); } @@ -690,8 +712,11 @@ define([ if (defined(sunCommand) && sunVisible) { sunCommand.execute(context, passState); - scene._sunPostProcess.execute(context); - passState.framebuffer = undefined; + + if (scene.sunBloom) { + scene._sunPostProcess.execute(context); + passState.framebuffer = undefined; + } } var clearDepthStencil = scene._clearDepthStencilCommand; diff --git a/Source/Scene/Sun.js b/Source/Scene/Sun.js index 7e853f57a8f4..bdf77cb5a61c 100644 --- a/Source/Scene/Sun.js +++ b/Source/Scene/Sun.js @@ -1,32 +1,58 @@ /*global define*/ define([ '../Core/BoundingSphere', + '../Core/Cartesian2', '../Core/Cartesian3', + '../Core/Cartesian4', '../Core/ComponentDatatype', '../Core/defined', + '../Core/defineProperties', '../Core/destroyObject', '../Core/Math', '../Core/PrimitiveType', + '../Core/Geometry', + '../Core/GeometryAttribute', + '../Core/Color', + '../Core/BoundingRectangle', + '../Core/Matrix4', '../Renderer/BlendingState', '../Renderer/BufferUsage', '../Renderer/DrawCommand', + '../Renderer/PixelFormat', + '../Renderer/ClearCommand', + './SceneTransforms', './SceneMode', '../Shaders/SunVS', - '../Shaders/SunFS' + '../Shaders/SunFS', + '../Shaders/ViewportQuadVS', + '../Shaders/SunTextureFS' ], function( BoundingSphere, + Cartesian2, Cartesian3, + Cartesian4, ComponentDatatype, defined, + defineProperties, destroyObject, CesiumMath, PrimitiveType, + Geometry, + GeometryAttribute, + Color, + BoundingRectangle, + Matrix4, BlendingState, BufferUsage, DrawCommand, + PixelFormat, + ClearCommand, + SceneTransforms, SceneMode, SunVS, - SunFS) { + SunFS, + ViewportQuadVS, + SunTextureFS) { "use strict"; /** @@ -37,19 +63,11 @@ define([ * @constructor * * @example - * scene.sun = new Sun(); + * scene.sun = new Cesium.Sun(); * * @see Scene.sun */ var Sun = function() { - this._command = new DrawCommand(); - - this._boundingVolume = new BoundingSphere(); - this._boundingVolume.radius = CesiumMath.SOLAR_RADIUS * 30.0; - - this._boundingVolume2D = new BoundingSphere(); - this._boundingVolume2D.radius = this._boundingVolume.radius; - /** * Determines if the sun will be shown. * @@ -57,8 +75,105 @@ define([ * @default true */ this.show = true; + + this._command = new DrawCommand(); + this._boundingVolume = new BoundingSphere(); + this._boundingVolume2D = new BoundingSphere(); + + this._texture = undefined; + this._dimensions = undefined; + this._radiusTS = undefined; + this._size = undefined; + + this.glowFactor = 1.0; + this._glowFactorDirty = false; + + var that = this; + this._uniformMap = { + u_texture : function() { + return that._texture; + }, + u_size : function() { + return that._size; + } + }; }; + defineProperties(Sun.prototype, { + /** + * Gets or sets a number that controls how "bright" the Sun's lens flare appears + * to be. Zero shows just the Sun's disc without any flare. + * Use larger values for a more pronounced flare around the Sun. + * + * @memberof Sun.prototype + * @type {Number} + * @default 1.0 + */ + glowFactor : { + get : function () { return this._glowFactor; }, + set : function (glowFactor) { + glowFactor = Math.max(glowFactor, 0.0); + this._glowFactor = glowFactor; + this._glowFactorDirty = true; + } + } + }); + + var viewportAttributeIndices = { + position : 0, + textureCoordinates : 1 + }; + + function getVertexArray(context) { + // Per-context cache for viewport quads + var vertexArray = context.cache.viewportQuad_vertexArray; + + if (defined(vertexArray)) { + return vertexArray; + } + + var geometry = new Geometry({ + attributes : { + position : new GeometryAttribute({ + componentDatatype : ComponentDatatype.FLOAT, + componentsPerAttribute : 2, + values : [ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ] + }), + + textureCoordinates : new GeometryAttribute({ + componentDatatype : ComponentDatatype.FLOAT, + componentsPerAttribute : 2, + values : [ + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 + ] + }) + }, + primitiveType : PrimitiveType.TRIANGLES + }); + + vertexArray = context.createVertexArrayFromGeometry({ + geometry : geometry, + attributeIndices : viewportAttributeIndices, + bufferUsage : BufferUsage.STATIC_DRAW + }); + + context.cache.viewportQuad_vertexArray = vertexArray; + return vertexArray; + } + + var scratchPositionWC = new Cartesian2(); + var scratchLimbWC = new Cartesian2(); + var scratchCartesian3 = new Cartesian3(); + var scratchCartesian4 = new Cartesian4(); + /** * @private */ @@ -76,6 +191,61 @@ define([ return undefined; } + var canvasDimensions = frameState.canvasDimensions; + + if (!defined(this._texture) || !Cartesian2.equals(canvasDimensions, this._dimensions) || this._glowFactorDirty) { + this._texture = this._texture && this._texture.destroy(); + this._dimensions = Cartesian2.clone(canvasDimensions, this._dimensions); + this._glowFactorDirty = false; + + var size = Math.max(canvasDimensions.x, canvasDimensions.y); + size = Math.pow(2.0, Math.ceil(Math.log(size) / Math.log(2.0)) - 2.0); + + this._texture = context.createTexture2D({ + width : size, + height : size, + pixelFormat : PixelFormat.RGBA + }); + + var fbo = context.createFramebuffer({ + colorTexture : this._texture + }); + fbo.destroyAttachments = false; + + var clearCommand = new ClearCommand(); + clearCommand.color = new Color(0.0, 0.0, 0.0, 0.0); + clearCommand.framebuffer = fbo; + + var drawCommand = new DrawCommand(); + drawCommand.owner = this; + drawCommand.primitiveType = PrimitiveType.TRIANGLE_FAN; + drawCommand.vertexArray = getVertexArray(context); + drawCommand.shaderProgram = context.getShaderCache().getShaderProgram(ViewportQuadVS, SunTextureFS, viewportAttributeIndices); + drawCommand.framebuffer = fbo; + drawCommand.renderState = context.createRenderState({ + viewport : new BoundingRectangle(0.0, 0.0, size, size) + }); + + this._glowLengthTS = this._glowFactor * 5.0; + this._radiusTS = (1.0 / (1.0 + 2.0 * this._glowLengthTS)) * 0.5; + + var that = this; + drawCommand.uniformMap = { + u_glowLengthTS : function() { + return that._glowLengthTS; + }, + u_radiusTS : function() { + return that._radiusTS; + } + }; + + clearCommand.execute(context); + drawCommand.execute(context); + + drawCommand.shaderProgram.release(); + fbo.destroy(); + } + var command = this._command; if (!defined(command.vertexArray)) { @@ -111,6 +281,7 @@ define([ command.renderState = context.createRenderState({ blending : BlendingState.ALPHA_BLEND }); + command.uniformMap = this._uniformMap; command.boundingVolume = new BoundingSphere(); } @@ -125,12 +296,32 @@ define([ boundingVolume2D.center.y = sunPositionCV.x; boundingVolume2D.center.z = sunPositionCV.y; + boundingVolume.radius = CesiumMath.SOLAR_RADIUS + CesiumMath.SOLAR_RADIUS * this._glowLengthTS; + boundingVolume2D.radius = boundingVolume.radius; + if (mode === SceneMode.SCENE3D) { BoundingSphere.clone(boundingVolume, command.boundingVolume); } else if (mode === SceneMode.COLUMBUS_VIEW) { BoundingSphere.clone(boundingVolume2D, command.boundingVolume); } + var position = SceneTransforms.computeActualWgs84Position(frameState, sunPosition, scratchCartesian4); + + var dist = Cartesian3.magnitude(Cartesian3.subtract(position, frameState.camera.position, scratchCartesian4)); + var projMatrix = context.getUniformState().getProjection(); + + var positionEC = Cartesian3.clone(Cartesian3.ZERO, scratchCartesian3); + positionEC.z = -dist; + var positionCC = Matrix4.multiplyByPoint(projMatrix, positionEC, scratchCartesian4); + var positionWC = SceneTransforms.clipToWindowCoordinates(context.getCanvas(), positionCC, scratchPositionWC); + + positionEC.x = CesiumMath.SOLAR_RADIUS; + var limbCC = Matrix4.multiplyByPoint(projMatrix, positionEC, scratchCartesian4); + var limbWC = SceneTransforms.clipToWindowCoordinates(context.getCanvas(), limbCC, scratchLimbWC); + + this._size = Math.ceil(Cartesian2.magnitude(Cartesian2.subtract(limbWC, positionWC, scratchCartesian4))); + this._size = 2.0 * this._size * (1.0 + 2.0 * this._glowLengthTS); + return command; }; @@ -173,6 +364,9 @@ define([ var command = this._command; command.vertexArray = command.vertexArray && command.vertexArray.destroy(); command.shaderProgram = command.shaderProgram && command.shaderProgram.release(); + + this._texture = this._texture && this._texture.destroy(); + return destroyObject(this); }; diff --git a/Source/Shaders/Builtin/Constants/solarRadius.glsl b/Source/Shaders/Builtin/Constants/solarRadius.glsl index 0e8d9d6b4a31..d1f25e531093 100644 --- a/Source/Shaders/Builtin/Constants/solarRadius.glsl +++ b/Source/Shaders/Builtin/Constants/solarRadius.glsl @@ -10,4 +10,4 @@ * // GLSL declaration * const float czm_solarRadius = ...; */ -const float czm_solarRadius = 699500000.0; \ No newline at end of file +const float czm_solarRadius = 695500000.0; \ No newline at end of file diff --git a/Source/Shaders/SunFS.glsl b/Source/Shaders/SunFS.glsl index e1fbffc13fd6..58852482c392 100644 --- a/Source/Shaders/SunFS.glsl +++ b/Source/Shaders/SunFS.glsl @@ -1,11 +1,8 @@ +uniform sampler2D u_texture; + varying vec2 v_textureCoordinates; void main() { - vec4 color = vec4(1.0, 1.0, 0.0, 1.0); - - float b = smoothstep(0.03, 0.3, length(v_textureCoordinates - vec2(0.5))); - color.ba = mix(vec2(1.0), vec2(0.0), b); - - gl_FragColor = color; + gl_FragColor = texture2D(u_texture, v_textureCoordinates); } diff --git a/Source/Shaders/SunTextureFS.glsl b/Source/Shaders/SunTextureFS.glsl new file mode 100644 index 000000000000..05a674e7d4a6 --- /dev/null +++ b/Source/Shaders/SunTextureFS.glsl @@ -0,0 +1,56 @@ +uniform float u_glowLengthTS; +uniform float u_radiusTS; + +varying vec2 v_textureCoordinates; + +vec2 rotate(vec2 p, vec2 direction) +{ + return vec2(p.x * direction.x - p.y * direction.y, p.x * direction.y + p.y * direction.x); +} + +vec4 addBurst(vec2 position, vec2 direction) +{ + vec2 rotatedPosition = rotate(position, direction) * vec2(25.0, 0.75); + float radius = length(rotatedPosition); + float burst = 1.0 - smoothstep(0.0, 0.55, radius); + + return vec4(burst); +} + +void main() +{ + vec2 position = v_textureCoordinates - vec2(0.5); + float radius = length(position); + float surface = step(radius, u_radiusTS); + vec4 color = vec4(1.0, 1.0, surface + 0.2, surface); + + float glow = 1.0 - smoothstep(0.0, 0.55, radius); + color.ba += mix(vec2(0.0), vec2(1.0), glow) * 0.75; + + vec4 burst = vec4(0.0); + + // The following loop has been manually unrolled for speed, to + // avoid sin() and cos(). + // + //for (float i = 0.4; i < 3.2; i += 1.047) { + // vec2 direction = vec2(sin(i), cos(i)); + // burst += 0.4 * addBurst(position, direction); + // + // direction = vec2(sin(i - 0.08), cos(i - 0.08)); + // burst += 0.3 * addBurst(position, direction); + //} + + burst += 0.4 * addBurst(position, vec2(0.38942, 0.92106)); // angle == 0.4 + burst += 0.4 * addBurst(position, vec2(0.99235, 0.12348)); // angle == 0.4 + 1.047 + burst += 0.4 * addBurst(position, vec2(0.60327, -0.79754)); // angle == 0.4 + 1.047 * 2.0 + + burst += 0.3 * addBurst(position, vec2(0.31457, 0.94924)); // angle == 0.4 - 0.08 + burst += 0.3 * addBurst(position, vec2(0.97931, 0.20239)); // angle == 0.4 + 1.047 - 0.08 + burst += 0.3 * addBurst(position, vec2(0.66507, -0.74678)); // angle == 0.4 + 1.047 * 2.0 - 0.08 + + // End of manual loop unrolling. + + color += clamp(burst, vec4(0.0), vec4(1.0)) * 0.15; + + gl_FragColor = clamp(color, vec4(0.0), vec4(1.0)); +} diff --git a/Source/Shaders/SunVS.glsl b/Source/Shaders/SunVS.glsl index b4f81ab369a4..f2f2fbc5ee55 100644 --- a/Source/Shaders/SunVS.glsl +++ b/Source/Shaders/SunVS.glsl @@ -1,5 +1,7 @@ attribute vec2 direction; +uniform float u_size; + varying vec2 v_textureCoordinates; void main() @@ -17,9 +19,7 @@ void main() vec4 positionEC = czm_view * position; vec4 positionWC = czm_eyeToWindowCoordinates(positionEC); - vec4 limb = czm_eyeToWindowCoordinates(positionEC + vec4(czm_solarRadius, 0.0, 0.0, 0.0)); - vec2 halfSize = vec2(length(limb.xy - positionWC.xy)); - halfSize *= 30.0; + vec2 halfSize = vec2(u_size * 0.5); halfSize *= ((direction * 2.0) - 1.0); gl_Position = czm_viewportOrthographic * vec4(positionWC.xy + halfSize, -positionWC.z, 1.0); diff --git a/Specs/Scene/SunSpec.js b/Specs/Scene/SunSpec.js index 7b3173c9b71f..c2ee8a6a9118 100644 --- a/Specs/Scene/SunSpec.js +++ b/Specs/Scene/SunSpec.js @@ -130,6 +130,32 @@ defineSuite([ sun.destroy(); }); + it('can set glow factor', function() { + var sun = scene.sun = new Sun(); + sun.glowFactor = 0.0; + expect(sun.glowFactor).toEqual(0.0); + sun.glowFactor = 2.0; + expect(sun.glowFactor).toEqual(2.0); + }); + + it('draws without lens flare', function() { + scene.sun = new Sun(); + scene.sun.glowFactor = 0.0; + scene.initializeFrame(); + scene.render(); + + var us = scene.getContext().getUniformState(); + var camera = scene.getCamera(); + + var sunPosition = us.getSunPositionWC(); + var cameraPosition = sunPosition.normalize().multiplyByScalar(1e8); + camera.controller.lookAt(sunPosition, cameraPosition, Cartesian3.UNIT_Z); + + scene.initializeFrame(); + scene.render(); + expect(scene.getContext().readPixels()).toNotEqual([0, 0, 0, 0]); + }); + it('isDestroyed', function() { var sun = new Sun(); expect(sun.isDestroyed()).toEqual(false);