ソースを参照

Merge pull request #2238 from sebavan/Development

Add Simple PBR Materials
David Catuhe 8 年 前
コミット
86380bdac8

+ 1 - 1
Exporters/Unity 5/EditorToolkit/Assets/Babylon/Source/WebServer.cs

@@ -145,7 +145,7 @@ namespace BabylonHosting
             }
             catch
             {
-                // Note: Thread Abort Excpetions Caught Here
+                // Note: Thread Abort Exceptions Caught Here
             }
         }
 

+ 7 - 3
Tools/Gulp/config.json

@@ -151,7 +151,7 @@
             "dependUpon" : [
                 "core"
             ]
-        },                             
+        },
         "additionalMeshes" : 
         {
             "files": [
@@ -246,7 +246,11 @@
         "pbrMaterial" : 
         {
             "files": [
-                "../../src/Materials/babylon.pbrMaterial.js"
+                "../../src/Materials/PBR/babylon.pbrBaseMaterial.js",
+                "../../src/Materials/PBR/babylon.pbrBaseSimpleMaterial.js",
+                "../../src/Materials/PBR/babylon.pbrMaterial.js",
+                "../../src/Materials/PBR/babylon.pbrMetallicRoughnessMaterial.js",
+                "../../src/Materials/PBR/babylon.pbrSpecularGlossinessMaterial.js"
             ],
             "dependUpon" : [
                 "core"
@@ -279,7 +283,7 @@
                 "pbrFragmentDeclaration",
                 "pbrUboDeclaration",
                 "fresnelFunction",
-                "reflectionFunction",                
+                "reflectionFunction",
                 "colorGradingDefinition",
                 "colorCurvesDefinition",
                 "shadowsFragmentFunctions",

+ 13 - 1
materialsLibrary/index.html

@@ -49,6 +49,8 @@
 	<script src="test/addsky.js"></script>
 	<script src="test/addgrid.js"></script>
 	<script src="test/addpbr.js"></script>
+	<script src="test/addpbrmetallicroughness.js"></script>
+	<script src="test/addpbrspecularglossiness.js"></script>
 	<script src="test/addlegacypbr.js"></script>
 	<script src="test/addCell.js"></script>
 	
@@ -197,6 +199,10 @@
 
 				var pbr = preparePBR();
 
+				var pbrmetallicroughness = preparePBRMetallicRoughness();
+
+				var pbrspecularglossiness = preparePBRSpecularGlossiness();
+
 				var legacypbr = prepareLegacyPBR();
 				
 				var triPlanar = prepareTriPlanar();
@@ -214,7 +220,7 @@
 				sphere.material = std;				
 				sphere.receiveShadows = true;
 
-				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'legacyPbr', 'fur', 'triPlanar', 'gradient', 'sky', 'grid', 'shadowOnly', 'cell']).onFinishChange(function () {
+				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'pbrmetallicroughness', 'pbrspecularglossiness', 'legacyPbr', 'fur', 'triPlanar', 'gradient', 'sky', 'grid', 'shadowOnly', 'cell']).onFinishChange(function () {
 					water.enableRenderTargets(false);
 					skybox.material = skyboxMaterial;
 					currentMesh.isVisible = true;
@@ -247,6 +253,12 @@
 						case "pbr":
 							currentMaterial = pbr;
 							break;
+						case "pbrmetallicroughness":
+							currentMaterial = pbrmetallicroughness;
+							break;
+						case "pbrspecularglossiness":
+							currentMaterial = pbrspecularglossiness;
+							break;
 						case "legacyPbr":
 							currentMaterial = legacypbr;
 							break;

+ 83 - 0
materialsLibrary/test/addpbrmetallicroughness.js

@@ -0,0 +1,83 @@
+window.preparePBRMetallicRoughness = function() {
+	var pbr = new BABYLON.PBRMetallicRoughnessMaterial("pbrmetallicroughness", scene);
+
+	pbr.baseTexture = new BABYLON.Texture("../assets/textures/amiga.jpg", scene);
+	pbr.baseTexture.uScale = 5;
+	pbr.baseTexture.vScale = 5;
+	pbr.metallic = 0.5;
+	pbr.roughness = 0.1;
+
+    // Uncomment for PMREM Generation
+    var hdrTexture = new BABYLON.HDRCubeTexture("../assets/textures/hdr/environment.hdr", scene, 512);
+    // var hdrTexture = new BABYLON.HDRCubeTexture("textures/hdr/environment.hdr", scene, 128, false, true, false, true);
+
+    // Skybox
+    var hdrSkybox = scene.createDefaultSkybox(hdrTexture, true);
+    hdrSkybox.setEnabled(false);
+
+    registerRangeUI("pbrmetallicroughness", "alpha", 0, 1, function(value) {
+		pbr.alpha = value;
+	}, function() {
+		return pbr.alpha;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "metallic", 0, 1, function(value) {
+		pbr.metallic = value;
+	}, function() {
+		return pbr.metallic;
+	});
+	
+	registerRangeUI("pbrmetallicroughness", "roughness", 0, 1, function(value) {
+		pbr.roughness = value;
+	}, function() {
+		return pbr.roughness;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "baseColorR", 0, 1, function(value) {
+		pbr.baseColor.r = value;
+	}, function() {
+		return pbr.baseColor.r;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "baseColorG", 0, 1, function(value) {
+		pbr.baseColor.g = value;
+	}, function() {
+		return pbr.baseColor.g;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "baseColorB", 0, 1, function(value) {
+		pbr.baseColor.b = value;
+	}, function() {
+		return pbr.baseColor.b;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "emissiveColorR", 0, 1, function(value) {
+		pbr.emissiveColor.r = value;
+	}, function() {
+		return pbr.emissiveColor.r;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "emissiveColorG", 0, 1, function(value) {
+		pbr.emissiveColor.g = value;
+	}, function() {
+		return pbr.emissiveColor.g;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "emissiveColorB", 0, 1, function(value) {
+		pbr.emissiveColor.b = value;
+	}, function() {
+		return pbr.emissiveColor.b;
+	});
+
+	registerRangeUI("pbrmetallicroughness", "baseTextureLevel", 0, 1, function(value) {
+		pbr.baseTexture.level = value;
+	}, function() {
+		return pbr.baseTexture.level;
+	});
+    
+    registerButtonUI("pbrmetallicroughness", "Toggle Skybox", function() {
+        hdrSkybox.setEnabled(!hdrSkybox.isEnabled());
+	});
+
+	return pbr;
+}

+ 95 - 0
materialsLibrary/test/addpbrspecularglossiness.js

@@ -0,0 +1,95 @@
+window.preparePBRSpecularGlossiness = function() {
+	var pbr = new BABYLON.PBRSpecularGlossinessMaterial("pbrspecularglossiness", scene);
+
+	pbr.diffuseTexture = new BABYLON.Texture("../assets/textures/amiga.jpg", scene);
+	pbr.diffuseTexture.uScale = 5;
+	pbr.diffuseTexture.vScale = 5;
+	pbr.specularColor = new BABYLON.Color3(0.3, 0.3, 0.3);
+	pbr.glossiness = 0.9;
+
+    // Uncomment for PMREM Generation
+    var hdrTexture = new BABYLON.HDRCubeTexture("../assets/textures/hdr/environment.hdr", scene, 512);
+    // var hdrTexture = new BABYLON.HDRCubeTexture("textures/hdr/environment.hdr", scene, 128, false, true, false, true);
+
+    // Skybox
+    var hdrSkybox = scene.createDefaultSkybox(hdrTexture, true);
+    hdrSkybox.setEnabled(false);
+    
+    registerRangeUI("pbrspecularglossiness", "alpha", 0, 1, function(value) {
+		pbr.alpha = value;
+	}, function() {
+		return pbr.alpha;
+	});
+	
+	registerRangeUI("pbrspecularglossiness", "glossiness", 0, 1, function(value) {
+		pbr.glossiness = value;
+	}, function() {
+		return pbr.glossiness;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "diffuseColorR", 0, 1, function(value) {
+		pbr.diffuseColor.r = value;
+	}, function() {
+		return pbr.diffuseColor.r;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "diffuseColorG", 0, 1, function(value) {
+		pbr.diffuseColor.g = value;
+	}, function() {
+		return pbr.diffuseColor.g;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "diffuseColorB", 0, 1, function(value) {
+		pbr.diffuseColor.b = value;
+	}, function() {
+		return pbr.diffuseColor.b;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "specularColorR", 0, 1, function(value) {
+		pbr.specularColor.r = value;
+	}, function() {
+		return pbr.specularColor.r;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "specularColorG", 0, 1, function(value) {
+		pbr.specularColor.g = value;
+	}, function() {
+		return pbr.specularColor.g;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "specularColorB", 0, 1, function(value) {
+		pbr.specularColor.b = value;
+	}, function() {
+		return pbr.specularColor.b;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "emissiveColorR", 0, 1, function(value) {
+		pbr.emissiveColor.r = value;
+	}, function() {
+		return pbr.emissiveColor.r;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "emissiveColorG", 0, 1, function(value) {
+		pbr.emissiveColor.g = value;
+	}, function() {
+		return pbr.emissiveColor.g;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "emissiveColorB", 0, 1, function(value) {
+		pbr.emissiveColor.b = value;
+	}, function() {
+		return pbr.emissiveColor.b;
+	});
+
+	registerRangeUI("pbrspecularglossiness", "diffuseTextureLevel", 0, 1, function(value) {
+		pbr.diffuseTexture.level = value;
+	}, function() {
+		return pbr.diffuseTexture.level;
+	});
+    
+    registerButtonUI("pbrspecularglossiness", "Toggle Skybox", function() {
+        hdrSkybox.setEnabled(!hdrSkybox.isEnabled());
+	});
+
+	return pbr;
+}

ファイルの差分が大きいため隠しています
+ 331 - 390
src/Materials/babylon.pbrMaterial.ts


+ 170 - 0
src/Materials/PBR/babylon.pbrBaseSimpleMaterial.ts

@@ -0,0 +1,170 @@
+module BABYLON.Internals {
+    /**
+     * The Physically based simple base material of BJS.
+     * 
+     * This enables better naming and convention enforcements on top of the pbrMaterial.
+     * It is used as the base class for both the specGloss and metalRough conventions.
+     */
+    export abstract class PBRBaseSimpleMaterial extends PBRBaseMaterial {
+
+        /**
+         * Number of Simultaneous lights allowed on the material.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public maxSimultaneousLights = 4;
+
+        /**
+         * If sets to true, disables all the lights affecting the material.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public disableLighting = false;
+
+        /**
+         * Environment Texture used in the material (this is use for both reflection and environment lighting).
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_reflectionTexture")
+        public environmentTexture: BaseTexture;
+
+        /**
+         * If sets to true, x component of normal map value will invert (x = 1.0 - x).
+         */
+        @serialize()
+        @expandToProperty(null)
+        public invertNormalMapX = false;
+
+        /**
+         * If sets to true, y component of normal map value will invert (y = 1.0 - y).
+         */
+        @serialize()
+        @expandToProperty(null)
+        public invertNormalMapY = false;
+
+        /**
+         * Normal map used in the model.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_bumpTexture")
+        public normalTexture: BaseTexture;
+
+        /**
+         * Emissivie color used to self-illuminate the model.
+         */
+        @serializeAsColor3("emissive")
+        @expandToProperty(null)
+        public emissiveColor = new Color3(0, 0, 0);
+
+        /**
+         * Emissivie texture used to self-illuminate the model.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public emissiveTexture: BaseTexture;
+
+        /**
+         * Occlusion Channel Strenght.
+         */
+        @serialize()
+        @expandToProperty(null, "_ambientTextureStrength")
+        public occlusionStrength: number = 1.0;
+
+        /**
+         * Occlusion Texture of the material (adding extra occlusion effects).
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_ambientTexture")
+        public occlusionTexture: BaseTexture;
+
+        /**
+         * Defines the alpha limits in alpha test mode.
+         */
+        @serialize()
+        @expandToProperty(null, "_alphaCutOff")
+        public alphaCutOff: number;
+
+        protected _transparencyMode: number = PBRMaterial.PBRMATERIAL_OPAQUE;
+        /**
+         * Gets the current transparency mode.
+         */
+        @serialize()
+        public get transparencyMode(): number {
+            return this._transparencyMode;
+        }
+        /**
+         * Sets the transparency mode of the material.
+         */
+        public set transparencyMode(value: number) {
+            this._transparencyMode = value;
+            if (value === PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND) {
+                this._forceAlphaTest = true;
+            }
+            else {
+                this._forceAlphaTest = false;
+            }
+        }
+
+        /**
+         * Gets the current double sided mode.
+         */
+        @serialize()
+        public get doubleSided(): boolean {
+            return this._twoSidedLighting;
+        }
+        /**
+         * If sets to true and backfaceCulling is false, normals will be flipped on the backside.
+         */
+        public set doubleSided(value: boolean) {
+            this._twoSidedLighting = value;
+            this.backFaceCulling = !value;
+        }
+
+        /**
+         * Specifies wether or not the alpha value of the albedo texture should be used.
+         */
+        protected _shouldUseAlphaFromAlbedoTexture(): boolean {
+            return this._albedoTexture && this._albedoTexture.hasAlpha && this._transparencyMode !== PBRMaterial.PBRMATERIAL_OPAQUE;
+        }
+
+        /**
+         * Specifies wether or not the meshes using this material should be rendered in alpha blend mode.
+         */
+        public needAlphaBlending(): boolean {
+            if (this._linkRefractionWithTransparency) {
+                return false;
+            }
+
+            return (this.alpha < 1.0) || 
+                    (this._shouldUseAlphaFromAlbedoTexture() &&
+                        (this._transparencyMode === PBRMaterial.PBRMATERIAL_ALPHABLEND ||
+                            this._transparencyMode === PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND));
+        }
+
+        /**
+         * Specifies wether or not the meshes using this material should be rendered in alpha test mode.
+         */
+        public needAlphaTesting(): boolean {
+            if (this._linkRefractionWithTransparency) {
+                return false;
+            }
+
+            return this._shouldUseAlphaFromAlbedoTexture() &&
+                 this._transparencyMode === PBRMaterial.PBRMATERIAL_ALPHATEST;
+        }
+
+        /**
+         * Instantiates a new PBRMaterial instance.
+         * 
+         * @param name The material name
+         * @param scene The scene the material will be use in.
+         */
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+
+            this._useEmissiveAsIllumination = true;
+            this._useAmbientInGrayScale = true;
+            this._useScalarInLinearSpace = true;
+        }
+    }
+}

+ 450 - 0
src/Materials/PBR/babylon.pbrMaterial.ts

@@ -0,0 +1,450 @@
+module BABYLON {
+    /**
+     * The Physically based material of BJS.
+     * 
+     * This offers the main features of a standard PBR material.
+     * For more information, please refer to the documentation : 
+     * http://doc.babylonjs.com/extensions/Physically_Based_Rendering
+     */
+    export class PBRMaterial extends PBRBaseMaterial {
+        private static _PBRMATERIAL_OPAQUE = 0;
+        /**
+         * PBRMaterialTransparencyMode: No transparency mode, Alpha channel is not use.
+         */
+        public static get PBRMATERIAL_OPAQUE(): number {
+            return this._PBRMATERIAL_OPAQUE;
+        }
+
+        private static _PBRMATERIAL_ALPHATEST = 1;
+        /**
+         * PBRMaterialTransparencyMode: Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value.
+         */
+        public static get PBRMATERIAL_ALPHATEST(): number {
+            return this._PBRMATERIAL_ALPHATEST;
+        }
+
+        private static _PBRMATERIAL_ALPHABLEND = 2;
+        /**
+         * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
+         */
+        public static get PBRMATERIAL_ALPHABLEND(): number {
+            return this._PBRMATERIAL_ALPHABLEND;
+        }
+
+        private static _PBRMATERIAL_ALPHATESTANDBLEND = 3;
+        /**
+         * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
+         * They are also discarded below the alpha cutoff threshold to improve performances.
+         */
+        public static get PBRMATERIAL_ALPHATESTANDBLEND(): number {
+            return this._PBRMATERIAL_ALPHATESTANDBLEND;
+        }
+
+        /**
+         * Intensity of the direct lights e.g. the four lights available in your scene.
+         * This impacts both the direct diffuse and specular highlights.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public directIntensity: number = 1.0;
+        
+        /**
+         * Intensity of the emissive part of the material.
+         * This helps controlling the emissive effect without modifying the emissive color.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public emissiveIntensity: number = 1.0;
+        
+        /**
+         * Intensity of the environment e.g. how much the environment will light the object
+         * either through harmonics for rough material or through the refelction for shiny ones.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public environmentIntensity: number = 1.0;
+        
+        /**
+         * This is a special control allowing the reduction of the specular highlights coming from the 
+         * four lights of the scene. Those highlights may not be needed in full environment lighting.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public specularIntensity: number = 1.0;
+
+        /**
+         * Debug Control allowing disabling the bump map on this material.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public disableBumpMap: boolean = false;
+
+        /**
+         * The camera exposure used on this material.
+         * This property is here and not in the camera to allow controlling exposure without full screen post process.
+         * This corresponds to a photographic exposure.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public cameraExposure: number = 1.0;
+        
+        /**
+         * The camera contrast used on this material.
+         * This property is here and not in the camera to allow controlling contrast without full screen post process.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public cameraContrast: number = 1.0;
+        
+        /**
+         * Color Grading 2D Lookup Texture.
+         * This allows special effects like sepia, black and white to sixties rendering style. 
+         */
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public cameraColorGradingTexture: BaseTexture = null;
+        
+        /**
+         * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT). 
+         * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects.
+         * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; 
+         * corresponding to low luminance, medium luminance, and high luminance areas respectively.
+         */
+        @serializeAsColorCurves()
+        @expandToProperty(null)
+        public cameraColorCurves: ColorCurves = null;
+
+        /**
+         * AKA Diffuse Texture in standard nomenclature.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public albedoTexture: BaseTexture;
+        
+        /**
+         * AKA Occlusion Texture in other nomenclature.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public ambientTexture: BaseTexture;
+
+        /**
+         * AKA Occlusion Texture Intensity in other nomenclature.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public ambientTextureStrength: number = 1.0;
+
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public opacityTexture: BaseTexture;
+
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public reflectionTexture: BaseTexture;
+
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public emissiveTexture: BaseTexture;
+        
+        /**
+         * AKA Specular texture in other nomenclature.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public reflectivityTexture: BaseTexture;
+
+        /**
+         * Used to switch from specular/glossiness to metallic/roughness workflow.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public metallicTexture: BaseTexture;
+
+        /**
+         * Specifies the metallic scalar of the metallic/roughness workflow.
+         * Can also be used to scale the metalness values of the metallic texture.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public metallic: number;
+
+        /**
+         * Specifies the roughness scalar of the metallic/roughness workflow.
+         * Can also be used to scale the roughness values of the metallic texture.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public roughness: number;
+
+        /**
+         * Used to enable roughness/glossiness fetch from a separate chanel depending on the current mode.
+         * Gray Scale represents roughness in metallic mode and glossiness in specular mode.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public microSurfaceTexture: BaseTexture;
+
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public bumpTexture: BaseTexture;
+
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public lightmapTexture: BaseTexture;
+
+        @serializeAsTexture()
+        @expandToProperty(null)
+        public refractionTexture: BaseTexture;
+
+        @serializeAsColor3("ambient")
+        @expandToProperty(null)
+        public ambientColor = new Color3(0, 0, 0);
+
+        /**
+         * AKA Diffuse Color in other nomenclature.
+         */
+        @serializeAsColor3("albedo")
+        @expandToProperty(null)
+        public albedoColor = new Color3(1, 1, 1);
+        
+        /**
+         * AKA Specular Color in other nomenclature.
+         */
+        @serializeAsColor3("reflectivity")
+        @expandToProperty(null)
+        public reflectivityColor = new Color3(1, 1, 1);
+
+        @serializeAsColor3("reflection")
+        @expandToProperty(null)
+        public reflectionColor = new Color3(0.0, 0.0, 0.0);
+
+        @serializeAsColor3("emissive")
+        @expandToProperty(null)
+        public emissiveColor = new Color3(0, 0, 0);
+        
+        /**
+         * AKA Glossiness in other nomenclature.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public microSurface = 0.9;
+
+        /**
+         * source material index of refraction (IOR)' / 'destination material IOR.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public indexOfRefraction = 0.66;
+        
+        /**
+         * Controls if refraction needs to be inverted on Y. This could be usefull for procedural texture.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public invertRefractionY = false;
+
+        @serializeAsFresnelParameters()
+        @expandToProperty(null)
+        public opacityFresnelParameters: FresnelParameters;
+
+        @serializeAsFresnelParameters()
+        @expandToProperty(null)
+        public emissiveFresnelParameters: FresnelParameters;
+
+        /**
+         * This parameters will make the material used its opacity to control how much it is refracting aginst not.
+         * Materials half opaque for instance using refraction could benefit from this control.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public linkRefractionWithTransparency = false;
+
+        @serialize()
+        @expandToProperty(null)
+        public useLightmapAsShadowmap = false;
+        
+        /**
+         * In this mode, the emissive informtaion will always be added to the lighting once.
+         * A light for instance can be thought as emissive.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useEmissiveAsIllumination = false;
+        
+        /**
+         * Secifies that the alpha is coming form the albedo channel alpha channel.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useAlphaFromAlbedoTexture = false;
+        
+        /**
+         * Specifies that the material will keeps the specular highlights over a transparent surface (only the most limunous ones).
+         * A car glass is a good exemple of that. When sun reflects on it you can not see what is behind.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useSpecularOverAlpha = true;
+        
+        /**
+         * Specifies if the reflectivity texture contains the glossiness information in its alpha channel.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useMicroSurfaceFromReflectivityMapAlpha = false;
+
+        /**
+         * Specifies if the metallic texture contains the roughness information in its alpha channel.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useRoughnessFromMetallicTextureAlpha = true;
+
+        /**
+         * Specifies if the metallic texture contains the roughness information in its green channel.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useRoughnessFromMetallicTextureGreen = false;
+
+        /**
+         * Specifies if the metallic texture contains the metallness information in its blue channel.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useMetallnessFromMetallicTextureBlue = false;
+
+        /**
+         * Specifies if the metallic texture contains the ambient occlusion information in its red channel.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useAmbientOcclusionFromMetallicTextureRed = false;
+
+        /**
+         * Specifies if the ambient texture contains the ambient occlusion information in its red channel only.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useAmbientInGrayScale = false;
+        
+        /**
+         * In case the reflectivity map does not contain the microsurface information in its alpha channel,
+         * The material will try to infer what glossiness each pixel should be.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useAutoMicroSurfaceFromReflectivityMap = false;
+        
+        /**
+         * Allows to work with scalar in linear mode. This is definitely a matter of preferences and tools used during
+         * the creation of the material.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useScalarInLinearSpace = false;
+        
+        /**
+         * BJS is using an harcoded light falloff based on a manually sets up range.
+         * In PBR, one way to represents the fallof is to use the inverse squared root algorythm.
+         * This parameter can help you switch back to the BJS mode in order to create scenes using both materials.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public usePhysicalLightFalloff = true;
+        
+        /**
+         * Specifies that the material will keeps the reflection highlights over a transparent surface (only the most limunous ones).
+         * A car glass is a good exemple of that. When the street lights reflects on it you can not see what is behind.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useRadianceOverAlpha = true;
+        
+        /**
+         * Allows using the bump map in parallax mode.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useParallax = false;
+
+        /**
+         * Allows using the bump map in parallax occlusion mode.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public useParallaxOcclusion = false;
+
+        /**
+         * Controls the scale bias of the parallax mode.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public parallaxScaleBias = 0.05;
+        
+        /**
+         * If sets to true, disables all the lights affecting the material.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public disableLighting = false;
+
+        /**
+         * Number of Simultaneous lights allowed on the material.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public maxSimultaneousLights = 4;  
+
+        /**
+         * If sets to true, x component of normal map value will invert (x = 1.0 - x).
+         */
+        @serialize()
+        @expandToProperty(null)
+        public invertNormalMapX = false;
+
+        /**
+         * If sets to true, y component of normal map value will invert (y = 1.0 - y).
+         */
+        @serialize()
+        @expandToProperty(null)
+        public invertNormalMapY = false;
+
+        /**
+         * If sets to true and backfaceCulling is false, normals will be flipped on the backside.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public twoSidedLighting = false;
+
+        /**
+         * Instantiates a new PBRMaterial instance.
+         * 
+         * @param name The material name
+         * @param scene The scene the material will be use in.
+         */
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+        }
+
+        public getClassName(): string {
+            return "PBRMaterial";
+        }
+
+        public clone(name: string): PBRMaterial {
+            return SerializationHelper.Clone(() => new PBRMaterial(name, this.getScene()), this);
+        }
+
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this);
+            serializationObject.customType = "BABYLON.PBRMaterial";
+            return serializationObject;
+        }
+
+        // Statics
+        public static Parse(source: any, scene: Scene, rootUrl: string): PBRMaterial {
+            return SerializationHelper.Parse(() => new PBRMaterial(source.name, scene), source, scene, rootUrl);
+        }
+    }
+}

+ 87 - 0
src/Materials/PBR/babylon.pbrMetallicRoughnessMaterial.ts

@@ -0,0 +1,87 @@
+module BABYLON {
+    /**
+     * The PBR material of BJS following the metal roughness convention.
+     * 
+     * This fits to the define PBR convention in the GLTF definition: 
+     * https://github.com/KhronosGroup/glTF/tree/2.0/specification/2.0
+     */
+    export class PBRMetallicRoughnessMaterial extends Internals.PBRBaseSimpleMaterial {
+
+        /**
+         * The base color has two different interpretations depending on the value of metalness. 
+         * When the material is a metal, the base color is the specific measured reflectance value 
+         * at normal incidence (F0). For a non-metal the base color represents the reflected diffuse color 
+         * of the material.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_albedoColor")
+        public baseColor: Color3;
+        
+        /**
+         * Base texture of the metallic workflow. It contains both the baseColor information in RGB as
+         * well as opacity information in the alpha channel.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_albedoTexture")
+        public baseTexture: BaseTexture;
+
+        /**
+         * Specifies the metallic scalar value of the material.
+         * Can also be used to scale the metalness values of the metallic texture.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public metallic: number;
+
+        /**
+         * Specifies the roughness scalar value of the material.
+         * Can also be used to scale the roughness values of the metallic texture.
+         */
+        @serialize()
+        @expandToProperty(null)
+        public roughness: number;
+
+        /**
+         * Texture containing both the metallic value in the B channel and the 
+         * roughness value in the G channel to keep better precision.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_metallicTexture")
+        public metallicRoughnessTexture: BaseTexture;
+
+        /**
+         * Instantiates a new PBRMetalRoughnessMaterial instance.
+         * 
+         * @param name The material name
+         * @param scene The scene the material will be use in.
+         */
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+            this._useRoughnessFromMetallicTextureGreen = true;
+            this._useMetallnessFromMetallicTextureBlue = true;
+        }
+
+        /**
+         * Return the currrent class name of the material.
+         */
+        public getClassName(): string {
+            return "PBRMetallicRoughnessMaterial";
+        }
+
+        /**
+         * Serialize the material to a parsable JSON object.
+         */
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this);
+            serializationObject.customType = "BABYLON.PBRMetallicRoughnessMaterial";
+            return serializationObject;
+        }
+
+        /**
+         * Parses a JSON object correponding to the serialize function.
+         */
+        public static Parse(source: any, scene: Scene, rootUrl: string): PBRMetallicRoughnessMaterial {
+            return SerializationHelper.Parse(() => new PBRMetallicRoughnessMaterial(source.name, scene), source, scene, rootUrl);
+        }
+    }
+}

+ 80 - 0
src/Materials/PBR/babylon.pbrSpecularGlossinessMaterial.ts

@@ -0,0 +1,80 @@
+module BABYLON {
+    /**
+     * The PBR material of BJS following the specular glossiness convention.
+     * 
+     * This fits to the define PBR convention in the GLTF definition: 
+     * https://github.com/KhronosGroup/glTF/tree/2.0/extensions/Khronos/KHR_materials_pbrSpecularGlossiness
+     */
+    export class PBRSpecularGlossinessMaterial extends Internals.PBRBaseSimpleMaterial {
+
+        /**
+         * Specifies the diffuse Color of the material.
+         */
+        @serializeAsColor3()
+        @expandToProperty(null, "_albedoColor")
+        public diffuseColor: Color3;
+        
+        /**
+         * Specifies the diffuse texture of the material. This can aslo contains the opcity value in its alpha
+         * channel.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_albedoTexture")
+        public diffuseTexture: BaseTexture;
+
+        /**
+         * Specifies the specular color of the material. This indicates how reflective is the material (none to mirror).
+         */
+        @serializeAsColor3()
+        @expandToProperty(null, "_reflectivityColor")
+        public specularColor: Color3;
+
+        /**
+         * Specifies the glossiness of the material. This indicates "how sharp is the reflection".
+         */
+        @serialize()
+        @expandToProperty(null, "_microSurface")
+        public glossiness: number;
+        
+        /**
+         * Spectifies both the specular color RGB and the glossiness A of the material per pixels.
+         */
+        @serializeAsTexture()
+        @expandToProperty(null, "_reflectivityTexture")
+        public specularGlossinessTexture: BaseTexture;
+
+        /**
+         * Instantiates a new PBRSpecularGlossinessMaterial instance.
+         * 
+         * @param name The material name
+         * @param scene The scene the material will be use in.
+         */
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+            this._useMicroSurfaceFromReflectivityMapAlpha = true;
+        }
+
+        /**
+         * Return the currrent class name of the material.
+         */
+        public getClassName(): string {
+            return "PBRSpecularGlossinessMaterial";
+        }
+
+        /**
+         * Serialize the material to a parsable JSON object.
+         */
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this);
+            serializationObject.customType = "BABYLON.PBRSpecularGlossinessMaterial";
+            return serializationObject;
+        }
+
+        /**
+         * Parses a JSON object correponding to the serialize function.
+         */
+        public static Parse(source: any, scene: Scene, rootUrl: string): PBRSpecularGlossinessMaterial {
+            return SerializationHelper.Parse(() => new PBRSpecularGlossinessMaterial(source.name, scene), source, scene, rootUrl);
+        }
+    }
+}

+ 8 - 0
src/Materials/Textures/babylon.baseTexture.ts

@@ -90,6 +90,10 @@
         public _texture: WebGLTexture;
         private _uid: string;
 
+        public get isBlocking(): boolean {
+            return true;
+        }
+
         constructor(scene: Scene) {
             this._scene = scene || Engine.LastCreatedScene;
             this._scene.textures.push(this);
@@ -112,6 +116,10 @@
             return this._texture;
         }
 
+        public isReadyOrNotBlocking(): boolean {
+            return !this.isBlocking || this.isReady();
+        }
+
         public isReady(): boolean {
             if (this.delayLoadState === Engine.DELAYLOADSTATE_NOTLOADED) {
                 this.delayLoad();

+ 19 - 3
src/Materials/Textures/babylon.hdrCubeTexture.ts

@@ -49,6 +49,20 @@ module BABYLON {
          */
         public isPMREM = false;
 
+        protected _isBlocking: boolean = true;
+        /**
+         * Sets wether or not the texture is blocking during loading.
+         */
+        public set isBlocking(value: boolean) {
+            this._isBlocking = value;
+        }
+        /**
+         * Gets wether or not the texture is blocking during loading.
+         */
+        public get isBlocking(): boolean {
+            return this._isBlocking;
+        }
+
         /**
          * Instantiates an HDRTexture from the following parameters.
          * 
@@ -232,7 +246,7 @@ module BABYLON {
                 return results;
             }
 
-            this._texture = (<any>this.getScene().getEngine()).createRawCubeTexture(this.url, this.getScene(), this._size,
+            this._texture = (<any>this.getScene().getEngine()).createRawCubeTextureFromUrl(this.url, this.getScene(), this._size,
                 Engine.TEXTUREFORMAT_RGB,
                 this.getScene().getEngine().getCaps().textureFloat ? BABYLON.Engine.TEXTURETYPE_FLOAT : BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT,
                 this._noMipmap,
@@ -331,12 +345,12 @@ module BABYLON {
                 };
             }
 
-            this._texture = (<any>this.getScene().getEngine()).createRawCubeTexture(this.url, this.getScene(), this._size,
+            this._texture = (<any>this.getScene().getEngine()).createRawCubeTextureFromUrl(this.url, this.getScene(), this._size,
                 Engine.TEXTUREFORMAT_RGB,
                 this.getScene().getEngine().getCaps().textureFloat ? BABYLON.Engine.TEXTURETYPE_FLOAT : BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT,
                 this._noMipmap,
                 callback,
-                mipmapGenerator, this._onLoad, this._onError);                
+                mipmapGenerator, this._onLoad, this._onError);
         }
 
         /**
@@ -394,6 +408,7 @@ module BABYLON {
                 texture.hasAlpha = parsedTexture.hasAlpha;
                 texture.level = parsedTexture.level;
                 texture.coordinatesMode = parsedTexture.coordinatesMode;
+                texture.isBlocking = parsedTexture.isBlocking;
             }
             return texture;
         }
@@ -416,6 +431,7 @@ module BABYLON {
             serializationObject.isBABYLONPreprocessed = this._isBABYLONPreprocessed;
             serializationObject.customType = "BABYLON.HDRCubeTexture";
             serializationObject.noMipmap = this._noMipmap;
+            serializationObject.isBlocking = this._isBlocking;
             
             return serializationObject;
         }

+ 13 - 0
src/Materials/Textures/babylon.texture.ts

@@ -74,6 +74,15 @@
         private _delayedOnError: () => void;
         private _onLoadObservarble: Observable<boolean>;
 
+        protected _isBlocking: boolean = true;
+        public set isBlocking(value: boolean) {
+            this._isBlocking = value;
+        }
+        @serialize()
+        public get isBlocking(): boolean {
+            return this._isBlocking;
+        }
+
         constructor(url: string, scene: Scene, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: () => void = null, onError: () => void = null, buffer: any = null, deleteBuffer: boolean = false, format?: number) {
             super(scene);
 
@@ -100,6 +109,10 @@
                 if (onLoad) {
                     onLoad();
                 }
+
+                if (!this.isBlocking) {
+                    scene.resetCachedMaterial();
+                }
             }
 
             if (!this._texture) {

+ 10 - 8
src/Materials/babylon.standardMaterial.ts

@@ -379,7 +379,7 @@ module BABYLON {
                 defines._needUVs = false;
                 if (scene.texturesEnabled) {
                     if (this._diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
-                        if (!this._diffuseTexture.isReady()) {
+                        if (!this._diffuseTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needUVs = true;
@@ -390,7 +390,7 @@ module BABYLON {
                     }
 
                     if (this._ambientTexture && StandardMaterial.AmbientTextureEnabled) {
-                        if (!this._ambientTexture.isReady()) {
+                        if (!this._ambientTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needUVs = true;
@@ -401,7 +401,7 @@ module BABYLON {
                     }
 
                     if (this._opacityTexture && StandardMaterial.OpacityTextureEnabled) {
-                        if (!this._opacityTexture.isReady()) {
+                        if (!this._opacityTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needUVs = true;
@@ -413,7 +413,7 @@ module BABYLON {
                     }
 
                     if (this._reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
-                        if (!this._reflectionTexture.isReady()) {
+                        if (!this._reflectionTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needNormals = true;
@@ -460,7 +460,7 @@ module BABYLON {
                     }
 
                     if (this._emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
-                        if (!this._emissiveTexture.isReady()) {
+                        if (!this._emissiveTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needUVs = true;
@@ -471,7 +471,7 @@ module BABYLON {
                     }
 
                     if (this._lightmapTexture && StandardMaterial.LightmapTextureEnabled) {
-                        if (!this._lightmapTexture.isReady()) {
+                        if (!this._lightmapTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needUVs = true;
@@ -483,7 +483,7 @@ module BABYLON {
                     }
 
                     if (this._specularTexture && StandardMaterial.SpecularTextureEnabled) {
-                        if (!this._specularTexture.isReady()) {
+                        if (!this._specularTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needUVs = true;
@@ -495,6 +495,7 @@ module BABYLON {
                     }
 
                     if (scene.getEngine().getCaps().standardDerivatives && this._bumpTexture && StandardMaterial.BumpTextureEnabled) {
+                        // Bump texure can not be none blocking.
                         if (!this._bumpTexture.isReady()) {
                             return false;
                         } else {
@@ -512,7 +513,7 @@ module BABYLON {
                     }
 
                     if (this._refractionTexture && StandardMaterial.RefractionTextureEnabled) {
-                        if (!this._refractionTexture.isReady()) {
+                        if (!this._refractionTexture.isReadyOrNotBlocking()) {
                             return false;
                         } else {
                             defines._needUVs = true;
@@ -525,6 +526,7 @@ module BABYLON {
                     }
 
                     if (this._cameraColorGradingTexture && StandardMaterial.ColorGradingTextureEnabled) {
+                        // Camera Color Grading can not be none blocking.
                         if (!this._cameraColorGradingTexture.isReady()) {
                             return false;
                         } else {

+ 8 - 1
src/Shaders/ShadersInclude/bumpFragment.fx

@@ -1,10 +1,17 @@
 vec2 uvOffset = vec2(0.0, 0.0);
 
 #if defined(BUMP) || defined(PARALLAX)
+	#if NORMALXYSCALE
+		normalW = normalize(normalW * vec3(vBumpInfos.y, vBumpInfos.y, 1.0));
+		float normalScale = 1.0;
+	#else		
+		float normalScale = vBumpInfos.y;
+	#endif
+
 	#if defined(TANGENT) && defined(NORMAL)
 		mat3 TBN = vTBN;
 	#else
-		mat3 TBN = cotangent_frame(normalW * vBumpInfos.y, vPositionW, vBumpUV);
+		mat3 TBN = cotangent_frame(normalW * normalScale, vPositionW, vBumpUV);
 	#endif
 #endif
 

+ 57 - 61
src/Shaders/pbr.fragment.fx

@@ -149,41 +149,35 @@ void main(void) {
 
 #include<bumpFragment>
 
-#ifdef TWOSIDEDLIGHTING
+#if defined(TWOSIDEDLIGHTING) && defined(NORMAL) 
 	normalW = gl_FrontFacing ? normalW : -normalW;
 #endif
 
 	// Albedo
-	vec4 surfaceAlbedo = vec4(1., 1., 1., 1.);
-	vec3 surfaceAlbedoContribution = vAlbedoColor.rgb;
+	vec3 surfaceAlbedo = vAlbedoColor.rgb;
 
 	// Alpha
 	float alpha = vAlbedoColor.a;
 
 #ifdef ALBEDO
-	surfaceAlbedo = texture2D(albedoSampler, vAlbedoUV + uvOffset);
-	surfaceAlbedo = vec4(toLinearSpace(surfaceAlbedo.rgb), surfaceAlbedo.a);
-
-	#ifndef LINKREFRACTIONTOTRANSPARENCY
-		#ifdef ALPHATEST
-			if (surfaceAlbedo.a < 0.4)
-				discard;
-		#endif
-	#endif
-
+	vec4 albedoTexture = texture2D(albedoSampler, vAlbedoUV + uvOffset);
 	#ifdef ALPHAFROMALBEDO
-		alpha *= surfaceAlbedo.a;
+		alpha *= albedoTexture.a;
 	#endif
 
-	surfaceAlbedo.rgb *= vAlbedoInfos.y;
-#else
-	// No Albedo texture.
-	surfaceAlbedo.rgb = surfaceAlbedoContribution;
-	surfaceAlbedoContribution = vec3(1., 1., 1.);
+	surfaceAlbedo *= toLinearSpace(albedoTexture.rgb);
+	surfaceAlbedo *= vAlbedoInfos.y;
+#endif
+
+#ifndef LINKREFRACTIONTOTRANSPARENCY
+	#if defined(ALPHATEST) && defined(ALPHATESTVALUE)
+		if (alpha < ALPHATESTVALUE)
+			discard;
+	#endif
 #endif
 
 #ifdef VERTEXCOLOR
-	surfaceAlbedo.rgb *= vColor.rgb;
+	surfaceAlbedo *= vColor.rgb;
 #endif
 
 	// Ambient color
@@ -201,28 +195,13 @@ void main(void) {
 	float microSurface = vReflectivityColor.a;
 	vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
 
-#ifdef REFLECTIVITY
-	vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
-	surfaceReflectivityColor = surfaceReflectivityColorMap.rgb;
-	surfaceReflectivityColor = toLinearSpace(surfaceReflectivityColor);
-	surfaceReflectivityColor *= vReflectivityInfos.y;
-
-	#ifdef MICROSURFACEFROMREFLECTIVITYMAP
-		microSurface = surfaceReflectivityColorMap.a * vReflectivityInfos.z;
-	#else
-		#ifdef MICROSURFACEAUTOMATIC
-			microSurface = computeDefaultMicroSurface(microSurface, surfaceReflectivityColor);
-		#endif
-	#endif
-#endif
-
 #ifdef METALLICWORKFLOW
 	vec2 metallicRoughness = surfaceReflectivityColor.rg;
 
 	#ifdef METALLICMAP
 		vec4 surfaceMetallicColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
 
-		#ifdef AOSTOREINMETALMAPRED			
+		#ifdef AOSTOREINMETALMAPRED
 			vec3 aoStoreInMetalMap = vec3(surfaceMetallicColorMap.r, surfaceMetallicColorMap.r, surfaceMetallicColorMap.r);
 			ambientOcclusionColor = mix(ambientOcclusionColor, aoStoreInMetalMap, vReflectivityInfos.z);
 		#endif
@@ -251,21 +230,36 @@ void main(void) {
 	microSurface = 1.0 - metallicRoughness.g;
 
 	// Diffuse is used as the base of the reflectivity.
-	vec3 baseColor = surfaceAlbedo.rgb;
+	vec3 baseColor = surfaceAlbedo;
 
 	// Default specular reflectance at normal incidence.
 	// 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
 	const vec3 DefaultSpecularReflectanceDielectric = vec3(0.04, 0.04, 0.04);
 
 	// Compute the converted diffuse.
-	surfaceAlbedo.rgb = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
+	surfaceAlbedo = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
 
 	// Compute the converted reflectivity.
 	surfaceReflectivityColor = mix(DefaultSpecularReflectanceDielectric, baseColor, metallicRoughness.r);
 #else
-	#ifdef MICROSURFACEMAP
-		vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
-		microSurface = microSurfaceTexel.r;
+	#ifdef REFLECTIVITY
+		vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
+		surfaceReflectivityColor *= toLinearSpace(surfaceReflectivityColorMap.rgb);
+		surfaceReflectivityColor *= vReflectivityInfos.y;
+
+		#ifdef MICROSURFACEFROMREFLECTIVITYMAP
+			microSurface *= surfaceReflectivityColorMap.a;
+			microSurface *= vReflectivityInfos.z;
+		#else
+			#ifdef MICROSURFACEAUTOMATIC
+				microSurface *= computeDefaultMicroSurface(microSurface, surfaceReflectivityColor);
+			#endif
+
+			#ifdef MICROSURFACEMAP
+				vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
+				microSurface *= microSurfaceTexel.r;
+			#endif
+		#endif
 	#endif
 #endif
 
@@ -484,12 +478,12 @@ void main(void) {
 
 		// Tint the material with albedo.
 		// TODO. PBR Tinting.
-		vec3 mixedAlbedo = surfaceAlbedoContribution.rgb * surfaceAlbedo.rgb;
+		vec3 mixedAlbedo = surfaceAlbedo;
 		float maxChannel = max(max(mixedAlbedo.r, mixedAlbedo.g), mixedAlbedo.b);
 		vec3 tint = clamp(maxChannel * mixedAlbedo, 0.0, 1.0);
 
 		// Decrease Albedo Contribution
-		surfaceAlbedoContribution *= alpha;
+		surfaceAlbedo *= alpha;
 
 		// Decrease irradiance Contribution
 		environmentIrradiance *= alpha;
@@ -532,14 +526,9 @@ void main(void) {
 #endif
 
 	// Composition
-#ifdef EMISSIVEASILLUMINATION
-	vec3 finalDiffuse = lightDiffuseContribution * surfaceAlbedoContribution;
-#else
-	#ifdef LINKEMISSIVEWITHALBEDO
-		vec3 finalDiffuse = (lightDiffuseContribution + surfaceEmissiveColor) * surfaceAlbedoContribution;
-	#else
-		vec3 finalDiffuse = lightDiffuseContribution * surfaceAlbedoContribution + surfaceEmissiveColor;
-	#endif
+	vec3 finalDiffuse = lightDiffuseContribution;
+#ifndef EMISSIVEASILLUMINATION
+	finalDiffuse += surfaceEmissiveColor;
 #endif
 
 finalDiffuse.rgb += vAmbientColor;
@@ -547,23 +536,28 @@ finalDiffuse *= surfaceAlbedo.rgb;
 finalDiffuse = max(finalDiffuse, 0.0);
 finalDiffuse = (finalDiffuse * vLightingIntensity.x + surfaceAlbedo.rgb * environmentIrradiance) * ambientOcclusionColor;
 
+float luminanceOverAlpha = 0.0;
+#ifdef RADIANCEOVERALPHA
+	luminanceOverAlpha += getLuminance(environmentRadiance);
+#endif
+
 #ifdef SPECULARTERM
 	vec3 finalSpecular = lightSpecularContribution * surfaceReflectivityColor;
 	#ifdef SPECULAROVERALPHA
-		alpha = clamp(alpha + getLuminance(finalSpecular), 0., 1.);
+		luminanceOverAlpha += getLuminance(finalSpecular);
 	#endif
 #else
 	vec3 finalSpecular = vec3(0.0);
 #endif
+finalSpecular *= vLightingIntensity.x;
 
-#ifdef RADIANCEOVERALPHA
-	alpha = clamp(alpha + getLuminance(environmentRadiance), 0., 1.);
+#if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA)
+	alpha = clamp(alpha + luminanceOverAlpha * alpha, 0., 1.);
 #endif
 
 // Composition
 // Reflection already includes the environment intensity.
-vec4 finalColor = vec4(finalDiffuse + finalSpecular * vLightingIntensity.x + environmentRadiance + refractance, alpha);
-
+vec4 finalColor = vec4(finalDiffuse + finalSpecular + environmentRadiance + refractance, alpha);
 #ifdef EMISSIVEASILLUMINATION
 	finalColor.rgb += (surfaceEmissiveColor * vLightingIntensity.y);
 #endif
@@ -593,14 +587,16 @@ vec4 finalColor = vec4(finalDiffuse + finalSpecular * vLightingIntensity.x + env
 	finalColor = contrasts(finalColor);
 #endif
 
+#ifdef LDROUTPUT
 	finalColor.rgb = clamp(finalColor.rgb, 0., 1.);
 
-#ifdef CAMERACOLORGRADING
-	finalColor = colorGrades(finalColor);
-#endif
+	#ifdef CAMERACOLORGRADING
+		finalColor = colorGrades(finalColor);
+	#endif
 
-#ifdef CAMERACOLORCURVES
-	finalColor.rgb = applyColorCurves(finalColor.rgb);
+	#ifdef CAMERACOLORCURVES
+		finalColor.rgb = applyColorCurves(finalColor.rgb);
+	#endif
 #endif
 
 	gl_FragColor = finalColor;

+ 20 - 19
src/Tools/babylon.decorators.ts

@@ -11,30 +11,31 @@
         }
     }
 
-    function generateExpandMember(setCallback: string) {
+    function generateExpandMember(setCallback: string, targetKey: string) {
         return (target: any, propertyKey: string) => {
-            if (setCallback) {
-                var key = "_" + propertyKey;
-                Object.defineProperty(target, propertyKey, {
-                    get: function () {
-                        return this[key];
-                    },
-                    set: function (value) {
-                        if (this[key] === value) {
-                            return;
-                        }
-                        this[key] = value;
+            var key = targetKey || ("_" + propertyKey);
+            Object.defineProperty(target, propertyKey, {
+                get: function () {
+                    return this[key];
+                },
+                set: function (value) {
+                    if (this[key] === value) {
+                        return;
+                    }
+                    this[key] = value;
+                    
+                    if (setCallback) {
                         target[setCallback].apply(this);
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-            }
+                    }
+                },
+                enumerable: true,
+                configurable: true
+            });
         }
     }
 
-    export function expandToProperty(callback: string) {
-        return generateExpandMember(callback);
+    export function expandToProperty(callback: string, targetKey?: string) {
+        return generateExpandMember(callback, targetKey);
     }
 
     export function serialize(sourceName?: string) {

+ 143 - 53
src/babylon.engine.ts

@@ -557,6 +557,7 @@
         private _mustWipeVertexAttributes = false;
 
         private _emptyTexture: WebGLTexture;
+        private _emptyCubeTexture: WebGLTexture;
 
         // Hardware supported Compressed Textures
         private _texturesSupported = new Array<string>();
@@ -578,6 +579,15 @@
 
             return this._emptyTexture;
         }
+        public get emptyCubeTexture(): WebGLTexture {
+            if (!this._emptyCubeTexture) {
+                var faceData = new Uint8Array(4);
+                var cubeData = [faceData, faceData, faceData, faceData, faceData, faceData];
+                this._emptyCubeTexture = this.createRawCubeTexture(cubeData, 1, BABYLON.Engine.TEXTUREFORMAT_RGBA, BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT, false, false, BABYLON.Texture.NEAREST_SAMPLINGMODE);
+            }
+
+            return this._emptyCubeTexture;
+        }
 
         /**
          * @constructor
@@ -3196,43 +3206,138 @@
             texture._baseHeight = height;
         }
 
-        public createRawCubeTexture(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean,
-            callback: (ArrayBuffer: ArrayBuffer) => ArrayBufferView[],
-            mipmmapGenerator: ((faces: ArrayBufferView[]) => ArrayBufferView[][]), onLoad: () => void = null, onError: () => void = null): WebGLTexture {
+        public updateRawCubeTexture(texture: WebGLTexture, data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: string = null, level = 0): void {
+            var gl = this._gl;
+            var textureType = this._getWebGLTextureType(type);
+            var internalFormat = this._getInternalFormat(format);
+            var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
+
+            var needConversion = false;
+            if (internalFormat === gl.RGB) {
+                internalFormat = gl.RGBA;
+                needConversion = true;
+            }
+
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
+
+            if (texture._width % 4 !== 0) {
+                gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
+            }
+
+            var facesIndex = [
+                gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
+                gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
+            ];
+
+            // Data are known to be in +X +Y +Z -X -Y -Z
+            for (let index = 0; index < facesIndex.length; index++) {
+                let faceData = data[index];
+
+                if (compression) {
+                    gl.compressedTexImage2D(facesIndex[index], level, this.getCaps().s3tc[compression], texture._width, texture._height, 0, faceData);
+                } else {
+                    if (needConversion) {
+                        faceData = this._convertRGBtoRGBATextureData(faceData, texture._width, texture._height, type);
+                    }
+                    gl.texImage2D(facesIndex[index], level, internalSizedFomat, texture._width, texture._height, 0, internalFormat, textureType, faceData);
+                }
+            }
+
+            var isPot = (Tools.IsExponentOfTwo(texture._width) && Tools.IsExponentOfTwo(texture._height));
+            if (isPot && texture.generateMipMaps && level === 0) {
+                this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP);
+            }
+            this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
+
+            this.resetTextureCache();
+            texture.isReady = true;
+        }
+
+        public createRawCubeTexture(data: ArrayBufferView[], size: number, format: number, type: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: string = null): WebGLTexture {
             var gl = this._gl;
             var texture = gl.createTexture();
-            scene._addPendingData(texture);
             texture.isCube = true;
             texture.references = 1;
-            texture.url = url;
-
-            var textureType = gl.UNSIGNED_BYTE;
-            if (type === Engine.TEXTURETYPE_FLOAT) {
-                textureType = gl.FLOAT;
-            }
 
+            var textureType = this._getWebGLTextureType(type);
             var internalFormat = this._getInternalFormat(format);
+            var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
+
             var needConversion = false;
             if (internalFormat === gl.RGB) {
                 internalFormat = gl.RGBA;
                 needConversion = true;
             }
-            var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
 
             var width = size;
             var height = width;
-            var isPot = (Tools.IsExponentOfTwo(width) && Tools.IsExponentOfTwo(height));
 
             texture._width = width;
             texture._height = height;
 
+            // Double check on POT to generate Mips.
+            var isPot = (Tools.IsExponentOfTwo(texture._width) && Tools.IsExponentOfTwo(texture._height));
+            if (!isPot) {
+                generateMipMaps = false;
+            }
+            texture.generateMipMaps = generateMipMaps;
+
+            // Upload data if needed. The texture won t be ready until then.
+            if (data) {
+                this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
+            }
+
+            this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture);
+
+            // Filters
+            if (data && generateMipMaps) {
+                this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP);
+            }
+
+            if (textureType === gl.FLOAT && !this._caps.textureFloatLinearFiltering) {
+                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+            }
+            else if (textureType === Engine.HALF_FLOAT_OES && !this._caps.textureHalfFloatLinearFiltering) {
+                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+            }
+            else {
+                var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
+                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, filters.mag);
+                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, filters.min);
+            }
+
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
+
+            this._loadedTexturesCache.push(texture);
+
+            return texture;
+        }
+
+        public createRawCubeTextureFromUrl(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean,
+            callback: (ArrayBuffer: ArrayBuffer) => ArrayBufferView[],
+            mipmmapGenerator: ((faces: ArrayBufferView[]) => ArrayBufferView[][]), 
+            onLoad: () => void = null, 
+            onError: () => void = null,
+            samplingMode = Texture.TRILINEAR_SAMPLINGMODE,
+            invertY = false): WebGLTexture {
+
+            var gl = this._gl;
+            var texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode);
+            scene._addPendingData(texture);
+            texture.url = url;
+
             var onerror = () => {
                 scene._removePendingData(texture);
                 if (onError) {
                     onError();
                 }
             };
-
+            
             var internalCallback = (data) => {
                 var rgbeDataArrays = callback(data);
 
@@ -3241,15 +3346,24 @@
                     gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
                 ];
 
-                width = texture._width;
-                height = texture._height;
-                isPot = (Tools.IsExponentOfTwo(width) && Tools.IsExponentOfTwo(height));
+                var width = texture._width;
+                var height = texture._height;
+                if (mipmmapGenerator) {
 
-                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
-                gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
+                    // TODO Remove this once Proper CubeMap Blur... This has nothing to do in engine...
+                    // I ll remove ASAP.
+                    var textureType = this._getWebGLTextureType(type);
+                    var internalFormat = this._getInternalFormat(format);
+                    var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
 
-                if (mipmmapGenerator) {
+                    var needConversion = false;
+                    if (internalFormat === gl.RGB) {
+                        internalFormat = gl.RGBA;
+                        needConversion = true;
+                    }
 
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
                     var arrayTemp: ArrayBufferView[] = [];
                     // Data are known to be in +X +Y +Z -X -Y -Z
                     // mipmmapGenerator data is expected to be order in +X -X +Y -Y +Z -Z
@@ -3274,45 +3388,15 @@
                             gl.texImage2D(facesIndex[mipIndex], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipFaceData);
                         }
                     }
-                }
-                else {
-                    // Data are known to be in +X +Y +Z -X -Y -Z
-                    for (let index = 0; index < facesIndex.length; index++) {
-                        let faceData = rgbeDataArrays[index];
-                        if (needConversion) {
-                            faceData = this._convertRGBtoRGBATextureData(faceData, width, height, type);
-                        }
-
-                        gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, faceData);
-                    }
-
-                    if (!noMipmap && isPot) {
-                        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                    }
-                    else {
-                        noMipmap = true;
-                    }
-                }
 
-                if (textureType === gl.FLOAT && !this._caps.textureFloatLinearFiltering) {
-                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
-                }
-                else if (textureType === Engine.HALF_FLOAT_OES && !this._caps.textureHalfFloatLinearFiltering) {
-                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
                 }
                 else {
-                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, noMipmap ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR);
+                    texture.generateMipMaps = !noMipmap;
+                    this.updateRawCubeTexture(texture, rgbeDataArrays, format, type, invertY);
                 }
 
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-                gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
-
                 texture.isReady = true;
-
                 this.resetTextureCache();
                 scene._removePendingData(texture);
 
@@ -3471,7 +3555,9 @@
                 return;
             }
 
-            var internalTexture = texture.isReady() ? texture.getInternalTexture() : this.emptyTexture;
+
+            var internalTexture = texture.isReady() ? texture.getInternalTexture() : 
+                (texture.isCube ? this.emptyCubeTexture : this.emptyTexture);
 
             if (this._activeTexturesCache[channel] === internalTexture) {
                 return;
@@ -3684,6 +3770,10 @@
                 this._releaseTexture(this._emptyTexture);
                 this._emptyTexture = null;
             }
+            if (this._emptyCubeTexture) {
+                this._releaseTexture(this._emptyCubeTexture);
+                this._emptyCubeTexture = null;
+            }
 
             // Release scenes
             while (this.scenes.length) {

+ 61 - 0
src/babylon.scene.ts

@@ -189,6 +189,25 @@
         public clearColor: Color4 = new Color4(0.2, 0.2, 0.3, 1.0);
         public ambientColor = new Color3(0, 0, 0);
 
+        protected _environmentTexture: BaseTexture;
+        /**
+         * Texture used in all pbr material as the reflection texture.
+         * As in the majority of the scene they are the same (exception for multi room and so on),
+         * this is easier to reference from here than from all the materials.
+         */
+        public get environmentTexture(): BaseTexture {
+            return this._environmentTexture;
+        }
+        /**
+         * Texture used in all pbr material as the reflection texture.
+         * As in the majority of the scene they are the same (exception for multi room and so on),
+         * this is easier to set here than in all the materials.
+         */
+        public set environmentTexture(value: BaseTexture) {
+            this._environmentTexture = value;
+            this.markAllMaterialsAsDirty(Material.TextureDirtyFlag);
+        }
+
         public forceWireframe = false;
         private _forcePointsCloud = false;
         public set forcePointsCloud(value : boolean) {
@@ -3663,6 +3682,48 @@
             }
         }
 
+        public createDefaultSkybox(environmentTexture?: BaseTexture, pbr = false): Mesh {
+            if (environmentTexture) {
+                this.environmentTexture = environmentTexture;
+            }
+
+            if (!this.environmentTexture) {
+                Tools.Warn("Can not create default skybox without environment texture.");
+                return;
+            }
+
+            if (!this.environmentTexture) {
+                Tools.Warn("Can not create default skybox without environment texture.");
+                return;
+            }
+
+            // Skybox
+            var hdrSkybox = BABYLON.Mesh.CreateBox("hdrSkyBox", 1000.0, this);
+            if (pbr) {
+                let hdrSkyboxMaterial = new BABYLON.PBRMaterial("skyBox", this);
+                hdrSkyboxMaterial.backFaceCulling = false;
+                hdrSkyboxMaterial.reflectionTexture = environmentTexture.clone();
+                hdrSkyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
+                hdrSkyboxMaterial.microSurface = 1.0;
+                hdrSkyboxMaterial.disableLighting = true;
+                hdrSkybox.infiniteDistance = true;
+                hdrSkybox.material = hdrSkyboxMaterial;
+            }
+            else {
+                let skyboxMaterial = new BABYLON.StandardMaterial("skyBox", this);
+                skyboxMaterial.backFaceCulling = false;
+                skyboxMaterial.reflectionTexture = environmentTexture.clone();
+                skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
+                skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
+                skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
+                skyboxMaterial.disableLighting = true;
+                hdrSkybox.infiniteDistance = true;
+                hdrSkybox.material = skyboxMaterial;
+            }
+
+            return hdrSkybox;
+        }
+
         // Tags
         private _getByTags(list: any[], tagsQuery: string, forEach?: (item: any) => void): any[] {
             if (tagsQuery === undefined) {