Browse Source

First stab at adding ramp color. Associated with #5064

David Catuhe 7 years ago
parent
commit
b0bd7de41e

File diff suppressed because it is too large
+ 9936 - 9767
Playground/babylon.d.txt


File diff suppressed because it is too large
+ 9927 - 9758
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 387 - 54
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 387 - 54
dist/preview release/babylon.no-module.max.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 387 - 54
dist/preview release/es6.js


+ 1 - 1
dist/preview release/gui/babylon.gui.d.ts

@@ -1,6 +1,6 @@
 /*BabylonJS GUI*/
 // Dependencies for this module:
-//   ../../../../tools/Gulp/babylonjs
+//   ../../../../Tools/Gulp/babylonjs
 declare module BABYLON.GUI {
 }
 declare module BABYLON.GUI {

+ 2 - 2
dist/preview release/gui/babylon.gui.module.d.ts

@@ -1,6 +1,6 @@
 /*BabylonJS GUI*/
 // Dependencies for this module:
-//   ../../../../tools/Gulp/babylonjs
+//   ../../../../Tools/Gulp/babylonjs
 
 declare module 'babylonjs-gui' {
     export * from "babylonjs-gui/2D";
@@ -2806,7 +2806,7 @@ declare module 'babylonjs-gui/3D/materials/fluentMaterial' {
 
 /*BabylonJS GUI*/
 // Dependencies for this module:
-//   ../../../../tools/Gulp/babylonjs
+//   ../../../../Tools/Gulp/babylonjs
 declare module BABYLON.GUI {
 }
 declare module BABYLON.GUI {

+ 1 - 1
dist/preview release/inspector/babylon.inspector.d.ts

@@ -1,6 +1,6 @@
 /*BabylonJS Inspector*/
 // Dependencies for this module:
-//   ../../../../tools/Gulp/babylonjs
+//   ../../../../Tools/Gulp/babylonjs
 declare module INSPECTOR {
 }
 declare module INSPECTOR {

+ 2 - 2
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -1,6 +1,6 @@
 /*BabylonJS Inspector*/
 // Dependencies for this module:
-//   ../../../../tools/Gulp/babylonjs
+//   ../../../../Tools/Gulp/babylonjs
 
 declare module 'babylonjs-inspector' {
     export * from 'babylonjs-inspector/adapters';
@@ -1340,7 +1340,7 @@ declare module 'babylonjs-inspector/treetools/SoundInteractions' {
 
 /*BabylonJS Inspector*/
 // Dependencies for this module:
-//   ../../../../tools/Gulp/babylonjs
+//   ../../../../Tools/Gulp/babylonjs
 declare module INSPECTOR {
 }
 declare module INSPECTOR {

+ 3 - 17
dist/preview release/viewer/babylon.viewer.d.ts

@@ -4,8 +4,8 @@
 declare module "babylonjs-loaders"{ export=BABYLON;}
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
-//   ../../../../../tools/Gulp/babylonjs
-//   ../../../../../tools/Gulp/babylonjs-loaders
+//   ../../../../../Tools/Gulp/babylonjs
+//   ../../../../../Tools/Gulp/babylonjs-loaders
 declare module BabylonViewer {
     /**
         * BabylonJS Viewer
@@ -924,7 +924,7 @@ declare module BabylonViewer {
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
@@ -1558,20 +1558,6 @@ declare module BabylonViewer {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 declare module BabylonViewer {
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-declare module BabylonViewer {
 }
 declare module BabylonViewer {
     export interface IEnvironmentMapConfiguration {

File diff suppressed because it is too large
+ 2 - 2
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 12 - 12
dist/preview release/viewer/babylon.viewer.max.js


+ 3 - 20
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -5,8 +5,8 @@ declare module "babylonjs-loaders"{ export=BABYLON;}
 
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
-//   ../../../../../tools/Gulp/babylonjs
-//   ../../../../../tools/Gulp/babylonjs-loaders
+//   ../../../../../Tools/Gulp/babylonjs
+//   ../../../../../Tools/Gulp/babylonjs-loaders
 
 declare module 'babylonjs-viewer' {
     import { mapperManager } from 'babylonjs-viewer/configuration/mappers';
@@ -985,14 +985,13 @@ declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
 }
 
 declare module 'babylonjs-viewer/optimizer/custom' {
-    import { extendedUpgrade } from "babylonjs-viewer/optimizer/custom/extended";
     import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
     /**
       *
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 
@@ -1663,22 +1662,6 @@ declare module 'babylonjs-viewer/loader/plugins' {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 
-declare module 'babylonjs-viewer/optimizer/custom/extended' {
-    import { SceneManager } from 'babylonjs-viewer/managers/sceneManager';
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-
 declare module 'babylonjs-viewer/configuration/interfaces' {
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';

+ 50 - 0
src/Particles/babylon.IParticleSystem.ts

@@ -422,7 +422,57 @@ module BABYLON {
          * @returns the list of start size gradients
          */
         getStartSizeGradients(): Nullable<Array<FactorGradient>>;  
+
+        /**
+         * Gets the current list of color gradients.
+         * You must use addColorGradient and removeColorGradient to udpate this list
+         * @returns the list of color gradients
+         */
+        getColorGradients(): Nullable<Array<ColorGradient>>;      
+        
+        /**
+         * Adds a new ramp gradient used to remap particle colors
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param color defines the color to affect to the specified gradient
+         * @returns the current particle system
+         */
+        addRampGradient(gradient: number, color: Color4): IParticleSystem;     
+        /**
+         * Gets the current list of ramp gradients.
+         * You must use addRampGradient and removeRampGradient to udpate this list
+         * @returns the list of ramp gradients
+         */
+        getRampGradients(): Nullable<Array<ColorGradient>>;             
         
+        /**
+         * Adds a new color remap gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param min defines the color remap minimal range        
+         * @param max defines the color remap maximal range        
+         * @returns the current particle system
+         */
+        addColorRemapGradient(gradient: number, min: number, max: number): IParticleSystem;    
+        /**
+         * Gets the current list of color remap gradients.
+         * You must use addColorRemapGradient and removeColorRemapGradient to udpate this list
+         * @returns the list of color remap gradients
+         */
+        getColorRemapGradients(): Nullable<Array<FactorGradient>>;
+        
+        /**
+         * Adds a new alpha remap gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param min defines the alpha remap minimal range        
+         * @param max defines the alpha remap maximal range        
+         * @returns the current particle system
+         */
+        addAlphaRemapGradient(gradient: number, min: number, max: number): IParticleSystem; 
+        /**
+         * Gets the current list of alpha remap gradients.
+         * You must use addAlphaRemapGradient and removeAlphaRemapGradient to udpate this list
+         * @returns the list of alpha remap gradients
+         */
+        getAlphaRemapGradients(): Nullable<Array<FactorGradient>>;            
 
         /**
          * Creates a Point Emitter for the particle system (emits directly from the emitter position)

+ 44 - 1
src/Particles/babylon.baseParticleSystem.ts

@@ -218,7 +218,7 @@ module BABYLON {
 
         /** @hidden */
         protected _isAnimationSheetEnabled: boolean;
-        
+       
         /**
          * Gets or sets whether an animation sprite sheet is enabled or not on the particle system
          */
@@ -258,6 +258,9 @@ module BABYLON {
         protected _dragGradients: Nullable<Array<FactorGradient>> = null;
         protected _emitRateGradients: Nullable<Array<FactorGradient>> = null;
         protected _startSizeGradients: Nullable<Array<FactorGradient>> = null;
+        protected _rampGradients: Nullable<Array<ColorGradient>> = null;
+        protected _colorRemapGradients: Nullable<Array<FactorGradient>> = null;
+        protected _alphaRemapGradients: Nullable<Array<FactorGradient>> = null;        
 
         /**
          * Gets the current list of drag gradients.
@@ -299,6 +302,24 @@ module BABYLON {
         }        
 
         /**
+         * Gets the current list of color remap gradients.
+         * You must use addColorRemapGradient and removeColorRemapGradient to udpate this list
+         * @returns the list of color remap gradients
+         */
+        public getColorRemapGradients(): Nullable<Array<FactorGradient>> {
+            return this._colorRemapGradients;
+        }   
+
+        /**
+         * Gets the current list of alpha remap gradients.
+         * You must use addAlphaRemapGradient and removeAlphaRemapGradient to udpate this list
+         * @returns the list of alpha remap gradients
+         */
+        public getAlphaRemapGradients(): Nullable<Array<FactorGradient>> {
+            return this._alphaRemapGradients;
+        }   
+
+        /**
          * Gets the current list of life time gradients.
          * You must use addLifeTimeGradient and removeLifeTimeGradient to udpate this list
          * @returns the list of life time gradients
@@ -515,6 +536,28 @@ module BABYLON {
         protected _reset() {
         }
 
+        /** @hidden */
+        protected _removeGradientAndTexture(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): BaseParticleSystem {
+            if (!gradients) {
+                return this;
+            }
+
+            let index = 0;
+            for (var valueGradient of gradients) {
+                if (valueGradient.gradient === gradient) {
+                    gradients.splice(index, 1);
+                    break;
+                }
+                index++;
+            }
+
+            if (texture) {
+                texture.dispose();
+            }            
+
+            return this;
+        } 
+
         /**
          * Instantiates a particle system.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.

+ 90 - 26
src/Particles/babylon.gpuParticleSystem.ts

@@ -157,29 +157,13 @@
         }            
                
         private _colorGradientsTexture: RawTexture;
-
-        private _removeGradient(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): GPUParticleSystem {
-            if (!gradients) {
-                return this;
-            }
-
-            let index = 0;
-            for (var valueGradient of gradients) {
-                if (valueGradient.gradient === gradient) {
-                    gradients.splice(index, 1);
-                    break;
-                }
-                index++;
-            }
-
-            if (texture) {
-                texture.dispose();
-            }            
-
+        
+        protected _removeGradientAndTexture(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): BaseParticleSystem {
+            super._removeGradientAndTexture(gradient, gradients, texture);
             this._releaseBuffers();
 
             return this;
-        }    
+        }
         
         /**
          * Adds a new color gradient
@@ -224,7 +208,7 @@
          * @returns the current particle system
          */
         public removeColorGradient(gradient: number): GPUParticleSystem {
-            this._removeGradient(gradient, this._colorGradients, this._colorGradientsTexture);
+            this._removeGradientAndTexture(gradient, this._colorGradients, this._colorGradientsTexture);
             (<any>this._colorGradientsTexture) = null;
 
             return this;
@@ -284,7 +268,7 @@
          * @returns the current particle system
          */
         public removeSizeGradient(gradient: number): GPUParticleSystem {
-            this._removeGradient(gradient, this._sizeGradients, this._sizeGradientsTexture);
+            this._removeGradientAndTexture(gradient, this._sizeGradients, this._sizeGradientsTexture);
             (<any>this._sizeGradientsTexture) = null;
 
             return this;            
@@ -319,7 +303,7 @@
          * @returns the current particle system
          */
         public removeAngularSpeedGradient(gradient: number): GPUParticleSystem {
-            this._removeGradient(gradient, this._angularSpeedGradients, this._angularSpeedGradientsTexture);
+            this._removeGradientAndTexture(gradient, this._angularSpeedGradients, this._angularSpeedGradientsTexture);
             (<any>this._angularSpeedGradientsTexture) = null;
 
             return this;           
@@ -354,7 +338,7 @@
          * @returns the current particle system
          */
         public removeVelocityGradient(gradient: number): GPUParticleSystem {
-            this._removeGradient(gradient, this._velocityGradients, this._velocityGradientsTexture);
+            this._removeGradientAndTexture(gradient, this._velocityGradients, this._velocityGradientsTexture);
             (<any>this._velocityGradientsTexture) = null;
 
             return this;           
@@ -389,7 +373,7 @@
          * @returns the current particle system
          */
         public removeLimitVelocityGradient(gradient: number): GPUParticleSystem {
-            this._removeGradient(gradient, this._limitVelocityGradients, this._limitVelocityGradientsTexture);
+            this._removeGradientAndTexture(gradient, this._limitVelocityGradients, this._limitVelocityGradientsTexture);
             (<any>this._limitVelocityGradientsTexture) = null;
 
             return this;
@@ -424,7 +408,7 @@
          * @returns the current particle system
          */
         public removeDragGradient(gradient: number): GPUParticleSystem {
-            this._removeGradient(gradient, this._dragGradients, this._dragGradientsTexture);
+            this._removeGradientAndTexture(gradient, this._dragGradients, this._dragGradientsTexture);
             (<any>this._dragGradientsTexture) = null;
 
             return this;
@@ -475,6 +459,86 @@
         } 
 
         /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param min defines the color remap minimal range        
+         * @param max defines the color remap maximal range        
+         * @returns the current particle system
+         */
+        public addColorRemapGradient(gradient: number, min: number, max: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+
+            return this;
+        }
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeColorRemapGradient(gradient: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+
+            return this;
+        }  
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param min defines the alpha remap minimal range        
+         * @param max defines the alpha remap maximal range        
+         * @returns the current particle system
+         */
+        public addAlphaRemapGradient(gradient: number, min: number, max: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+
+            return this;
+        }
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAlphaRemapGradient(gradient: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+
+            return this;
+        }   
+        
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param color defines the color to affect to the specified gradient
+         * @returns the current particle system
+         */
+        public addRampGradient(gradient: number, color: Color4): IParticleSystem {
+            //Not supported by GPUParticleSystem          
+
+            return this;
+        }
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeRampGradient(gradient: number): IParticleSystem {
+            //Not supported by GPUParticleSystem
+
+            return this;
+        }  
+        
+        /**
+         * Not supported by GPUParticleSystem
+         * @returns the list of ramp gradients
+         */
+        public getRampGradients(): Nullable<Array<ColorGradient>> {
+            return null;
+        }             
+        
+
+        /**
          * Instantiates a GPU particle system.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
          * @param name The name of the particle system

+ 9 - 0
src/Particles/babylon.particle.ts

@@ -60,6 +60,11 @@
          */
         public cellIndex: number = 0;  
 
+        /**
+         * The information required to support color remapping
+         */
+        public remapData: Vector4;
+
         /** @hidden */
         public _randomCellOffset?: number;
 
@@ -111,6 +116,7 @@
         public _currentDrag1 = 0;
         /** @hidden */
         public _currentDrag2 = 0;  
+     
 
         /**
          * Creates a new instance Particle
@@ -225,6 +231,9 @@
                 other._initialStartSpriteCellID = this._initialStartSpriteCellID;
                 other._initialEndSpriteCellID = this._initialEndSpriteCellID;
             }
+            if (this.particleSystem.useRampGradients) {
+                other.remapData.copyFrom(this.remapData);
+            }
         }
     }
 } 

+ 296 - 13
src/Particles/babylon.particleSystem.ts

@@ -84,7 +84,26 @@
         /** @hidden */
         public _currentStartSize2 = 0;   
 
-        // end of sheet animation
+        private readonly _rawTextureWidth = 256;
+        private _rampGradientsTexture: RawTexture;    
+        private _useRampGradients = false;    
+
+        /** Gets or sets a boolean indicating that ramp gradients must be used
+         * @see http://doc.babylonjs.com/babylon101/particles#ramp-gradients
+         */
+        public get useRampGradients(): boolean {
+            return this._useRampGradients;
+        }
+
+        public set useRampGradients(value: boolean) {
+            if (this._useRampGradients === value) {
+                return;
+            }
+
+            this._useRampGradients = value;
+
+            this._resetEffect();
+        }
 
         // Sub-emitters
         /**
@@ -296,6 +315,29 @@
                         if (this._isAnimationSheetEnabled) {
                             particle.updateCellIndex();
                         }
+
+                        // Remap data
+                        if (this._useRampGradients) {
+                            if (this._colorRemapGradients && this._colorRemapGradients.length > 0) {                  
+                                Tools.GetCurrentGradient(ratio, this._colorRemapGradients, (currentGradient, nextGradient, scale) => {                                                                 
+                                    let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
+                                    let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
+
+                                    particle.remapData.x = min;
+                                    particle.remapData.y = max - min;
+                                });
+                            }
+
+                            if (this._alphaRemapGradients && this._alphaRemapGradients.length > 0) {                  
+                                Tools.GetCurrentGradient(ratio, this._alphaRemapGradients, (currentGradient, nextGradient, scale) => {
+                                    let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
+                                    let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
+
+                                    particle.remapData.z = min;
+                                    particle.remapData.w = max - min;
+                                });
+                            }
+                        }
                     }
                 }
             }
@@ -389,7 +431,63 @@
             this._removeFactorGradient(this._sizeGradients, gradient);
 
             return this;
-        }        
+        }    
+        
+        /**
+         * Adds a new color remap gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param min defines the color remap minimal range        
+         * @param max defines the color remap maximal range        
+         * @returns the current particle system
+         */
+        public addColorRemapGradient(gradient: number, min: number, max: number): IParticleSystem {
+            if (!this._colorRemapGradients) {
+                this._colorRemapGradients = [];
+            }
+
+            this._addFactorGradient(this._colorRemapGradients, gradient, min, max);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific color remap gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeColorRemapGradient(gradient: number): IParticleSystem {
+            this._removeFactorGradient(this._colorRemapGradients, gradient);
+
+            return this;
+        }  
+
+        /**
+         * Adds a new alpha remap gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param min defines the alpha remap minimal range        
+         * @param max defines the alpha remap maximal range        
+         * @returns the current particle system
+         */
+        public addAlphaRemapGradient(gradient: number, min: number, max: number): IParticleSystem {
+            if (!this._alphaRemapGradients) {
+                this._alphaRemapGradients = [];
+            }
+
+            this._addFactorGradient(this._alphaRemapGradients, gradient, min, max);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific alpha remap gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAlphaRemapGradient(gradient: number): IParticleSystem {
+            this._removeFactorGradient(this._alphaRemapGradients, gradient);
+
+            return this;
+        }          
 
         /**
          * Adds a new angular speed gradient
@@ -579,6 +677,92 @@
             return this;
         } 
 
+        private _createRampGradientTexture() {
+            if (!this._rampGradients || !this._rampGradients.length || this._rampGradientsTexture) {
+                return;
+            }
+
+            let data = new Uint8Array(this._rawTextureWidth * 4);
+            let tmpColor = Tmp.Color4[0];
+
+            for (var x = 0; x < this._rawTextureWidth; x++) {
+                var ratio = x / this._rawTextureWidth;
+
+                Tools.GetCurrentGradient(ratio, this._rampGradients, (currentGradient, nextGradient, scale) => {
+
+                    Color4.LerpToRef((<ColorGradient>currentGradient).color1, (<ColorGradient>nextGradient).color1, scale, tmpColor);
+                    data[x * 4] = tmpColor.r * 255;
+                    data[x * 4 + 1] = tmpColor.g * 255;
+                    data[x * 4 + 2] = tmpColor.b * 255;
+                    data[x * 4 + 3] = tmpColor.a * 255;
+                });
+
+            }
+
+            this._rampGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        }
+
+        /**
+         * Gets the current list of ramp gradients.
+         * You must use addRampGradient and removeRampGradient to udpate this list
+         * @returns the list of ramp gradients
+         */
+        public getRampGradients(): Nullable<Array<ColorGradient>> {
+            return this._rampGradients;
+        }        
+
+        /**
+         * Adds a new ramp gradient used to remap particle colors
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param color defines the color to affect to the specified gradient
+         * @returns the current particle system
+         */
+        public addRampGradient(gradient: number, color: Color4): ParticleSystem {
+            if (!this._rampGradients) {
+                this._rampGradients = [];
+            }
+
+            let rampGradient = new ColorGradient();
+            rampGradient.gradient = gradient;
+            rampGradient.color1 = color;
+            this._rampGradients.push(rampGradient);
+
+            this._rampGradients.sort((a, b) => {
+                if (a.gradient < b.gradient) {
+                    return -1;
+                } else if (a.gradient > b.gradient) {
+                    return 1;
+                }
+
+                return 0;
+            });
+
+            if (this._rampGradientsTexture) {
+                this._rampGradientsTexture.dispose();
+                (<any>this._rampGradientsTexture) = null;
+            }
+
+            this._createRampGradientTexture();            
+
+            return this;
+        }
+
+        /**
+         * Remove a specific ramp gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeRampGradient(gradient: number): ParticleSystem {
+            this._removeGradientAndTexture(gradient, this._rampGradients, this._rampGradientsTexture);
+            (<any>this._rampGradientsTexture) = null;
+
+            if (this._rampGradients && this._rampGradients.length > 0) {
+                this._createRampGradientTexture();   
+            }
+
+            return this;
+        }            
+
         /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
@@ -712,7 +896,13 @@
                 offsets = this._vertexBuffer.createVertexBuffer("offset", dataOffset, 2, this._vertexBufferSize, this._useInstancing);
                 dataOffset += 2;
             }
-            this._vertexBuffers["offset"] = offsets;              
+            this._vertexBuffers["offset"] = offsets;         
+            
+            if (this._useRampGradients) {
+                var rampDataBuffer = this._vertexBuffer.createVertexBuffer("rampData", dataOffset, 4, this._vertexBufferSize, this._useInstancing);
+                this._vertexBuffers["rampData"] = rampDataBuffer;
+                dataOffset += 4;
+            }
         }
 
         private _createIndexBuffer() {
@@ -856,6 +1046,13 @@
                 this._vertexData[offset++] = offsetX;
                 this._vertexData[offset++] = offsetY;   
             }
+
+            if (this._useRampGradients) {
+                this._vertexData[offset++] = particle.remapData.x;
+                this._vertexData[offset++] = particle.remapData.y;
+                this._vertexData[offset++] = particle.remapData.z;
+                this._vertexData[offset++] = particle.remapData.w;                
+            }
         }
 
         // start of sub system methods
@@ -1097,12 +1294,17 @@
                     particle._initialStartSpriteCellID = this.startSpriteCellID;
                     particle._initialEndSpriteCellID = this.endSpriteCellID;
                 }
+
+                // Remap
+                if (this._useRampGradients) {
+                    particle.remapData = new Vector4(0, 1, 0, 1);
+                }
             }
         }
 
         /** @hidden */
         public static _GetAttributeNamesOrOptions(isAnimationSheetEnabled = false, isBillboardBased = false): string[] {
-            var attributeNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "angle", "offset", "size"];
+            var attributeNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "angle", "offset", "size", "remapData"];
 
             if (isAnimationSheetEnabled) {
                 attributeNamesOrOptions.push("cellIndex");
@@ -1156,6 +1358,10 @@
                 defines.push("#define BLENDMULTIPLYMODE");
             }
 
+            if (this._useRampGradients) {
+                defines.push("#define RAMPGRADIENT");
+            }
+
             if (this._isBillboardBased) {
                 defines.push("#define BILLBOARD");
 
@@ -1182,7 +1388,7 @@
                 var attributesNamesOrOptions = ParticleSystem._GetAttributeNamesOrOptions(this._isAnimationSheetEnabled, this._isBillboardBased);
                 var effectCreationOption = ParticleSystem._GetEffectCreationOptions(this._isAnimationSheetEnabled);
 
-                var samplers = ["diffuseSampler"];
+                var samplers = ["diffuseSampler", "rampSampler"];
 
                 if (ImageProcessingConfiguration) {
                     ImageProcessingConfiguration.PrepareUniforms(effectCreationOption, this._imageProcessingConfigurationDefines);
@@ -1193,7 +1399,7 @@
                     "particles",
                     attributesNamesOrOptions,
                     effectCreationOption,
-                    ["diffuseSampler"], join);
+                    samplers, join);
             }
 
             return this._effect;
@@ -1367,6 +1573,10 @@
                 effect.setVector3("eyePosition", camera.globalPosition);
             }
 
+            if (this._rampGradientsTexture) {
+                effect.setTexture("rampSampler", this._rampGradientsTexture);
+            }
+
             if (this._scene.clipPlane || this._scene.clipPlane2 || this._scene.clipPlane3 || this._scene.clipPlane4) {
                 var invView = viewMatrix.clone();
                 invView.invert();
@@ -1590,6 +1800,55 @@
                     serializationObject.colorGradients.push(serializedGradient);
                 }
             }
+            
+            let rampGradients = particleSystem.getRampGradients();
+            if (rampGradients) {
+                serializationObject.rampGradients = [];
+                for (var rampGradient of rampGradients) {
+                    var serializedGradient: any = {
+                        gradient: rampGradient.gradient,
+                        color1: rampGradient.color1.asArray()
+                    };
+
+                    serializationObject.rampGradients.push(serializedGradient);
+                }
+            }
+
+            let colorRemapGradients = particleSystem.getColorRemapGradients();
+            if (colorRemapGradients) {
+                serializationObject.colorRemapGradients = [];
+                for (var colorRemapGradient of colorRemapGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: colorRemapGradient.gradient,
+                        factor1: colorRemapGradient.factor1
+                    };
+
+                    if (colorRemapGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = colorRemapGradient.factor2;
+                    }
+
+                    serializationObject.colorRemapGradients.push(serializedGradient);
+                }
+            }        
+            
+            let alphaRemapGradients = particleSystem.getAlphaRemapGradients();
+            if (alphaRemapGradients) {
+                serializationObject.alphaRemapGradients = [];
+                for (var alphaRemapGradient of alphaRemapGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: alphaRemapGradient.gradient,
+                        factor1: alphaRemapGradient.factor1
+                    };
+
+                    if (alphaRemapGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = alphaRemapGradient.factor2;
+                    }
+
+                    serializationObject.alphaRemapGradients.push(serializedGradient);
+                }
+            }             
 
             let sizeGradients = particleSystem.getSizeGradients();
             if (sizeGradients) {
@@ -1810,45 +2069,69 @@
                 }
             }
 
+            if (parsedParticleSystem.rampGradients) {
+                for (var rampGradient of parsedParticleSystem.rampGradients) {
+                    particleSystem.addRampGradient(rampGradient.gradient, Color4.FromArray(rampGradient.color1));
+                }
+            }
+
+            if (parsedParticleSystem.colorRemapGradients) {
+                for (var colorRemapGradient of parsedParticleSystem.colorRemapGradients) {
+                    particleSystem.addColorRemapGradient(colorRemapGradient.gradient, colorRemapGradient.factor1 !== undefined ? colorRemapGradient.factor1 : colorRemapGradient.factor, colorRemapGradient.factor2);
+                }
+            } 
+
+            if (parsedParticleSystem.alphaRemapGradients) {
+                for (var alphaRemapGradient of parsedParticleSystem.alphaRemapGradients) {
+                    particleSystem.addAlphaRemapGradient(alphaRemapGradient.gradient, alphaRemapGradient.factor1 !== undefined ? alphaRemapGradient.factor1 : alphaRemapGradient.factor, alphaRemapGradient.factor2);
+                }
+            } 
+
+            if (parsedParticleSystem.sizeGradients) {
+                for (var sizeGradient of parsedParticleSystem.sizeGradients) {
+                    particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ? sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
+                }
+            } 
+
             if (parsedParticleSystem.sizeGradients) {
                 for (var sizeGradient of parsedParticleSystem.sizeGradients) {
-                    particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ?  sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
+                    particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ? sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
                 }
             }       
 
             if (parsedParticleSystem.angularSpeedGradients) {
                 for (var angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
-                    particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ?  angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
+                    particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ? angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
                 }
             }       
             
             if (parsedParticleSystem.velocityGradients) {
                 for (var velocityGradient of parsedParticleSystem.velocityGradients) {
-                    particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ?  velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
+                    particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ? velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
                 }
             }     
 
             if (parsedParticleSystem.dragGradients) {
                 for (var dragGradient of parsedParticleSystem.dragGradients) {
-                    particleSystem.addDragGradient(dragGradient.gradient, dragGradient.factor1 !== undefined ?  dragGradient.factor1 : dragGradient.factor, dragGradient.factor2);
+                    particleSystem.addDragGradient(dragGradient.gradient, dragGradient.factor1 !== undefined ? dragGradient.factor1 : dragGradient.factor, dragGradient.factor2);
                 }
             }        
             
             if (parsedParticleSystem.emitRateGradients) {
                 for (var emitRateGradient of parsedParticleSystem.emitRateGradients) {
-                    particleSystem.addEmitRateGradient(emitRateGradient.gradient, emitRateGradient.factor1 !== undefined ?  emitRateGradient.factor1 : emitRateGradient.factor, emitRateGradient.factor2);
+                    particleSystem.addEmitRateGradient(emitRateGradient.gradient, emitRateGradient.factor1 !== undefined ? emitRateGradient.factor1 : emitRateGradient.factor, emitRateGradient.factor2);
                 }
             } 
             
             if (parsedParticleSystem.startSizeGradients) {
                 for (var startSizeGradient of parsedParticleSystem.startSizeGradients) {
-                    particleSystem.addStartSizeGradient(startSizeGradient.gradient, startSizeGradient.factor1 !== undefined ?  startSizeGradient.factor1 : startSizeGradient.factor, startSizeGradient.factor2);
+                    particleSystem.addStartSizeGradient(startSizeGradient.gradient, startSizeGradient.factor1 !== undefined ? startSizeGradient.factor1 : startSizeGradient.factor, startSizeGradient.factor2);
                 }
             }
 
             if (parsedParticleSystem.limitVelocityGradients) {
                 for (var limitVelocityGradient of parsedParticleSystem.limitVelocityGradients) {
-                    particleSystem.addLimitVelocityGradient(limitVelocityGradient.gradient, limitVelocityGradient.factor1 !== undefined ?  limitVelocityGradient.factor1 : limitVelocityGradient.factor, limitVelocityGradient.factor2);
+                    particleSystem.addLimitVelocityGradient(limitVelocityGradient.gradient, limitVelocityGradient.factor1 !== undefined ? limitVelocityGradient.factor1 : limitVelocityGradient.factor, limitVelocityGradient.factor2);
                 }
                 particleSystem.limitVelocityDamping = parsedParticleSystem.limitVelocityDamping;
             }               

+ 17 - 2
src/Shaders/particles.fragment.fx

@@ -12,15 +12,30 @@ uniform sampler2D diffuseSampler;
 
 #include<imageProcessingFunctions>
 
+#ifdef RAMPGRADIENT
+varying vec4 remapRanges;
+uniform sampler2D rampSampler;
+#endif
+
 void main(void) {
 	#include<clipPlaneFragment>
 
 	vec4 textureColor = texture2D(diffuseSampler, vUV);
 	vec4 baseColor = (textureColor * textureMask + (vec4(1., 1., 1., 1.) - textureMask)) * vColor;
 
+	#ifdef RAMPGRADIENT
+		float alpha = textureColor.a;
+		float remappedColorIndex = clamp((alpha - remapRanges.x) / remapRanges.y, 0.0, 1.0);
+
+		baseColor.rgb *= texture2D(rampSampler, vec2(remappedColorIndex, 0.)).rgb;
+
+		// Remapped alpha
+		baseColor.a = clamp((alpha - remapRanges.z) / remapRanges.w, 0.0, 1.0);
+	#endif
+
 	#ifdef BLENDMULTIPLYMODE
-	float alpha = vColor.a * textureColor.a;
-	baseColor.rgb = baseColor.rgb * alpha + vec3(1.0) * (1.0 - alpha);
+		float sourceAlpha = vColor.a * textureColor.a;
+		baseColor.rgb = baseColor.rgb * sourceAlpha + vec3(1.0) * (1.0 - sourceAlpha);
 	#endif
 
 // Apply image processing if relevant. As this applies in linear space, 

+ 11 - 0
src/Shaders/particles.vertex.fx

@@ -10,6 +10,9 @@ attribute float cellIndex;
 attribute vec3 direction;
 #endif
 attribute vec2 offset;
+#ifdef RAMPGRADIENT
+attribute vec4 remapData;
+#endif
 
 // Uniforms
 uniform mat4 view;
@@ -24,6 +27,10 @@ uniform vec3 particlesInfos; // x (number of rows) y(number of columns) z(rowSiz
 varying vec2 vUV;
 varying vec4 vColor;
 
+#ifdef RAMPGRADIENT
+varying vec4 remapRanges;
+#endif
+
 #if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4)
 uniform mat4 invView;
 #endif
@@ -75,6 +82,10 @@ void main(void) {
 	vec3 viewPos = (view * vec4(position, 1.0)).xyz + rotatedCorner; 
 #endif
 
+#ifdef RAMPGRADIENT
+	remapRanges = remapData;
+#endif
+
 	// Position
 	gl_Position = projection * vec4(viewPos, 1.0);   
 #else