123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- import Cartesian2 from '../Core/Cartesian2.js';
- import Color from '../Core/Color.js';
- import defined from '../Core/defined.js';
- import defineProperties from '../Core/defineProperties.js';
- import destroyObject from '../Core/destroyObject.js';
- import PixelFormat from '../Core/PixelFormat.js';
- import ClearCommand from '../Renderer/ClearCommand.js';
- import Framebuffer from '../Renderer/Framebuffer.js';
- import PixelDatatype from '../Renderer/PixelDatatype.js';
- import Sampler from '../Renderer/Sampler.js';
- import Texture from '../Renderer/Texture.js';
- import TextureMagnificationFilter from '../Renderer/TextureMagnificationFilter.js';
- import TextureMinificationFilter from '../Renderer/TextureMinificationFilter.js';
- import TextureWrap from '../Renderer/TextureWrap.js';
- /**
- * A post process stage that will get the luminance value at each pixel and
- * uses parallel reduction to compute the average luminance in a 1x1 texture.
- * This texture can be used as input for tone mapping.
- *
- * @constructor
- * @private
- */
- function AutoExposure() {
- this._uniformMap = undefined;
- this._command = undefined;
- this._colorTexture = undefined;
- this._depthTexture = undefined;
- this._ready = false;
- this._name = 'czm_autoexposure';
- this._logDepthChanged = undefined;
- this._useLogDepth = undefined;
- this._framebuffers = undefined;
- this._previousLuminance = undefined;
- this._commands = undefined;
- this._clearCommand = undefined;
- this._minMaxLuminance = new Cartesian2();
- /**
- * Whether or not to execute this post-process stage when ready.
- *
- * @type {Boolean}
- */
- this.enabled = true;
- this._enabled = true;
- /**
- * The minimum value used to clamp the luminance.
- *
- * @type {Number}
- * @default 0.1
- */
- this.minimumLuminance = 0.1;
- /**
- * The maximum value used to clamp the luminance.
- *
- * @type {Number}
- * @default 10.0
- */
- this.maximumLuminance = 10.0;
- }
- defineProperties(AutoExposure.prototype, {
- /**
- * Determines if this post-process stage is ready to be executed. A stage is only executed when both <code>ready</code>
- * and {@link AutoExposure#enabled} are <code>true</code>. A stage will not be ready while it is waiting on textures
- * to load.
- *
- * @memberof AutoExposure.prototype
- * @type {Boolean}
- * @readonly
- */
- ready : {
- get : function() {
- return this._ready;
- }
- },
- /**
- * The unique name of this post-process stage for reference by other stages.
- *
- * @memberof AutoExposure.prototype
- * @type {String}
- * @readonly
- */
- name : {
- get : function() {
- return this._name;
- }
- },
- /**
- * A reference to the texture written to when executing this post process stage.
- *
- * @memberof AutoExposure.prototype
- * @type {Texture}
- * @readonly
- * @private
- */
- outputTexture : {
- get : function() {
- var framebuffers = this._framebuffers;
- if (!defined(framebuffers)) {
- return undefined;
- }
- return framebuffers[framebuffers.length - 1].getColorTexture(0);
- }
- }
- });
- function destroyFramebuffers(autoexposure) {
- var framebuffers = autoexposure._framebuffers;
- if (!defined(framebuffers)) {
- return;
- }
- var length = framebuffers.length;
- for (var i = 0; i < length; ++i) {
- framebuffers[i].destroy();
- }
- autoexposure._framebuffers = undefined;
- autoexposure._previousLuminance.destroy();
- autoexposure._previousLuminance = undefined;
- }
- function createFramebuffers(autoexposure, context) {
- destroyFramebuffers(autoexposure);
- var width = autoexposure._width;
- var height = autoexposure._height;
- var pixelFormat = PixelFormat.RGBA;
- var pixelDatatype = context.halfFloatingPointTexture ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT;
- var sampler = new Sampler({
- wrapS : TextureWrap.CLAMP_TO_EDGE,
- wrapT : TextureWrap.CLAMP_TO_EDGE,
- minificationFilter : TextureMinificationFilter.NEAREST,
- magnificationFilter : TextureMagnificationFilter.NEAREST
- });
- var length = Math.ceil(Math.log(Math.max(width, height)) / Math.log(3.0));
- var framebuffers = new Array(length);
- for (var i = 0; i < length; ++i) {
- width = Math.max(Math.ceil(width / 3.0), 1.0);
- height = Math.max(Math.ceil(height / 3.0), 1.0);
- framebuffers[i] = new Framebuffer({
- context : context,
- colorTextures : [new Texture({
- context : context,
- width : width,
- height : height,
- pixelFormat : pixelFormat,
- pixelDatatype : pixelDatatype,
- sampler : sampler
- })]
- });
- }
- var lastTexture = framebuffers[length - 1].getColorTexture(0);
- autoexposure._previousLuminance = new Framebuffer({
- context : context,
- colorTextures : [new Texture({
- context : context,
- width : lastTexture.width,
- height : lastTexture.height,
- pixelFormat : pixelFormat,
- pixelDatatype : pixelDatatype,
- sampler : sampler
- })]
- });
- autoexposure._framebuffers = framebuffers;
- }
- function destroyCommands(autoexposure) {
- var commands = autoexposure._commands;
- if (!defined(commands)) {
- return;
- }
- var length = commands.length;
- for (var i = 0; i < length; ++i) {
- commands[i].shaderProgram.destroy();
- }
- autoexposure._commands = undefined;
- }
- function createUniformMap(autoexposure, index) {
- var uniforms;
- if (index === 0) {
- uniforms = {
- colorTexture : function() {
- return autoexposure._colorTexture;
- },
- colorTextureDimensions : function() {
- return autoexposure._colorTexture.dimensions;
- }
- };
- } else {
- var texture = autoexposure._framebuffers[index - 1].getColorTexture(0);
- uniforms = {
- colorTexture : function() {
- return texture;
- },
- colorTextureDimensions : function() {
- return texture.dimensions;
- }
- };
- }
- uniforms.minMaxLuminance = function() {
- return autoexposure._minMaxLuminance;
- };
- uniforms.previousLuminance = function() {
- return autoexposure._previousLuminance.getColorTexture(0);
- };
- return uniforms;
- }
- function getShaderSource(index, length) {
- var source =
- 'uniform sampler2D colorTexture; \n' +
- 'varying vec2 v_textureCoordinates; \n' +
- 'float sampleTexture(vec2 offset) { \n';
- if (index === 0) {
- source +=
- ' vec4 color = texture2D(colorTexture, v_textureCoordinates + offset); \n' +
- ' return czm_luminance(color.rgb); \n';
- } else {
- source +=
- ' return texture2D(colorTexture, v_textureCoordinates + offset).r; \n';
- }
- source += '}\n\n';
- source +=
- 'uniform vec2 colorTextureDimensions; \n' +
- 'uniform vec2 minMaxLuminance; \n' +
- 'uniform sampler2D previousLuminance; \n' +
- 'void main() { \n' +
- ' float color = 0.0; \n' +
- ' float xStep = 1.0 / colorTextureDimensions.x; \n' +
- ' float yStep = 1.0 / colorTextureDimensions.y; \n' +
- ' int count = 0; \n' +
- ' for (int i = 0; i < 3; ++i) { \n' +
- ' for (int j = 0; j < 3; ++j) { \n' +
- ' vec2 offset; \n' +
- ' offset.x = -xStep + float(i) * xStep; \n' +
- ' offset.y = -yStep + float(j) * yStep; \n' +
- ' if (offset.x < 0.0 || offset.x > 1.0 || offset.y < 0.0 || offset.y > 1.0) { \n' +
- ' continue; \n' +
- ' } \n' +
- ' color += sampleTexture(offset); \n' +
- ' ++count; \n' +
- ' } \n' +
- ' } \n' +
- ' if (count > 0) { \n' +
- ' color /= float(count); \n' +
- ' } \n';
- if (index === length - 1) {
- source +=
- ' float previous = texture2D(previousLuminance, vec2(0.5)).r; \n' +
- ' color = clamp(color, minMaxLuminance.x, minMaxLuminance.y); \n' +
- ' color = previous + (color - previous) / (60.0 * 1.5); \n' +
- ' color = clamp(color, minMaxLuminance.x, minMaxLuminance.y); \n';
- }
- source +=
- ' gl_FragColor = vec4(color); \n' +
- '} \n';
- return source;
- }
- function createCommands(autoexposure, context) {
- destroyCommands(autoexposure);
- var framebuffers = autoexposure._framebuffers;
- var length = framebuffers.length;
- var commands = new Array(length);
- for (var i = 0; i < length; ++i) {
- commands[i] = context.createViewportQuadCommand(getShaderSource(i, length), {
- framebuffer : framebuffers[i],
- uniformMap : createUniformMap(autoexposure, i)
- });
- }
- autoexposure._commands = commands;
- }
- /**
- * A function that will be called before execute. Used to clear any textures attached to framebuffers.
- * @param {Context} context The context.
- * @private
- */
- AutoExposure.prototype.clear = function(context) {
- var framebuffers = this._framebuffers;
- if (!defined(framebuffers)) {
- return;
- }
- var clearCommand = this._clearCommand;
- if (!defined(clearCommand)) {
- clearCommand = this._clearCommand = new ClearCommand({
- color : new Color(0.0, 0.0, 0.0, 0.0),
- framebuffer : undefined
- });
- }
- var length = framebuffers.length;
- for (var i = 0; i < length; ++i) {
- clearCommand.framebuffer = framebuffers[i];
- clearCommand.execute(context);
- }
- };
- /**
- * A function that will be called before execute. Used to create WebGL resources and load any textures.
- * @param {Context} context The context.
- * @private
- */
- AutoExposure.prototype.update = function(context) {
- var width = context.drawingBufferWidth;
- var height = context.drawingBufferHeight;
- if (width !== this._width || height !== this._height) {
- this._width = width;
- this._height = height;
- createFramebuffers(this, context);
- createCommands(this, context);
- if (!this._ready) {
- this._ready = true;
- }
- }
- this._minMaxLuminance.x = this.minimumLuminance;
- this._minMaxLuminance.y = this.maximumLuminance;
- var framebuffers = this._framebuffers;
- var temp = framebuffers[framebuffers.length - 1];
- framebuffers[framebuffers.length - 1] = this._previousLuminance;
- this._commands[this._commands.length - 1].framebuffer = this._previousLuminance;
- this._previousLuminance = temp;
- };
- /**
- * Executes the post-process stage. The color texture is the texture rendered to by the scene or from the previous stage.
- * @param {Context} context The context.
- * @param {Texture} colorTexture The input color texture.
- * @private
- */
- AutoExposure.prototype.execute = function(context, colorTexture) {
- this._colorTexture = colorTexture;
- var commands = this._commands;
- if (!defined(commands)) {
- return;
- }
- var length = commands.length;
- for (var i = 0; i < length; ++i) {
- commands[i].execute(context);
- }
- };
- /**
- * Returns true if this object was destroyed; otherwise, false.
- * <p>
- * If this object was destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
- * </p>
- *
- * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
- *
- * @see AutoExposure#destroy
- */
- AutoExposure.prototype.isDestroyed = function() {
- return false;
- };
- /**
- * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
- * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
- * <p>
- * Once an object is destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
- * assign the return value (<code>undefined</code>) to the object as done in the example.
- * </p>
- *
- * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
- *
- * @see AutoExposure#isDestroyed
- */
- AutoExposure.prototype.destroy = function() {
- destroyFramebuffers(this);
- destroyCommands(this);
- return destroyObject(this);
- };
- export default AutoExposure;
|