Sébastien Vandenberghe 9 rokov pred
rodič
commit
10a6e9dc45

+ 1 - 0
Tools/Gulp/config.json

@@ -212,6 +212,7 @@
       "../../src/tools/hdr/babylon.tools.pmremGenerator.js",
       "../../src/materials/textures/babylon.hdrcubetexture.js",
       "../../src/debug/babylon.skeletonViewer.js",
+      "../../src/materials/textures/babylon.colorGradingTexture.js",
       "../../src/materials/babylon.pbrmaterial.js"
     ]
   }

+ 169 - 0
src/Materials/Textures/babylon.colorGradingTexture.js

@@ -0,0 +1,169 @@
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * This represents a color grading texture. This acts as a lookup table LUT, useful during post process
+     * It can help converting any input color in a desired output one. This can then be used to create effects
+     * from sepia, black and white to sixties or futuristic rendering...
+     *
+     * The only supported format is currently 3dl.
+     * More information on LUT: https://en.wikipedia.org/wiki/3D_lookup_table/
+     */
+    var ColorGradingTexture = (function (_super) {
+        __extends(ColorGradingTexture, _super);
+        /**
+         * Instantiates a ColorGradingTexture from the following parameters.
+         *
+         * @param url The location of the color gradind data (currently only supporting 3dl)
+         * @param scene The scene the texture will be used in
+         */
+        function ColorGradingTexture(url, scene) {
+            _super.call(this, scene);
+            if (!url) {
+                return;
+            }
+            this._textureMatrix = BABYLON.Matrix.Identity();
+            this.name = url;
+            this.url = url;
+            this.hasAlpha = false;
+            this.isCube = false;
+            this._texture = this._getFromCache(url, true);
+            if (!this._texture) {
+                if (!scene.useDelayedTextureLoading) {
+                    this.loadTexture();
+                }
+                else {
+                    this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NOTLOADED;
+                }
+            }
+        }
+        /**
+         * Returns the texture matrix used in most of the material.
+         * This is not used in color grading but keep for troubleshooting purpose (easily swap diffuse by colorgrading to look in).
+         */
+        ColorGradingTexture.prototype.getTextureMatrix = function () {
+            return this._textureMatrix;
+        };
+        /**
+         * Occurs when the file being loaded is a .3dl LUT file.
+         */
+        ColorGradingTexture.prototype.load3dlTexture = function () {
+            var _this = this;
+            var mipLevels = 0;
+            var floatArrayView = null;
+            var texture = this.getScene().getEngine().createRawTexture(null, 1, 1, BABYLON.Engine.TEXTUREFORMAT_RGB, false, false, BABYLON.Texture.NEAREST_SAMPLINGMODE);
+            this._texture = texture;
+            var callback = function (text) {
+                var buffer;
+                var data;
+                var line;
+                var lines = text.split('\n');
+                var size = 0, pixelIndex = 0;
+                for (var i = 0; i < lines.length; i++) {
+                    line = lines[i];
+                    if (!ColorGradingTexture._noneEmptyLineRegex.test(line))
+                        continue;
+                    if (line.indexOf('#') === 0)
+                        continue;
+                    var words = line.split(" ");
+                    if (size === 0) {
+                        // Number of space + one
+                        size = words.length;
+                        buffer = new ArrayBuffer(size * size * size * 3); // volume texture of side size and rgb 8
+                        data = new Uint8Array(buffer);
+                        continue;
+                    }
+                    if (size != 0) {
+                        var r = parseInt(words[0]);
+                        var g = parseInt(words[1]);
+                        var b = parseInt(words[2]);
+                        r = Math.round(Math.max(Math.min(255, r), 0));
+                        g = Math.round(Math.max(Math.min(255, g), 0));
+                        b = Math.round(Math.max(Math.min(255, b), 0));
+                        // Transpose ordering from RGB to BGR (naming here might also be transposed).
+                        var indexR = pixelIndex % size;
+                        var indexG = (pixelIndex / size) % size;
+                        var indexB = (pixelIndex / size) / size;
+                        var pixelStorageIndex = indexR * (size * size * 3) + indexG * size * 3 + indexB * 3;
+                        data[pixelStorageIndex + 0] = r; //255;//r;
+                        data[pixelStorageIndex + 1] = g; //0;//g;
+                        data[pixelStorageIndex + 2] = b; //255;//b;
+                        pixelIndex++;
+                    }
+                }
+                _this.getScene().getEngine().updateRawTexture(texture, data, BABYLON.Engine.TEXTUREFORMAT_RGB, false);
+                _this.getScene().getEngine().updateTextureSize(texture, size * size, size);
+            };
+            BABYLON.Tools.LoadFile(this.url, callback);
+            return this._texture;
+        };
+        /**
+         * Starts the loading process of the texture.
+         */
+        ColorGradingTexture.prototype.loadTexture = function () {
+            if (this.url && this.url.toLocaleLowerCase().indexOf(".3dl") == (this.url.length - 4)) {
+                this.load3dlTexture();
+            }
+        };
+        /**
+         * Clones the color gradind texture.
+         */
+        ColorGradingTexture.prototype.clone = function () {
+            var newTexture = new ColorGradingTexture(this.url, this.getScene());
+            // Base texture
+            newTexture.level = this.level;
+            return newTexture;
+        };
+        /**
+         * Called during delayed load for textures.
+         */
+        ColorGradingTexture.prototype.delayLoad = function () {
+            if (this.delayLoadState !== BABYLON.Engine.DELAYLOADSTATE_NOTLOADED) {
+                return;
+            }
+            this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_LOADED;
+            this._texture = this._getFromCache(this.url, true);
+            if (!this._texture) {
+                this.loadTexture();
+            }
+        };
+        /**
+         * Parses a color grading texture serialized by Babylon.
+         * @param parsedTexture The texture information being parsedTexture
+         * @param scene The scene to load the texture in
+         * @param rootUrl The root url of the data assets to load
+         * @return A color gradind texture
+         */
+        ColorGradingTexture.Parse = function (parsedTexture, scene, rootUrl) {
+            var texture = null;
+            if (parsedTexture.name && !parsedTexture.isRenderTarget) {
+                texture = new BABYLON.ColorGradingTexture(parsedTexture.name, scene);
+                texture.name = parsedTexture.name;
+                texture.level = parsedTexture.level;
+            }
+            return texture;
+        };
+        /**
+         * Serializes the LUT texture to json format.
+         */
+        ColorGradingTexture.prototype.serialize = function () {
+            if (!this.name) {
+                return null;
+            }
+            var serializationObject = {};
+            serializationObject.name = this.name;
+            serializationObject.level = this.level;
+            return serializationObject;
+        };
+        /**
+         * Empty line regex stored for GC.
+         */
+        ColorGradingTexture._noneEmptyLineRegex = /\S+/;
+        return ColorGradingTexture;
+    }(BABYLON.BaseTexture));
+    BABYLON.ColorGradingTexture = ColorGradingTexture;
+})(BABYLON || (BABYLON = {}));

+ 208 - 0
src/Materials/Textures/babylon.colorGradingTexture.ts

@@ -0,0 +1,208 @@
+module BABYLON {
+
+    /**
+     * This represents a color grading texture. This acts as a lookup table LUT, useful during post process
+     * It can help converting any input color in a desired output one. This can then be used to create effects
+     * from sepia, black and white to sixties or futuristic rendering...
+     * 
+     * The only supported format is currently 3dl.
+     * More information on LUT: https://en.wikipedia.org/wiki/3D_lookup_table/
+     */
+    export class ColorGradingTexture extends BaseTexture {
+
+        /**
+         * The current internal texture size.
+         */        
+        private _size: number;
+        
+        /**
+         * The current texture matrix. (will always be identity in color grading texture)
+         */
+        private _textureMatrix: Matrix;
+        
+        /**
+         * The texture URL.
+         */
+        public url: string;
+
+        /**
+         * Empty line regex stored for GC.
+         */
+        private static _noneEmptyLineRegex = /\S+/;
+        
+        /**
+         * Instantiates a ColorGradingTexture from the following parameters.
+         * 
+         * @param url The location of the color gradind data (currently only supporting 3dl)
+         * @param scene The scene the texture will be used in
+         */
+        constructor(url: string, scene: Scene) {
+            super(scene);
+
+            if (!url) {
+                return;
+            }
+
+            this._textureMatrix = Matrix.Identity();
+            this.name = url;
+            this.url = url;
+            this.hasAlpha = false;
+            this.isCube = false;
+            
+            this._texture = this._getFromCache(url, true);
+
+            if (!this._texture) {
+                if (!scene.useDelayedTextureLoading) {
+                    this.loadTexture();
+                } else {
+                    this.delayLoadState = Engine.DELAYLOADSTATE_NOTLOADED;
+                }
+            }
+        }
+
+        /**
+         * Returns the texture matrix used in most of the material.
+         * This is not used in color grading but keep for troubleshooting purpose (easily swap diffuse by colorgrading to look in).
+         */
+        public getTextureMatrix(): Matrix {
+            return this._textureMatrix;
+        }
+        
+        /**
+         * Occurs when the file being loaded is a .3dl LUT file.
+         */
+        private load3dlTexture() {
+
+            var mipLevels = 0;
+            var floatArrayView: Float32Array = null;
+            var texture = this.getScene().getEngine().createRawTexture(null, 1, 1, BABYLON.Engine.TEXTUREFORMAT_RGB, false, false, Texture.NEAREST_SAMPLINGMODE);
+            this._texture = texture;
+            
+            var callback = (text: string) => {
+                var buffer: ArrayBuffer;
+                var data: Uint8Array;
+                
+                var line: string;
+                var lines = text.split('\n');
+                var size = 0, pixelIndex = 0;
+                
+                for (let i = 0; i < lines.length; i++) {
+                    line = lines[i];
+                    
+                    if (!ColorGradingTexture._noneEmptyLineRegex.test(line))
+                        continue;
+                        
+                    if (line.indexOf('#') === 0)
+                        continue;
+                    
+                    var words = line.split(" ");
+                    if (size === 0) {
+                        // Number of space + one
+                        size = words.length;
+                        buffer = new ArrayBuffer(size * size * size * 3); // volume texture of side size and rgb 8
+                        data = new Uint8Array(buffer);
+                        continue;
+                    }
+                    
+                    if (size != 0)
+                    {
+                        var r = parseInt(words[0]);
+                        var g = parseInt(words[1]);
+                        var b = parseInt(words[2]);
+                        
+                        r = Math.round(Math.max(Math.min(255, r), 0));
+                        g = Math.round(Math.max(Math.min(255, g), 0));
+                        b = Math.round(Math.max(Math.min(255, b), 0));
+
+                        // Transpose ordering from RGB to BGR (naming here might also be transposed).
+                        var indexR = pixelIndex % size;
+                        var indexG = (pixelIndex / size) % size;
+                        var indexB = (pixelIndex / size) / size;
+
+                        var pixelStorageIndex = indexR * (size * size * 3) + indexG * size * 3 + indexB * 3;
+                        data[pixelStorageIndex + 0] = r;//255;//r;
+                        data[pixelStorageIndex + 1] = g;//0;//g;
+                        data[pixelStorageIndex + 2] = b;//255;//b;
+
+                        pixelIndex++;
+                    }
+                }
+                
+                this.getScene().getEngine().updateRawTexture(texture, data, BABYLON.Engine.TEXTUREFORMAT_RGB, false);
+                this.getScene().getEngine().updateTextureSize(texture, size * size, size);
+            }
+
+            Tools.LoadFile(this.url, callback);
+            return this._texture;
+        }
+
+        /**
+         * Starts the loading process of the texture.
+         */
+        private loadTexture() {
+            if (this.url && this.url.toLocaleLowerCase().indexOf(".3dl") == (this.url.length - 4)) {
+                this.load3dlTexture();
+            }
+        }
+
+        /**
+         * Clones the color gradind texture.
+         */
+        public clone(): ColorGradingTexture {
+            var newTexture = new ColorGradingTexture(this.url, this.getScene());
+
+            // Base texture
+            newTexture.level = this.level;
+
+            return newTexture;
+        }
+
+        /**
+         * Called during delayed load for textures.
+         */
+        public delayLoad(): void {
+            if (this.delayLoadState !== Engine.DELAYLOADSTATE_NOTLOADED) {
+                return;
+            }
+
+            this.delayLoadState = Engine.DELAYLOADSTATE_LOADED;
+            this._texture = this._getFromCache(this.url, true);
+
+            if (!this._texture) {
+                this.loadTexture();
+            }
+        }
+
+        /**
+         * Parses a color grading texture serialized by Babylon.
+         * @param parsedTexture The texture information being parsedTexture
+         * @param scene The scene to load the texture in
+         * @param rootUrl The root url of the data assets to load
+         * @return A color gradind texture
+         */
+        public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): ColorGradingTexture {
+            var texture = null;
+            if (parsedTexture.name && !parsedTexture.isRenderTarget) {
+                texture = new BABYLON.ColorGradingTexture(parsedTexture.name, scene);
+                texture.name = parsedTexture.name;
+                texture.level = parsedTexture.level;
+            }
+            return texture;
+        }
+        
+        /**
+         * Serializes the LUT texture to json format.
+         */
+        public serialize(): any {
+            if (!this.name) {
+                return null;
+            }
+
+            var serializationObject: any = {};
+            serializationObject.name = this.name;
+            serializationObject.level = this.level;
+
+            return serializationObject;
+        }
+    }
+}

+ 32 - 1
src/Materials/babylon.pbrMaterial.ts

@@ -81,6 +81,7 @@
         public LOGARITHMICDEPTH = false;
         public CAMERATONEMAP = false;
         public CAMERACONTRAST = false;
+        public CAMERACOLORGRADING = false;
         public OVERLOADEDVALUES = false;
         public OVERLOADEDSHADOWVALUES = false;
         public USESPHERICALFROMREFLECTIONMAP = false;
@@ -175,6 +176,13 @@
          */
         @serialize()
         public cameraContrast: number = 1.0;
+        
+        /**
+         * Color Grading 2D Lookup Texture.
+         * This allows special effects like sepia, black and white to sixties rendering style. 
+         */
+        @serializeAsTexture()
+        public cameraColorGradingTexture: BaseTexture = null;
 
         private _cameraInfos: Vector4 = new Vector4(1.0, 1.0, 0.0, 0.0);
 
@@ -766,6 +774,14 @@
                         }
                     }
                 }
+            
+                if (this.cameraColorGradingTexture) {
+                    if (!this.cameraColorGradingTexture.isReady()) {
+                        return false;
+                    } else {
+                        this._defines.CAMERACOLORGRADING = true;
+                    }
+                }
             }
 
             // Effect
@@ -1009,7 +1025,8 @@
                         "vMicrosurfaceTextureLods", "vLightRadiuses"
                     ],
                     ["albedoSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "reflectivitySampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
-                        "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
+                        "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3",
+                        "cameraColorGrading2DSampler"
                     ],
                     join, fallbacks, this.onCompiled, this.onError);
             }
@@ -1188,6 +1205,12 @@
                     if ((this.reflectionTexture || this.refractionTexture)) {
                         this._effect.setFloat2("vMicrosurfaceTextureLods", this._microsurfaceTextureLods.x, this._microsurfaceTextureLods.y);
                     }
+                    
+                    if (this.cameraColorGradingTexture) {
+                        this._effect.setTexture("cameraColorGrading2DSampler", this.cameraColorGradingTexture);
+                        this._cameraInfos.z = this.cameraColorGradingTexture.level;
+                        this._cameraInfos.w = this.cameraColorGradingTexture.getSize().height;
+                    }
                 }
 
                 // Clip plane
@@ -1318,6 +1341,10 @@
             if (this.refractionTexture && this.refractionTexture.animations && this.refractionTexture.animations.length > 0) {
                 results.push(this.refractionTexture);
             }
+            
+            if (this.cameraColorGradingTexture && this.cameraColorGradingTexture.animations && this.cameraColorGradingTexture.animations.length > 0) {
+                results.push(this.cameraColorGradingTexture);
+            }
 
             return results;
         }
@@ -1359,6 +1386,10 @@
                 if (this.refractionTexture) {
                     this.refractionTexture.dispose();
                 }
+                
+                if (this.cameraColorGradingTexture) {
+                    this.cameraColorGradingTexture.dispose();
+                }
             }
 
             super.dispose(forceDisposeEffect, forceDisposeTextures);

+ 22 - 0
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -194,4 +194,26 @@ float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float ra
 
         return color;
     }
+#endif
+
+#ifdef CAMERACOLORGRADING
+    vec4 colorGrades(vec4 color, sampler2D lut, float level, float lutSize)
+    {
+        color = clamp(color, 0.0, 1.0);
+        vec2 uv = color.rg;
+        
+        float blueIndex = color.b / lutSize;
+        float fl = floor(blueIndex);
+        float fr = fract(blueIndex);
+        
+        uv.x += fl;
+        vec3 colorTransformInputLowerBound = texture2D(lut, uv).rgb;
+        uv.x += (1.0 / lutSize);
+        vec3 colorTransformInputUpperBound = texture2D(lut, uv).rgb;
+        
+        vec3 result = mix(colorTransformInputLowerBound, colorTransformInputUpperBound, fr);
+        
+        color.rgb = mix(color.rgb, result, level);
+        return color;
+    }
 #endif

+ 27 - 19
src/Shaders/pbr.fragment.fx

@@ -128,30 +128,34 @@ uniform vec4 emissiveRightColor;
 
 // Reflection
 #ifdef REFLECTION
-uniform vec2 vReflectionInfos;
+    uniform vec2 vReflectionInfos;
 
-#ifdef REFLECTIONMAP_3D
-uniform samplerCube reflectionCubeSampler;
-#else
-uniform sampler2D reflection2DSampler;
-#endif
-
-#ifdef REFLECTIONMAP_SKYBOX
-varying vec3 vPositionUVW;
-#else
-    #ifdef REFLECTIONMAP_EQUIRECTANGULAR_FIXED
-    varying vec3 vDirectionW;
+    #ifdef REFLECTIONMAP_3D
+        uniform samplerCube reflectionCubeSampler;
+    #else
+        uniform sampler2D reflection2DSampler;
     #endif
 
-    #if defined(REFLECTIONMAP_PLANAR) || defined(REFLECTIONMAP_CUBIC) || defined(REFLECTIONMAP_PROJECTION)
-    uniform mat4 reflectionMatrix;
+    #ifdef REFLECTIONMAP_SKYBOX
+        varying vec3 vPositionUVW;
+    #else
+        #ifdef REFLECTIONMAP_EQUIRECTANGULAR_FIXED
+            varying vec3 vDirectionW;
+        #endif
+
+        #if defined(REFLECTIONMAP_PLANAR) || defined(REFLECTIONMAP_CUBIC) || defined(REFLECTIONMAP_PROJECTION)
+            uniform mat4 reflectionMatrix;
+        #endif
     #endif
-#endif
 
-#include<reflectionFunction>
+    #include<reflectionFunction>
 
 #endif
 
+#ifdef CAMERACOLORGRADING
+    uniform sampler2D cameraColorGrading2DSampler;
+#endif
+
 // PBR
 #include<pbrShadowFunctions>
 #include<pbrFunctions>
@@ -590,10 +594,17 @@ vec3 surfaceEmissiveColor = vEmissiveColor;
 
     finalColor.rgb = toGammaSpace(finalColor.rgb);
 
+#include<logDepthFragment>
+#include<fogFragment>(color, finalColor)
+
 #ifdef CAMERACONTRAST
     finalColor = contrasts(finalColor);
 #endif
 
+#ifdef CAMERACOLORGRADING
+    finalColor = colorGrades(finalColor, cameraColorGrading2DSampler, vCameraInfos.z, vCameraInfos.w);
+#endif
+
     // Normal Display.
     // gl_FragColor = vec4(normalW * 0.5 + 0.5, 1.0);
 
@@ -622,8 +633,5 @@ vec3 surfaceEmissiveColor = vEmissiveColor;
     //vec2 test = vEmissiveUV * 0.5 + 0.5;
     //gl_FragColor = vec4(test.x, test.y, 1.0, 1.0);
 
-#include<logDepthFragment>
-#include<fogFragment>(color, finalColor)
-
     gl_FragColor = finalColor;
 }