import buildModuleUrl from "../Core/buildModuleUrl.js"; import Color from "../Core/Color.js"; import createGuid from "../Core/createGuid.js"; import defined from "../Core/defined.js"; import Ellipsoid from "../Core/Ellipsoid.js"; import AcesTonemapping from "../Shaders/PostProcessStages/AcesTonemappingStage.js"; import AmbientOcclusionGenerate from "../Shaders/PostProcessStages/AmbientOcclusionGenerate.js"; import AmbientOcclusionModulate from "../Shaders/PostProcessStages/AmbientOcclusionModulate.js"; import BlackAndWhite from "../Shaders/PostProcessStages/BlackAndWhite.js"; import BloomComposite from "../Shaders/PostProcessStages/BloomComposite.js"; import Brightness from "../Shaders/PostProcessStages/Brightness.js"; import ContrastBias from "../Shaders/PostProcessStages/ContrastBias.js"; import DepthOfField from "../Shaders/PostProcessStages/DepthOfField.js"; import DepthView from "../Shaders/PostProcessStages/DepthView.js"; import EdgeDetection from "../Shaders/PostProcessStages/EdgeDetection.js"; import FilmicTonemapping from "../Shaders/PostProcessStages/FilmicTonemapping.js"; import FXAA from "../Shaders/PostProcessStages/FXAA.js"; import GaussianBlur1D from "../Shaders/PostProcessStages/GaussianBlur1D.js"; import LensFlare from "../Shaders/PostProcessStages/LensFlare.js"; import ModifiedReinhardTonemapping from "../Shaders/PostProcessStages/ModifiedReinhardTonemapping.js"; import NightVision from "../Shaders/PostProcessStages/NightVision.js"; import ReinhardTonemapping from "../Shaders/PostProcessStages/ReinhardTonemapping.js"; import Silhouette from "../Shaders/PostProcessStages/Silhouette.js"; import FXAA3_11 from "../ThirdParty/Shaders/FXAA3_11.js"; import AutoExposure from "./AutoExposure.js"; import PostProcessStage from "./PostProcessStage.js"; import PostProcessStageComposite from "./PostProcessStageComposite.js"; import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js"; /** * Contains functions for creating common post-process stages. * * @namespace PostProcessStageLibrary */ var PostProcessStageLibrary = {}; function createBlur(name) { var delta = 1.0; var sigma = 2.0; var stepSize = 1.0; var blurShader = "#define USE_STEP_SIZE\n" + GaussianBlur1D; var blurX = new PostProcessStage({ name: name + "_x_direction", fragmentShader: blurShader, uniforms: { delta: delta, sigma: sigma, stepSize: stepSize, direction: 0.0, }, sampleMode: PostProcessStageSampleMode.LINEAR, }); var blurY = new PostProcessStage({ name: name + "_y_direction", fragmentShader: blurShader, uniforms: { delta: delta, sigma: sigma, stepSize: stepSize, direction: 1.0, }, sampleMode: PostProcessStageSampleMode.LINEAR, }); var uniforms = {}; Object.defineProperties(uniforms, { delta: { get: function () { return blurX.uniforms.delta; }, set: function (value) { var blurXUniforms = blurX.uniforms; var blurYUniforms = blurY.uniforms; blurXUniforms.delta = blurYUniforms.delta = value; }, }, sigma: { get: function () { return blurX.uniforms.sigma; }, set: function (value) { var blurXUniforms = blurX.uniforms; var blurYUniforms = blurY.uniforms; blurXUniforms.sigma = blurYUniforms.sigma = value; }, }, stepSize: { get: function () { return blurX.uniforms.stepSize; }, set: function (value) { var blurXUniforms = blurX.uniforms; var blurYUniforms = blurY.uniforms; blurXUniforms.stepSize = blurYUniforms.stepSize = value; }, }, }); return new PostProcessStageComposite({ name: name, stages: [blurX, blurY], uniforms: uniforms, }); } /** * Creates a post-process stage that applies a Gaussian blur to the input texture. This stage is usually applied in conjunction with another stage. *

* This stage has the following uniforms: delta, sigma, and stepSize. *

*

* delta and sigma are used to compute the weights of a Gaussian filter. The equation is exp((-0.5 * delta * delta) / (sigma * sigma)). * The default value for delta is 1.0. The default value for sigma is 2.0. * stepSize is the distance to the next texel. The default is 1.0. *

* @return {PostProcessStageComposite} A post-process stage that applies a Gaussian blur to the input texture. */ PostProcessStageLibrary.createBlurStage = function () { return createBlur("czm_blur"); }; /** * Creates a post-process stage that applies a depth of field effect. *

* Depth of field simulates camera focus. Objects in the scene that are in focus * will be clear whereas objects not in focus will be blurred. *

*

* This stage has the following uniforms: focalDistance, delta, sigma, and stepSize. *

*

* focalDistance is the distance in meters from the camera to set the camera focus. *

*

* delta, sigma, and stepSize are the same properties as {@link PostProcessStageLibrary#createBlurStage}. * The blur is applied to the areas out of focus. *

* @return {PostProcessStageComposite} A post-process stage that applies a depth of field effect. */ PostProcessStageLibrary.createDepthOfFieldStage = function () { var blur = createBlur("czm_depth_of_field_blur"); var dof = new PostProcessStage({ name: "czm_depth_of_field_composite", fragmentShader: DepthOfField, uniforms: { focalDistance: 5.0, blurTexture: blur.name, }, }); var uniforms = {}; Object.defineProperties(uniforms, { focalDistance: { get: function () { return dof.uniforms.focalDistance; }, set: function (value) { dof.uniforms.focalDistance = value; }, }, delta: { get: function () { return blur.uniforms.delta; }, set: function (value) { blur.uniforms.delta = value; }, }, sigma: { get: function () { return blur.uniforms.sigma; }, set: function (value) { blur.uniforms.sigma = value; }, }, stepSize: { get: function () { return blur.uniforms.stepSize; }, set: function (value) { blur.uniforms.stepSize = value; }, }, }); return new PostProcessStageComposite({ name: "czm_depth_of_field", stages: [blur, dof], inputPreviousStageTexture: false, uniforms: uniforms, }); }; /** * Whether or not a depth of field stage is supported. *

* This stage requires the WEBGL_depth_texture extension. *

* * @param {Scene} scene The scene. * @return {Boolean} Whether this post process stage is supported. * * @see {Context#depthTexture} * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/|WEBGL_depth_texture} */ PostProcessStageLibrary.isDepthOfFieldSupported = function (scene) { return scene.context.depthTexture; }; /** * Creates a post-process stage that detects edges. *

* Writes the color to the output texture with alpha set to 1.0 when it is on an edge. *

*

* This stage has the following uniforms: color and length *

* *

* This stage is not supported in 2D. *

* @return {PostProcessStageComposite} A post-process stage that applies an edge detection effect. * * @example * // multiple silhouette effects * var yellowEdge = Cesium.PostProcessLibrary.createEdgeDetectionStage(); * yellowEdge.uniforms.color = Cesium.Color.YELLOW; * yellowEdge.selected = [feature0]; * * var greenEdge = Cesium.PostProcessLibrary.createEdgeDetectionStage(); * greenEdge.uniforms.color = Cesium.Color.LIME; * greenEdge.selected = [feature1]; * * // draw edges around feature0 and feature1 * postProcessStages.add(Cesium.PostProcessLibrary.createSilhouetteStage([yellowEdge, greenEdge]); */ PostProcessStageLibrary.createEdgeDetectionStage = function () { // unique name generated on call so more than one effect can be added var name = createGuid(); return new PostProcessStage({ name: "czm_edge_detection_" + name, fragmentShader: EdgeDetection, uniforms: { length: 0.25, color: Color.clone(Color.BLACK), }, }); }; /** * Whether or not an edge detection stage is supported. *

* This stage requires the WEBGL_depth_texture extension. *

* * @param {Scene} scene The scene. * @return {Boolean} Whether this post process stage is supported. * * @see {Context#depthTexture} * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/|WEBGL_depth_texture} */ PostProcessStageLibrary.isEdgeDetectionSupported = function (scene) { return scene.context.depthTexture; }; function getSilhouetteEdgeDetection(edgeDetectionStages) { if (!defined(edgeDetectionStages)) { return PostProcessStageLibrary.createEdgeDetectionStage(); } var edgeDetection = new PostProcessStageComposite({ name: "czm_edge_detection_multiple", stages: edgeDetectionStages, inputPreviousStageTexture: false, }); var compositeUniforms = {}; var fsDecl = ""; var fsLoop = ""; for (var i = 0; i < edgeDetectionStages.length; ++i) { fsDecl += "uniform sampler2D edgeTexture" + i + "; \n"; fsLoop += " vec4 edge" + i + " = texture2D(edgeTexture" + i + ", v_textureCoordinates); \n" + " if (edge" + i + ".a > 0.0) \n" + " { \n" + " color = edge" + i + "; \n" + " break; \n" + " } \n"; compositeUniforms["edgeTexture" + i] = edgeDetectionStages[i].name; } var fs = fsDecl + "varying vec2 v_textureCoordinates; \n" + "void main() { \n" + " vec4 color = vec4(0.0); \n" + " for (int i = 0; i < " + edgeDetectionStages.length + "; i++) \n" + " { \n" + fsLoop + " } \n" + " gl_FragColor = color; \n" + "} \n"; var edgeComposite = new PostProcessStage({ name: "czm_edge_detection_combine", fragmentShader: fs, uniforms: compositeUniforms, }); return new PostProcessStageComposite({ name: "czm_edge_detection_composite", stages: [edgeDetection, edgeComposite], }); } /** * Creates a post-process stage that applies a silhouette effect. *

* A silhouette effect composites the color from the edge detection pass with input color texture. *

*

* This stage has the following uniforms when edgeDetectionStages is undefined: color and length *

*

* color is the color of the highlighted edge. The default is {@link Color#BLACK}. * length is the length of the edges in pixels. The default is 0.5. *

* @param {PostProcessStage[]} [edgeDetectionStages] An array of edge detection post process stages. * @return {PostProcessStageComposite} A post-process stage that applies a silhouette effect. */ PostProcessStageLibrary.createSilhouetteStage = function (edgeDetectionStages) { var edgeDetection = getSilhouetteEdgeDetection(edgeDetectionStages); var silhouetteProcess = new PostProcessStage({ name: "czm_silhouette_color_edges", fragmentShader: Silhouette, uniforms: { silhouetteTexture: edgeDetection.name, }, }); return new PostProcessStageComposite({ name: "czm_silhouette", stages: [edgeDetection, silhouetteProcess], inputPreviousStageTexture: false, uniforms: edgeDetection.uniforms, }); }; /** * Whether or not a silhouette stage is supported. *

* This stage requires the WEBGL_depth_texture extension. *

* * @param {Scene} scene The scene. * @return {Boolean} Whether this post process stage is supported. * * @see {Context#depthTexture} * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/|WEBGL_depth_texture} */ PostProcessStageLibrary.isSilhouetteSupported = function (scene) { return scene.context.depthTexture; }; /** * Creates a post-process stage that applies a bloom effect to the input texture. *

* A bloom effect adds glow effect, makes bright areas brighter, and dark areas darker. *

*

* This post-process stage has the following uniforms: contrast, brightness, glowOnly, * delta, sigma, and stepSize. *

* *

* delta, sigma, and stepSize are the same properties as {@link PostProcessStageLibrary#createBlurStage}. *

* @return {PostProcessStageComposite} A post-process stage to applies a bloom effect. * * @private */ PostProcessStageLibrary.createBloomStage = function () { var contrastBias = new PostProcessStage({ name: "czm_bloom_contrast_bias", fragmentShader: ContrastBias, uniforms: { contrast: 128.0, brightness: -0.3, }, }); var blur = createBlur("czm_bloom_blur"); var generateComposite = new PostProcessStageComposite({ name: "czm_bloom_contrast_bias_blur", stages: [contrastBias, blur], }); var bloomComposite = new PostProcessStage({ name: "czm_bloom_generate_composite", fragmentShader: BloomComposite, uniforms: { glowOnly: false, bloomTexture: generateComposite.name, }, }); var uniforms = {}; Object.defineProperties(uniforms, { glowOnly: { get: function () { return bloomComposite.uniforms.glowOnly; }, set: function (value) { bloomComposite.uniforms.glowOnly = value; }, }, contrast: { get: function () { return contrastBias.uniforms.contrast; }, set: function (value) { contrastBias.uniforms.contrast = value; }, }, brightness: { get: function () { return contrastBias.uniforms.brightness; }, set: function (value) { contrastBias.uniforms.brightness = value; }, }, delta: { get: function () { return blur.uniforms.delta; }, set: function (value) { blur.uniforms.delta = value; }, }, sigma: { get: function () { return blur.uniforms.sigma; }, set: function (value) { blur.uniforms.sigma = value; }, }, stepSize: { get: function () { return blur.uniforms.stepSize; }, set: function (value) { blur.uniforms.stepSize = value; }, }, }); return new PostProcessStageComposite({ name: "czm_bloom", stages: [generateComposite, bloomComposite], inputPreviousStageTexture: false, uniforms: uniforms, }); }; /** * Creates a post-process stage that Horizon-based Ambient Occlusion (HBAO) to the input texture. *

* Ambient occlusion simulates shadows from ambient light. These shadows would always be present when the * surface receives light and regardless of the light's position. *

*

* The uniforms have the following properties: intensity, bias, lengthCap, * stepSize, frustumLength, randomTexture, ambientOcclusionOnly, * delta, sigma, and blurStepSize. *

* *

* delta, sigma, and blurStepSize are the same properties as {@link PostProcessStageLibrary#createBlurStage}. * The blur is applied to the shadows generated from the image to make them smoother. *

* @return {PostProcessStageComposite} A post-process stage that applies an ambient occlusion effect. * * @private */ PostProcessStageLibrary.createAmbientOcclusionStage = function () { var generate = new PostProcessStage({ name: "czm_ambient_occlusion_generate", fragmentShader: AmbientOcclusionGenerate, uniforms: { intensity: 3.0, bias: 0.1, lengthCap: 0.26, stepSize: 1.95, frustumLength: 1000.0, randomTexture: undefined, }, }); var blur = createBlur("czm_ambient_occlusion_blur"); blur.uniforms.stepSize = 0.86; var generateAndBlur = new PostProcessStageComposite({ name: "czm_ambient_occlusion_generate_blur", stages: [generate, blur], }); var ambientOcclusionModulate = new PostProcessStage({ name: "czm_ambient_occlusion_composite", fragmentShader: AmbientOcclusionModulate, uniforms: { ambientOcclusionOnly: false, ambientOcclusionTexture: generateAndBlur.name, }, }); var uniforms = {}; Object.defineProperties(uniforms, { intensity: { get: function () { return generate.uniforms.intensity; }, set: function (value) { generate.uniforms.intensity = value; }, }, bias: { get: function () { return generate.uniforms.bias; }, set: function (value) { generate.uniforms.bias = value; }, }, lengthCap: { get: function () { return generate.uniforms.lengthCap; }, set: function (value) { generate.uniforms.lengthCap = value; }, }, stepSize: { get: function () { return generate.uniforms.stepSize; }, set: function (value) { generate.uniforms.stepSize = value; }, }, frustumLength: { get: function () { return generate.uniforms.frustumLength; }, set: function (value) { generate.uniforms.frustumLength = value; }, }, randomTexture: { get: function () { return generate.uniforms.randomTexture; }, set: function (value) { generate.uniforms.randomTexture = value; }, }, delta: { get: function () { return blur.uniforms.delta; }, set: function (value) { blur.uniforms.delta = value; }, }, sigma: { get: function () { return blur.uniforms.sigma; }, set: function (value) { blur.uniforms.sigma = value; }, }, blurStepSize: { get: function () { return blur.uniforms.stepSize; }, set: function (value) { blur.uniforms.stepSize = value; }, }, ambientOcclusionOnly: { get: function () { return ambientOcclusionModulate.uniforms.ambientOcclusionOnly; }, set: function (value) { ambientOcclusionModulate.uniforms.ambientOcclusionOnly = value; }, }, }); return new PostProcessStageComposite({ name: "czm_ambient_occlusion", stages: [generateAndBlur, ambientOcclusionModulate], inputPreviousStageTexture: false, uniforms: uniforms, }); }; /** * Whether or not an ambient occlusion stage is supported. *

* This stage requires the WEBGL_depth_texture extension. *

* * @param {Scene} scene The scene. * @return {Boolean} Whether this post process stage is supported. * * @see {Context#depthTexture} * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/|WEBGL_depth_texture} */ PostProcessStageLibrary.isAmbientOcclusionSupported = function (scene) { return scene.context.depthTexture; }; var fxaaFS = "#define FXAA_QUALITY_PRESET 39 \n" + FXAA3_11 + "\n" + FXAA; /** * Creates a post-process stage that applies Fast Approximate Anti-aliasing (FXAA) to the input texture. * @return {PostProcessStage} A post-process stage that applies Fast Approximate Anti-aliasing to the input texture. * * @private */ PostProcessStageLibrary.createFXAAStage = function () { return new PostProcessStage({ name: "czm_FXAA", fragmentShader: fxaaFS, sampleMode: PostProcessStageSampleMode.LINEAR, }); }; /** * Creates a post-process stage that applies ACES tonemapping operator. * @param {Boolean} useAutoExposure Whether or not to use auto-exposure. * @return {PostProcessStage} A post-process stage that applies ACES tonemapping operator. * @private */ PostProcessStageLibrary.createAcesTonemappingStage = function ( useAutoExposure ) { var fs = useAutoExposure ? "#define AUTO_EXPOSURE\n" : ""; fs += AcesTonemapping; return new PostProcessStage({ name: "czm_aces", fragmentShader: fs, uniforms: { autoExposure: undefined, }, }); }; /** * Creates a post-process stage that applies filmic tonemapping operator. * @param {Boolean} useAutoExposure Whether or not to use auto-exposure. * @return {PostProcessStage} A post-process stage that applies filmic tonemapping operator. * @private */ PostProcessStageLibrary.createFilmicTonemappingStage = function ( useAutoExposure ) { var fs = useAutoExposure ? "#define AUTO_EXPOSURE\n" : ""; fs += FilmicTonemapping; return new PostProcessStage({ name: "czm_filmic", fragmentShader: fs, uniforms: { autoExposure: undefined, }, }); }; /** * Creates a post-process stage that applies Reinhard tonemapping operator. * @param {Boolean} useAutoExposure Whether or not to use auto-exposure. * @return {PostProcessStage} A post-process stage that applies Reinhard tonemapping operator. * @private */ PostProcessStageLibrary.createReinhardTonemappingStage = function ( useAutoExposure ) { var fs = useAutoExposure ? "#define AUTO_EXPOSURE\n" : ""; fs += ReinhardTonemapping; return new PostProcessStage({ name: "czm_reinhard", fragmentShader: fs, uniforms: { autoExposure: undefined, }, }); }; /** * Creates a post-process stage that applies modified Reinhard tonemapping operator. * @param {Boolean} useAutoExposure Whether or not to use auto-exposure. * @return {PostProcessStage} A post-process stage that applies modified Reinhard tonemapping operator. * @private */ PostProcessStageLibrary.createModifiedReinhardTonemappingStage = function ( useAutoExposure ) { var fs = useAutoExposure ? "#define AUTO_EXPOSURE\n" : ""; fs += ModifiedReinhardTonemapping; return new PostProcessStage({ name: "czm_modified_reinhard", fragmentShader: fs, uniforms: { white: Color.WHITE, autoExposure: undefined, }, }); }; /** * Creates a post-process stage that finds the average luminance of the input texture. * @return {PostProcessStage} A post-process stage that finds the average luminance of the input texture. * @private */ PostProcessStageLibrary.createAutoExposureStage = function () { return new AutoExposure(); }; /** * Creates a post-process stage that renders the input texture with black and white gradations. *

* This stage has one uniform value, gradations, which scales the luminance of each pixel. *

* @return {PostProcessStage} A post-process stage that renders the input texture with black and white gradations. */ PostProcessStageLibrary.createBlackAndWhiteStage = function () { return new PostProcessStage({ name: "czm_black_and_white", fragmentShader: BlackAndWhite, uniforms: { gradations: 5.0, }, }); }; /** * Creates a post-process stage that saturates the input texture. *

* This stage has one uniform value, brightness, which scales the saturation of each pixel. *

* @return {PostProcessStage} A post-process stage that saturates the input texture. */ PostProcessStageLibrary.createBrightnessStage = function () { return new PostProcessStage({ name: "czm_brightness", fragmentShader: Brightness, uniforms: { brightness: 0.5, }, }); }; /** * Creates a post-process stage that adds a night vision effect to the input texture. * @return {PostProcessStage} A post-process stage that adds a night vision effect to the input texture. */ PostProcessStageLibrary.createNightVisionStage = function () { return new PostProcessStage({ name: "czm_night_vision", fragmentShader: NightVision, }); }; /** * Creates a post-process stage that replaces the input color texture with a black and white texture representing the fragment depth at each pixel. * @return {PostProcessStage} A post-process stage that replaces the input color texture with a black and white texture representing the fragment depth at each pixel. * * @private */ PostProcessStageLibrary.createDepthViewStage = function () { return new PostProcessStage({ name: "czm_depth_view", fragmentShader: DepthView, }); }; /** * Creates a post-process stage that applies an effect simulating light flaring a camera lens. *

* This stage has the following uniforms: dirtTexture, starTexture, intensity, distortion, ghostDispersal, * haloWidth, dirtAmount, and earthRadius. *

*

* @return {PostProcessStage} A post-process stage for applying a lens flare effect. */ PostProcessStageLibrary.createLensFlareStage = function () { return new PostProcessStage({ name: "czm_lens_flare", fragmentShader: LensFlare, uniforms: { dirtTexture: buildModuleUrl("Assets/Textures/LensFlare/DirtMask.jpg"), starTexture: buildModuleUrl("Assets/Textures/LensFlare/StarBurst.jpg"), intensity: 2.0, distortion: 10.0, ghostDispersal: 0.4, haloWidth: 0.4, dirtAmount: 0.4, earthRadius: Ellipsoid.WGS84.maximumRadius, }, }); }; export default PostProcessStageLibrary;