Browse Source

Merge pull request #1025 from sebavan/GridMaterial

Grid Material
Raanan Weber 9 years ago
parent
commit
c90c1f3174

+ 16 - 1
materialsLibrary/config.json

@@ -12,7 +12,11 @@
     {
       "file": "materials/pbr/babylon.pbrMaterial.ts",
       "referenceFiles": [
-        "materials/pbr/includes/helperFunctions.fx"  
+        "materials/pbr/includes/pbrFunctions.fx",
+        "materials/pbr/includes/harmonicsFunctions.fx",
+        "materials/pbr/includes/pbrLightFunctions.fx",
+        "materials/pbr/includes/pbrLightFunctionsCall.fx",
+        "materials/pbr/includes/pbrShadowFunctions.fx"  
       ],
       "shaderFiles": [
         "materials/pbr/pbr.vertex.fx",
@@ -94,6 +98,17 @@
         "materials/sky/sky.fragment.fx"
       ],
       "output": "babylon.skyMaterial.js"
+    },
+    {
+      "file": "materials/grid/babylon.gridMaterial.ts",
+      "shaderFiles": [
+        "materials/grid/grid.vertex.fx",
+        "materials/grid/grid.fragment.fx",
+        "materials/grid/legacygrid.vertex.fx",
+        "materials/grid/legacygrid.fragment.fx"
+      ],
+      "output": "babylon.gridMaterial.js",
+      "declarationFilename": "babylon.gridMaterial.d.ts"
     }
   ],
   "build": {

File diff suppressed because it is too large
+ 180 - 0
materialsLibrary/dist/babylon.gridMaterial.js


File diff suppressed because it is too large
+ 1 - 0
materialsLibrary/dist/babylon.gridMaterial.min.js


File diff suppressed because it is too large
+ 8 - 4
materialsLibrary/dist/babylon.pbrMaterial.js


File diff suppressed because it is too large
+ 2 - 2
materialsLibrary/dist/babylon.pbrMaterial.min.js


+ 55 - 0
materialsLibrary/dist/dts/babylon.gridMaterial.d.ts

@@ -0,0 +1,55 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts" />
+declare module BABYLON {
+    /**
+     * The grid materials allows you to wrap any shape with a grid.
+     * Colors are customizable.
+     */
+    class GridMaterial extends BABYLON.Material {
+        /**
+         * Main color of the grid (e.g. between lines)
+         */
+        mainColor: Color3;
+        /**
+         * Color of the grid lines.
+         */
+        lineColor: Color3;
+        /**
+         * The scale of the grid compared to unit.
+         */
+        gridRatio: number;
+        /**
+         * The frequency of thicker lines.
+         */
+        majorUnitFrequency: number;
+        /**
+         * The visibility of minor units in the grid.
+         */
+        minorUnitVisibility: number;
+        /**
+         * The grid opacity outside of the lines.
+         */
+        opacity: number;
+        private _gridControl;
+        private _renderId;
+        private _defines;
+        private _cachedDefines;
+        /**
+         * constructor
+         * @param name The name given to the material in order to identify it afterwards.
+         * @param scene The scene the material is used in.
+         */
+        constructor(name: string, scene: Scene);
+        /**
+         * Returns wehter or not the grid requires alpha blending.
+         */
+        needAlphaBlending(): boolean;
+        private _checkCache(scene, mesh?, useInstances?);
+        isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean;
+        bindOnlyWorldMatrix(world: Matrix): void;
+        bind(world: Matrix, mesh?: Mesh): void;
+        dispose(forceDisposeEffect?: boolean): void;
+        clone(name: string): GridMaterial;
+        serialize(): any;
+        static Parse(source: any, scene: Scene, rootUrl: string): GridMaterial;
+    }
+}

+ 196 - 0
materialsLibrary/materials/grid/babylon.gridmaterial.ts

@@ -0,0 +1,196 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    class GRIDMaterialDefines extends MaterialDefines {
+        public TRANSPARENT = false;
+
+        constructor() {
+            super();
+            this._keys = Object.keys(this);
+        }
+    }
+    
+    /**
+     * The grid materials allows you to wrap any shape with a grid.
+     * Colors are customizable.
+     */
+    export class GridMaterial extends BABYLON.Material {
+        
+        /**
+         * Main color of the grid (e.g. between lines)
+         */
+        @serializeAsColor3()
+        public mainColor = Color3.White();
+        
+        /**
+         * Color of the grid lines.
+         */
+        @serializeAsColor3()
+        public lineColor = Color3.Black();
+        
+        /**
+         * The scale of the grid compared to unit.
+         */
+        @serialize()
+        public gridRatio = 1.0;
+        
+        /**
+         * The frequency of thicker lines.
+         */
+        @serialize()
+        public majorUnitFrequency = 10;
+        
+        /**
+         * The visibility of minor units in the grid.
+         */
+        @serialize()
+        public minorUnitVisibility = 0.33;
+        
+        /**
+         * The grid opacity outside of the lines.
+         */
+        @serialize()
+        public opacity = 1.0;
+        
+        private _gridControl: Vector4 = new Vector4(this.gridRatio, this.majorUnitFrequency, this.minorUnitVisibility, this.opacity);
+        
+        private _renderId: number;
+        private _defines = new GRIDMaterialDefines();
+        private _cachedDefines: GRIDMaterialDefines = new GRIDMaterialDefines();
+        
+        /**
+         * constructor
+         * @param name The name given to the material in order to identify it afterwards.
+         * @param scene The scene the material is used in.
+         */
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+            
+            // Forces cache to be different on first creation.
+            this._cachedDefines.TRANSPARENT = true;
+        }
+        
+        /**
+         * Returns wehter or not the grid requires alpha blending.
+         */
+        public needAlphaBlending(): boolean {
+            return this.opacity < 1.0;
+        }
+          
+        private _checkCache(scene: Scene, mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (!mesh) {
+                return true;
+            }
+
+            if (mesh._materialDefines && mesh._materialDefines.isEqual(this._defines)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (this.checkReadyOnlyOnce) {
+                if (this._wasPreviouslyReady) {
+                    return true;
+                }
+            }
+
+            var scene = this.getScene();
+
+            if (!this.checkReadyOnEveryCall) {
+                if (this._renderId === scene.getRenderId()) {
+                    if (this._checkCache(scene, mesh, useInstances)) {
+                        return true;
+                    }
+                }
+            }
+
+            var engine = scene.getEngine();
+            var needNormals = true;
+
+            this._defines.reset();
+
+            if (this.opacity < 1.0) {
+                this._defines.TRANSPARENT = true;
+            }
+
+            // Get correct effect      
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+
+                scene.resetCachedMaterial();
+
+                // Attributes
+                var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
+
+                // Effect
+                var shaderName = scene.getEngine().getCaps().standardDerivatives ? "grid" : "legacygrid";
+                
+                // Defines
+                var join = this._defines.toString();
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs,
+                    ["worldViewProjection", "mainColor", "lineColor", "gridControl"],
+                    [],
+                    join, 
+                    null, 
+                    this.onCompiled, 
+                    this.onError);
+            }
+            
+            if (!this._effect.isReady()) {
+                return false;
+            }
+
+            this._renderId = scene.getRenderId();
+            this._wasPreviouslyReady = true;
+
+            return true;
+        }
+        
+        public bindOnlyWorldMatrix(world: Matrix): void {
+            var scene = this.getScene();
+
+            this._effect.setMatrix("worldViewProjection", world.multiply(scene.getTransformMatrix()));
+        }
+
+        public bind(world: Matrix, mesh?: Mesh): void {
+            var scene = this.getScene();
+
+            // Matrices        
+            this.bindOnlyWorldMatrix(world);
+            
+            // Uniforms
+            if (scene.getCachedMaterial() !== (<BABYLON.Material>this)) {
+                this._effect.setColor3("mainColor", this.mainColor);
+                this._effect.setColor3("lineColor", this.lineColor);
+                
+                this._gridControl.x = this.gridRatio;
+                this._gridControl.y = Math.round(this.majorUnitFrequency);
+                this._gridControl.z = this.minorUnitVisibility;
+                this._gridControl.w = this.opacity;
+                this._effect.setVector4("gridControl", this._gridControl);
+            }
+            super.bind(world, mesh);
+        }
+        
+        public dispose(forceDisposeEffect?: boolean): void {
+            super.dispose(forceDisposeEffect);
+        }
+        
+        public clone(name: string): GridMaterial {
+            return SerializationHelper.Clone(() => new GridMaterial(name, this.getScene()), this);
+        }
+
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this); 
+            serializationObject.customType = "BABYLON.GridMaterial"; 
+            return serializationObject;
+        }
+
+        public static Parse(source: any, scene: Scene, rootUrl: string): GridMaterial {
+            return SerializationHelper.Parse(() => new GridMaterial(source.name, scene), source, scene, rootUrl);
+        }
+    }
+}

+ 94 - 0
materialsLibrary/materials/grid/grid.fragment.fx

@@ -0,0 +1,94 @@
+#extension GL_OES_standard_derivatives : enable
+
+#define SQRT2 1.41421356
+#define PI 3.14159
+
+precision highp float;
+
+uniform vec3 mainColor;
+uniform vec3 lineColor;
+uniform vec4 gridControl;
+
+// Varying
+varying vec3 vPosition;
+varying vec3 vNormal;
+
+float getVisibility(float position) {
+    // Major grid line every Frequency defined in material.
+    float majorGridFrequency = gridControl.y;
+    if (floor(position + 0.5) == floor(position / majorGridFrequency + 0.5) * majorGridFrequency)
+    {
+        return 1.0;
+    }  
+    
+    return gridControl.z;
+}
+
+float getAnisotropicAttenuation(float differentialLength) {
+    const float maxNumberOfLines = 10.0;
+    return clamp(1.0 / (differentialLength + 1.0) - 1.0 / maxNumberOfLines, 0.0, 1.0);
+}
+
+float isPointOnLine(float position, float differentialLength) {
+    float fractionPartOfPosition = position - floor(position + 0.5); // fract part around unit [-0.5; 0.5]
+    fractionPartOfPosition /= differentialLength; // adapt to the screen space size it takes
+    fractionPartOfPosition = clamp(fractionPartOfPosition, -1., 1.);
+    
+    float result = 0.5 + 0.5 * cos(fractionPartOfPosition * PI); // Convert to 0-1 for antialiasing.
+    return result;    
+}
+
+float contributionOnAxis(float position) {
+    float differentialLength = length(vec2(dFdx(position), dFdy(position)));
+    differentialLength *= SQRT2;  // Multiply by SQRT2 for diagonal length
+    
+    // Is the point on the line.
+    float result = isPointOnLine(position, differentialLength);
+    
+    // Add dynamic visibility.
+    float visibility = getVisibility(position);
+    result *= visibility;
+    
+    // Anisotropic filtering.
+    float anisotropicAttenuation = getAnisotropicAttenuation(differentialLength);
+    result *= anisotropicAttenuation;
+    
+    return result;
+}
+
+float normalImpactOnAxis(float x) {
+    float normalImpact = clamp(1.0 - 2.8 * abs(x * x * x), 0.0, 1.0);
+    return normalImpact;
+}
+
+void main(void) {
+    
+    // Scale position to the requested ratio.
+    float gridRatio = gridControl.x;
+    vec3 gridPos = vPosition / gridRatio;
+    
+    // Find the contribution of each coords.
+    float x = contributionOnAxis(gridPos.x);
+    float y = contributionOnAxis(gridPos.y);
+    float z = contributionOnAxis(gridPos.z); 
+    
+    // Find the normal contribution.
+    vec3 normal = normalize(vNormal);
+    x *= normalImpactOnAxis(normal.x);
+    y *= normalImpactOnAxis(normal.y);
+    z *= normalImpactOnAxis(normal.z);
+    
+    // Create the grid value by combining axis.
+    float grid = clamp(x + y + z, 0., 1.);
+    
+    // Create the color.
+    vec3 gridColor = mix(mainColor, lineColor, grid);
+
+#ifdef TRANSPARENT
+    float opacity = clamp(grid, 0.08, gridControl.w);
+    gl_FragColor = vec4(gridColor.rgb, opacity);
+#else
+    // Apply the color.
+    gl_FragColor = vec4(gridColor.rgb, 1.0);
+#endif
+}

+ 19 - 0
materialsLibrary/materials/grid/grid.vertex.fx

@@ -0,0 +1,19 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+attribute vec3 normal;
+
+// Uniforms
+uniform mat4 worldViewProjection;
+
+// Varying
+varying vec3 vPosition;
+varying vec3 vNormal;
+
+void main(void) {
+    gl_Position = worldViewProjection * vec4(position, 1.0);
+
+    vPosition = position;
+    vNormal = normal;
+}

+ 3 - 0
materialsLibrary/materials/grid/legacygrid.fragment.fx

@@ -0,0 +1,3 @@
+void main(void) {
+    gl_FragColor = vec4(1, 1, 1, 0.1);
+}

+ 11 - 0
materialsLibrary/materials/grid/legacygrid.vertex.fx

@@ -0,0 +1,11 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+
+// Uniforms
+uniform mat4 worldViewProjection;
+
+void main(void) {
+    gl_Position = worldViewProjection * vec4(position, 1.0);
+}

+ 31 - 0
materialsLibrary/materials/pbr/includes/harmonicsFunctions.fx

@@ -0,0 +1,31 @@
+#ifdef USESPHERICALFROMREFLECTIONMAP
+    uniform vec3 vSphericalX;
+    uniform vec3 vSphericalY;
+    uniform vec3 vSphericalZ;
+    uniform vec3 vSphericalXX;
+    uniform vec3 vSphericalYY;
+    uniform vec3 vSphericalZZ;
+    uniform vec3 vSphericalXY;
+    uniform vec3 vSphericalYZ;
+    uniform vec3 vSphericalZX;
+
+    vec3 EnvironmentIrradiance(vec3 normal)
+    {
+        // Note: 'normal' is assumed to be normalised (or near normalised)
+        // This isn't as critical as it is with other calculations (e.g. specular highlight), but the result will be incorrect nonetheless.
+
+        // TODO: switch to optimal implementation
+        vec3 result =
+            vSphericalX * normal.x +
+            vSphericalY * normal.y +
+            vSphericalZ * normal.z +
+            vSphericalXX * normal.x * normal.x +
+            vSphericalYY * normal.y * normal.y +
+            vSphericalZZ * normal.z * normal.z +
+            vSphericalYZ * normal.y * normal.z +
+            vSphericalZX * normal.z * normal.x +
+            vSphericalXY * normal.x * normal.y;
+
+        return result.rgb;
+    }
+#endif

+ 0 - 18
materialsLibrary/materials/pbr/includes/helperFunctions.fx

@@ -1,18 +0,0 @@
-// PBR HELPER METHODS
-float Square(float value)
-{
-    return value * value;
-}
-
-float getLuminance(vec3 color)
-{
-    return clamp(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0., 1.);
-}
-
-float convertRoughnessToAverageSlope(float roughness)
-{
-    // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
-    const float kMinimumVariance = 0.0005;
-    float alphaG = Square(roughness) + kMinimumVariance;
-    return alphaG;
-}

+ 197 - 0
materialsLibrary/materials/pbr/includes/pbrFunctions.fx

@@ -0,0 +1,197 @@
+// Constants
+#define RECIPROCAL_PI2 0.15915494
+#define FRESNEL_MAXIMUM_ON_ROUGH 0.25
+
+// PBR CUSTOM CONSTANTS
+const float kPi = 3.1415926535897932384626433832795;
+const float kRougnhessToAlphaScale = 0.1;
+const float kRougnhessToAlphaOffset = 0.29248125;
+
+float Square(float value)
+{
+    return value * value;
+}
+
+float getLuminance(vec3 color)
+{
+    return clamp(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0., 1.);
+}
+
+float convertRoughnessToAverageSlope(float roughness)
+{
+    // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
+    const float kMinimumVariance = 0.0005;
+    float alphaG = Square(roughness) + kMinimumVariance;
+    return alphaG;
+}
+
+// Based on Beckamm roughness to Blinn exponent + http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html 
+float getMipMapIndexFromAverageSlope(float maxMipLevel, float alpha)
+{
+    // do not take in account lower mips hence -1... and wait from proper preprocess.
+    // formula comes from approximation of the mathematical solution.
+    //float mip = maxMipLevel + kRougnhessToAlphaOffset + 0.5 * log2(alpha);
+    
+    // In the mean time 
+    // Always [0..1] goes from max mip to min mip in a log2 way.  
+    // Change 5 to nummip below.
+    // http://www.wolframalpha.com/input/?i=x+in+0..1+plot+(+5+%2B+0.3+%2B+0.1+*+5+*+log2(+(1+-+x)+*+(1+-+x)+%2B+0.0005))
+    float mip = kRougnhessToAlphaOffset + maxMipLevel + (maxMipLevel * kRougnhessToAlphaScale * log2(alpha));
+    
+    return clamp(mip, 0., maxMipLevel);
+}
+
+float getMipMapIndexFromAverageSlopeWithPMREM(float maxMipLevel, float alphaG)
+{
+    float specularPower = clamp(2. / alphaG - 2., 0.000001, 2048.);
+    
+    // Based on CubeMapGen for cosine power with 2048 spec default and 0.25 dropoff 
+    return clamp(- 0.5 * log2(specularPower) + 5.5, 0., maxMipLevel);
+}
+
+// From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
+float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
+{
+    float tanSquared = (1.0 - dot * dot) / (dot * dot);
+    return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
+}
+
+float smithVisibilityG_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
+{
+    return smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
+}
+
+// Trowbridge-Reitz (GGX)
+// Generalised Trowbridge-Reitz with gamma power=2.0
+float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
+{
+    // Note: alphaG is average slope (gradient) of the normals in slope-space.
+    // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
+    // a tangent (gradient) closer to the macrosurface than this slope.
+    float a2 = Square(alphaG);
+    float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
+    return a2 / (kPi * d * d);
+}
+
+vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
+{
+    return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0., 1.), 5.0);
+}
+
+vec3 FresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
+{
+    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
+    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
+    return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
+}
+
+// Cook Torance Specular computation.
+vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 specularColor)
+{
+    float alphaG = convertRoughnessToAverageSlope(roughness);
+    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
+    float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
+    visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
+
+    vec3 fresnel = fresnelSchlickGGX(VdotH, specularColor, vec3(1., 1., 1.));
+
+    float specTerm = max(0., visibility * distribution) * NdotL;
+    return fresnel * specTerm * kPi; // TODO: audit pi constants
+}
+
+float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
+{
+    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
+    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
+    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
+    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
+    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
+    float diffuseFresnelTerm =
+        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
+        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
+
+
+    return diffuseFresnelTerm * NdotL;
+    // PI Test
+    // diffuseFresnelTerm /= kPi;
+}
+
+float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
+{
+    // At small angle this approximation works. 
+    float lightRoughness = lightRadius / lightDistance;
+    // Distribution can sum.
+    float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
+    return totalRoughness;
+}
+
+float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
+{
+    float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
+
+    float reflectivityLuminance = getLuminance(reflectivityColor);
+    float reflectivityLuma = sqrt(reflectivityLuminance);
+    microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
+
+    return microSurface;
+}
+
+vec3 toLinearSpace(vec3 color)
+{
+    return vec3(pow(color.r, 2.2), pow(color.g, 2.2), pow(color.b, 2.2));
+}
+
+vec3 toGammaSpace(vec3 color)
+{
+    return vec3(pow(color.r, 1.0 / 2.2), pow(color.g, 1.0 / 2.2), pow(color.b, 1.0 / 2.2));
+}
+
+float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
+{
+    #ifdef USEPHYSICALLIGHTFALLOFF
+        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
+        return lightDistanceFalloff;
+    #else
+        float lightFalloff = max(0., 1.0 - length(lightOffset) / range);
+        return lightFalloff;
+    #endif
+}
+
+#ifdef CAMERATONEMAP
+    vec3 toneMaps(vec3 color)
+    {
+        color = max(color, 0.0);
+
+        // TONE MAPPING / EXPOSURE
+        color.rgb = color.rgb * vCameraInfos.x;
+
+        float tuning = 1.5; // TODO: sync up so e.g. 18% greys are matched to exposure appropriately
+        // PI Test
+        // tuning *=  kPi;
+        vec3 tonemapped = 1.0 - exp2(-color.rgb * tuning); // simple local photographic tonemapper
+        color.rgb = mix(color.rgb, tonemapped, 1.0);
+        return color;
+    }
+#endif
+
+#ifdef CAMERACONTRAST
+    vec4 contrasts(vec4 color)
+    {
+        color = clamp(color, 0.0, 1.0);
+
+        vec3 resultHighContrast = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb);
+        float contrast = vCameraInfos.y;
+        if (contrast < 1.0)
+        {
+            // Decrease contrast: interpolate towards zero-contrast image (flat grey)
+            color.rgb = mix(vec3(0.5, 0.5, 0.5), color.rgb, contrast);
+        }
+        else
+        {
+            // Increase contrast: apply simple shoulder-toe high contrast curve
+            color.rgb = mix(color.rgb, resultHighContrast, contrast - 1.0);
+        }
+
+        return color;
+    }
+#endif

+ 130 - 0
materialsLibrary/materials/pbr/includes/pbrLightFunctions.fx

@@ -0,0 +1,130 @@
+// Light Computing
+struct lightingInfo
+{
+    vec3 diffuse;
+    #ifdef SPECULARTERM
+        vec3 specular;
+    #endif
+};
+
+lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
+    lightingInfo result;
+
+    vec3 lightDirection;
+    float attenuation = 1.0;
+    float lightDistance;
+    
+    // Point
+    if (lightData.w == 0.)
+    {
+        vec3 lightOffset = lightData.xyz - vPositionW;
+        float lightDistanceSquared = dot(lightOffset, lightOffset);
+        attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
+        
+        lightDistance = sqrt(lightDistanceSquared);
+        lightDirection = normalize(lightOffset);
+    }
+    // Directional
+    else
+    {
+        lightDistance = length(-lightData.xyz);
+        lightDirection = normalize(-lightData.xyz);
+    }
+    
+    // Roughness
+    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
+    
+    // diffuse
+    vec3 H = normalize(viewDirectionW + lightDirection);
+    NdotL = max(0.00000000001, dot(vNormal, lightDirection));
+    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
+
+    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
+    result.diffuse = diffuseTerm * diffuseColor * attenuation;
+
+    #ifdef SPECULARTERM
+        // Specular
+        float NdotH = max(0.00000000001, dot(vNormal, H));
+
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+        result.specular = specTerm * attenuation;
+    #endif
+
+    return result;
+}
+
+lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
+    lightingInfo result;
+
+    vec3 lightOffset = lightData.xyz - vPositionW;
+    vec3 lightVectorW = normalize(lightOffset);
+
+    // diffuse
+    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
+    
+    if (cosAngle >= lightDirection.w)
+    {
+        cosAngle = max(0., pow(cosAngle, lightData.w));
+        
+        // Inverse squared falloff.
+        float lightDistanceSquared = dot(lightOffset, lightOffset);
+        float attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
+        
+        // Directional falloff.
+        attenuation *= cosAngle;
+        
+        // Roughness.
+        float lightDistance = sqrt(lightDistanceSquared);
+        roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
+        
+        // Diffuse
+        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
+        NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
+        float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
+
+        float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
+        result.diffuse = diffuseTerm * diffuseColor * attenuation;
+
+        #ifdef SPECULARTERM
+            // Specular
+            float NdotH = max(0.00000000001, dot(vNormal, H));
+
+            vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+            result.specular = specTerm  * attenuation;
+        #endif
+
+        return result;
+    }
+
+    result.diffuse = vec3(0.);
+    #ifdef SPECULARTERM
+        result.specular = vec3(0.);
+    #endif
+
+    return result;
+}
+
+lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, float lightRadius, out float NdotL) {
+    lightingInfo result;
+
+    // Roughness
+    // Do not touch roughness on hemispheric.
+
+    // Diffuse
+    NdotL = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
+    result.diffuse = mix(groundColor, diffuseColor, NdotL);
+
+    #ifdef SPECULARTERM
+        // Specular
+        vec3 lightVectorW = normalize(lightData.xyz);
+        vec3 H = normalize(viewDirectionW + lightVectorW);
+        float NdotH = max(0.00000000001, dot(vNormal, H));
+        NdotL = max(0.00000000001, NdotL);
+        float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
+
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+        result.specular = specTerm;
+    #endif
+
+    return result;
+}

+ 50 - 0
materialsLibrary/materials/pbr/includes/pbrLightFunctionsCall.fx

@@ -0,0 +1,50 @@
+#ifdef LIGHT{X}
+    #ifndef SPECULARTERM
+        vec3 vLightSpecular{X} = vec3(0.0);
+    #endif
+    #ifdef SPOTLIGHT{X}
+        info = computeSpotLighting(viewDirectionW, normalW, vLightData{X}, vLightDirection{X}, vLightDiffuse{X}.rgb, vLightSpecular{X}, vLightDiffuse{X}.a, roughness, NdotV, vLightRadiuses[{X}], NdotL);
+    #endif
+    #ifdef HEMILIGHT{X}
+        info = computeHemisphericLighting(viewDirectionW, normalW, vLightData{X}, vLightDiffuse{X}.rgb, vLightSpecular{X}, vLightGround{X}, roughness, NdotV, vLightRadiuses[{X}], NdotL);
+    #endif
+    #if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
+        info = computeLighting(viewDirectionW, normalW, vLightData{X}, vLightDiffuse{X}.rgb, vLightSpecular{X}, vLightDiffuse{X}.a, roughness, NdotV, vLightRadiuses[{X}], NdotL);
+    #endif
+    
+    #ifdef SHADOW{X}
+        #ifdef SHADOWVSM{X}
+            notShadowLevel = computeShadowWithVSM(vPositionFromLight{X}, shadowSampler{X}, shadowsInfo{X}.z, shadowsInfo{X}.x);
+        #else
+            #ifdef SHADOWPCF{X}
+                #if defined(POINTLIGHT{X})
+                    notShadowLevel = computeShadowWithPCFCube(vLightData{X}.xyz, shadowSampler{X}, shadowsInfo{X}.y, shadowsInfo{X}.z, shadowsInfo{X}.x);
+                #else
+                    notShadowLevel = computeShadowWithPCF(vPositionFromLight{X}, shadowSampler{X}, shadowsInfo{X}.y, shadowsInfo{X}.z, shadowsInfo{X}.x);
+                #endif
+            #else
+                #if defined(POINTLIGHT{X})
+                    notShadowLevel = computeShadowCube(vLightData{X}.xyz, shadowSampler{X}, shadowsInfo{X}.x, shadowsInfo{X}.z);
+                #else
+                    notShadowLevel = computeShadow(vPositionFromLight{X}, shadowSampler{X}, shadowsInfo{X}.x, shadowsInfo{X}.z);
+                #endif
+            #endif
+        #endif
+    #else
+        notShadowLevel = 1.;
+    #endif
+    
+    lightDiffuseContribution += info.diffuse * notShadowLevel;
+    
+    #ifdef OVERLOADEDSHADOWVALUES
+        if (NdotL < 0.000000000011)
+        {
+            notShadowLevel = 1.;
+        }
+        shadowedOnlyLightDiffuseContribution *= notShadowLevel;
+    #endif
+
+    #ifdef SPECULARTERM
+        lightSpecularContribution += info.specular * notShadowLevel;
+    #endif
+#endif

+ 171 - 0
materialsLibrary/materials/pbr/includes/pbrShadowFunctions.fx

@@ -0,0 +1,171 @@
+// Shadows
+#ifdef SHADOWS
+
+    float unpack(vec4 color)
+    {
+        const vec4 bit_shift = vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
+        return dot(color, bit_shift);
+    }
+
+    #if defined(POINTLIGHT0) || defined(POINTLIGHT1) || defined(POINTLIGHT2) || defined(POINTLIGHT3)
+        uniform vec2 depthValues;
+
+        float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float bias)
+        {
+            vec3 directionToLight = vPositionW - lightPosition;
+            float depth = length(directionToLight);
+            depth = clamp(depth, 0., 1.0);
+
+            directionToLight = normalize(directionToLight);
+            directionToLight.y = - directionToLight.y;
+
+            float shadow = unpack(textureCube(shadowSampler, directionToLight)) + bias;
+
+            if (depth > shadow)
+            {
+                #ifdef OVERLOADEDSHADOWVALUES
+                    return mix(1.0, darkness, vOverloadedShadowIntensity.x);
+                #else
+                    return darkness;
+                #endif
+            }
+            return 1.0;
+        }
+
+        float computeShadowWithPCFCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float bias, float darkness)
+        {
+            vec3 directionToLight = vPositionW - lightPosition;
+            float depth = length(directionToLight);
+
+            depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+            depth = clamp(depth, 0., 1.0);
+
+            directionToLight = normalize(directionToLight);
+            directionToLight.y = -directionToLight.y;
+
+            float visibility = 1.;
+
+            vec3 poissonDisk[4];
+            poissonDisk[0] = vec3(-1.0, 1.0, -1.0);
+            poissonDisk[1] = vec3(1.0, -1.0, -1.0);
+            poissonDisk[2] = vec3(-1.0, -1.0, -1.0);
+            poissonDisk[3] = vec3(1.0, -1.0, 1.0);
+
+            // Poisson Sampling
+            float biasedDepth = depth - bias;
+
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
+
+            #ifdef OVERLOADEDSHADOWVALUES
+                return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
+            #else
+                return  min(1.0, visibility + darkness);
+            #endif
+        }
+    #endif
+
+    #if defined(SPOTLIGHT0) || defined(SPOTLIGHT1) || defined(SPOTLIGHT2) || defined(SPOTLIGHT3) ||  defined(DIRLIGHT0) || defined(DIRLIGHT1) || defined(DIRLIGHT2) || defined(DIRLIGHT3)
+        float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness, float bias)
+        {
+            vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
+            depth = 0.5 * depth + vec3(0.5);
+            vec2 uv = depth.xy;
+
+            if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            float shadow = unpack(texture2D(shadowSampler, uv)) + bias;
+
+            if (depth.z > shadow)
+            {
+                #ifdef OVERLOADEDSHADOWVALUES
+                    return mix(1.0, darkness, vOverloadedShadowIntensity.x);
+                #else
+                    return darkness;
+                #endif
+            }
+            return 1.;
+        }
+
+        float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler, float mapSize, float bias, float darkness)
+        {
+            vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
+            depth = 0.5 * depth + vec3(0.5);
+            vec2 uv = depth.xy;
+
+            if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            float visibility = 1.;
+
+            vec2 poissonDisk[4];
+            poissonDisk[0] = vec2(-0.94201624, -0.39906216);
+            poissonDisk[1] = vec2(0.94558609, -0.76890725);
+            poissonDisk[2] = vec2(-0.094184101, -0.92938870);
+            poissonDisk[3] = vec2(0.34495938, 0.29387760);
+
+            // Poisson Sampling
+            float biasedDepth = depth.z - bias;
+
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
+
+            #ifdef OVERLOADEDSHADOWVALUES
+                return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
+            #else
+                return  min(1.0, visibility + darkness);
+            #endif
+        }
+
+        // Thanks to http://devmaster.net/
+        float unpackHalf(vec2 color)
+        {
+            return color.x + (color.y / 255.0);
+        }
+
+        float linstep(float low, float high, float v) {
+            return clamp((v - low) / (high - low), 0.0, 1.0);
+        }
+
+        float ChebychevInequality(vec2 moments, float compare, float bias)
+        {
+            float p = smoothstep(compare - bias, compare, moments.x);
+            float variance = max(moments.y - moments.x * moments.x, 0.02);
+            float d = compare - moments.x;
+            float p_max = linstep(0.2, 1.0, variance / (variance + d * d));
+
+            return clamp(max(p, p_max), 0.0, 1.0);
+        }
+
+        float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler, float bias, float darkness)
+        {
+            vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
+            depth = 0.5 * depth + vec3(0.5);
+            vec2 uv = depth.xy;
+
+            if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0 || depth.z >= 1.0)
+            {
+                return 1.0;
+            }
+
+            vec4 texel = texture2D(shadowSampler, uv);
+
+            vec2 moments = vec2(unpackHalf(texel.xy), unpackHalf(texel.zw));
+            #ifdef OVERLOADEDSHADOWVALUES
+                return min(1.0, mix(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness, vOverloadedShadowIntensity.x));
+            #else
+                return min(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness);
+            #endif
+        }
+    #endif
+
+#endif

+ 9 - 420
materialsLibrary/materials/pbr/legacypbr.fragment.fx

@@ -28,185 +28,6 @@ uniform vec3 vOverloadedMicroSurface;
 uniform vec4 vOverloadedShadowIntensity;
 #endif
 
-// PBR CUSTOM CONSTANTS
-const float kPi = 3.1415926535897932384626433832795;
-const float kRougnhessToAlphaScale = 0.1;
-const float kRougnhessToAlphaOffset = 0.29248125;
-
-#include<helperFunctions>
-
-// Based on Beckamm roughness to Blinn exponent + http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html 
-float getMipMapIndexFromAverageSlope(float maxMipLevel, float alpha)
-{
-    // do not take in account lower mips hence -1... and wait from proper preprocess.
-    // formula comes from approximation of the mathematical solution.
-    //float mip = maxMipLevel + kRougnhessToAlphaOffset + 0.5 * log2(alpha);
-    
-    // In the mean time 
-    // Always [0..1] goes from max mip to min mip in a log2 way.  
-    // Change 5 to nummip below.
-    // http://www.wolframalpha.com/input/?i=x+in+0..1+plot+(+5+%2B+0.3+%2B+0.1+*+5+*+log2(+(1+-+x)+*+(1+-+x)+%2B+0.0005))
-    float mip = kRougnhessToAlphaOffset + maxMipLevel + (maxMipLevel * kRougnhessToAlphaScale * log2(alpha));
-    
-    return clamp(mip, 0., maxMipLevel);
-}
-
-float getMipMapIndexFromAverageSlopeWithPMREM(float maxMipLevel, float alphaG)
-{
-    float specularPower = clamp(2. / alphaG - 2., 0.000001, 2048.);
-    
-    // Based on CubeMapGen for cosine power with 2048 spec default and 0.25 dropoff 
-    return clamp(- 0.5 * log2(specularPower) + 5.5, 0., maxMipLevel);
-}
-
-// From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
-float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
-{
-    float tanSquared = (1.0 - dot * dot) / (dot * dot);
-    return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
-}
-
-float smithVisibilityG_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
-{
-    return smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
-}
-
-// Trowbridge-Reitz (GGX)
-// Generalised Trowbridge-Reitz with gamma power=2.0
-float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
-{
-    // Note: alphaG is average slope (gradient) of the normals in slope-space.
-    // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
-    // a tangent (gradient) closer to the macrosurface than this slope.
-    float a2 = Square(alphaG);
-    float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
-    return a2 / (kPi * d * d);
-}
-
-vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
-{
-    return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0., 1.), 5.0);
-}
-
-vec3 FresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
-{
-    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
-    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
-    return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
-}
-
-// Cook Torance Specular computation.
-vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 specularColor)
-{
-    float alphaG = convertRoughnessToAverageSlope(roughness);
-    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
-    float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
-    visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
-
-    vec3 fresnel = fresnelSchlickGGX(VdotH, specularColor, vec3(1., 1., 1.));
-
-    float specTerm = max(0., visibility * distribution) * NdotL;
-    return fresnel * specTerm * kPi; // TODO: audit pi constants
-}
-
-float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
-{
-    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
-    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
-    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
-    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
-    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
-    float diffuseFresnelTerm =
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
-
-
-    return diffuseFresnelTerm * NdotL;
-    // PI Test
-    // diffuseFresnelTerm /= kPi;
-}
-
-float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
-{
-    // At small angle this approximation works. 
-    float lightRoughness = lightRadius / lightDistance;
-    // Distribution can sum.
-    float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
-    return totalRoughness;
-}
-
-float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
-{
-    float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
-
-    float reflectivityLuminance = getLuminance(reflectivityColor);
-    float reflectivityLuma = sqrt(reflectivityLuminance);
-    microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
-
-    return microSurface;
-}
-
-vec3 toLinearSpace(vec3 color)
-{
-    return vec3(pow(color.r, 2.2), pow(color.g, 2.2), pow(color.b, 2.2));
-}
-
-vec3 toGammaSpace(vec3 color)
-{
-    return vec3(pow(color.r, 1.0 / 2.2), pow(color.g, 1.0 / 2.2), pow(color.b, 1.0 / 2.2));
-}
-
-float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
-{
-    #ifdef USEPHYSICALLIGHTFALLOFF
-        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
-        return lightDistanceFalloff;
-    #else
-        float lightFalloff = max(0., 1.0 - length(lightOffset) / range);
-        return lightFalloff;
-    #endif
-}
-
-#ifdef CAMERATONEMAP
-    vec3 toneMaps(vec3 color)
-    {
-        color = max(color, 0.0);
-
-        // TONE MAPPING / EXPOSURE
-        color.rgb = color.rgb * vCameraInfos.x;
-
-        float tuning = 1.5; // TODO: sync up so e.g. 18% greys are matched to exposure appropriately
-        // PI Test
-        // tuning *=  kPi;
-        vec3 tonemapped = 1.0 - exp2(-color.rgb * tuning); // simple local photographic tonemapper
-        color.rgb = mix(color.rgb, tonemapped, 1.0);
-        return color;
-    }
-#endif
-
-#ifdef CAMERACONTRAST
-    vec4 contrasts(vec4 color)
-    {
-        color = clamp(color, 0.0, 1.0);
-
-        vec3 resultHighContrast = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb);
-        float contrast = vCameraInfos.y;
-        if (contrast < 1.0)
-        {
-            // Decrease contrast: interpolate towards zero-contrast image (flat grey)
-            color.rgb = mix(vec3(0.5, 0.5, 0.5), color.rgb, contrast);
-        }
-        else
-        {
-            // Increase contrast: apply simple shoulder-toe high contrast curve
-            color.rgb = mix(color.rgb, resultHighContrast, contrast - 1.0);
-        }
-
-        return color;
-    }
-#endif
-// END PBR HELPER METHODS
-
 uniform vec4 vReflectivityColor;
 uniform vec3 vEmissiveColor;
 
@@ -266,136 +87,10 @@ uniform sampler2D reflectivitySampler;
 
 #include<clipPlaneFragmentDeclaration>
 
-// Light Computing
-struct lightingInfo
-{
-    vec3 diffuse;
-#ifdef SPECULARTERM
-    vec3 specular;
-#endif
-};
-
-lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightDirection;
-    float attenuation = 1.0;
-    float lightDistance;
-    
-    // Point
-    if (lightData.w == 0.)
-    {
-        vec3 lightOffset = lightData.xyz - vPositionW;
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        lightDistance = sqrt(lightDistanceSquared);
-        lightDirection = normalize(lightOffset);
-    }
-    // Directional
-    else
-    {
-        lightDistance = length(-lightData.xyz);
-        lightDirection = normalize(-lightData.xyz);
-    }
-    
-    // Roughness
-    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-    
-    // diffuse
-    vec3 H = normalize(viewDirectionW + lightDirection);
-    NdotL = max(0.00000000001, dot(vNormal, lightDirection));
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-    result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-    // Specular
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm * attenuation;
-#endif
-
-    return result;
-}
-
-lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightOffset = lightData.xyz - vPositionW;
-    vec3 lightVectorW = normalize(lightOffset);
-
-    // diffuse
-    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
-    
-    if (cosAngle >= lightDirection.w)
-    {
-        cosAngle = max(0., pow(cosAngle, lightData.w));
-        
-        // Inverse squared falloff.
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        float attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        // Directional falloff.
-        attenuation *= cosAngle;
-        
-        // Roughness.
-        float lightDistance = sqrt(lightDistanceSquared);
-        roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-        
-        // Diffuse
-        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
-        NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
-        float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
-
-        float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-        result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-        // Specular
-        float NdotH = max(0.00000000001, dot(vNormal, H));
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-        result.specular = specTerm  * attenuation;
-#endif
-
-        return result;
-    }
-
-    result.diffuse = vec3(0.);
-#ifdef SPECULARTERM
-    result.specular = vec3(0.);
-#endif
-
-    return result;
-}
-
-lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    // Roughness
-    // Do not touch roughness on hemispheric.
-
-    // Diffuse
-    NdotL = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
-    result.diffuse = mix(groundColor, diffuseColor, NdotL);
-
-#ifdef SPECULARTERM
-    // Specular
-    vec3 lightVectorW = normalize(lightData.xyz);
-    vec3 H = normalize(viewDirectionW + lightVectorW);
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-    NdotL = max(0.00000000001, NdotL);
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm;
-#endif
-
-    return result;
-}
+// PBR
+#include<pbrFunctions>
+#include<harmonicsFunctions>
+#include<pbrLightFunctions>
 
 void main(void) {
 #include<clipPlaneFragment>
@@ -506,118 +201,12 @@ void main(void) {
 #endif
     float notShadowLevel = 1.; // 1 - shadowLevel
     float NdotL = -1.;
+    lightingInfo info;
 
-#ifdef LIGHT0
-#ifndef SPECULARTERM
-    vec3 vLightSpecular0 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT0
-    lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#ifdef HEMILIGHT0
-    lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightGround0, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#if defined(POINTLIGHT0) || defined(DIRLIGHT0)
-    lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT1
-#ifndef SPECULARTERM
-    vec3 vLightSpecular1 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT1
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData1, vLightDirection1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#ifdef HEMILIGHT1
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightGround1, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#if defined(POINTLIGHT1) || defined(DIRLIGHT1)
-    info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT2
-#ifndef SPECULARTERM
-    vec3 vLightSpecular2 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT2
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData2, vLightDirection2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#ifdef HEMILIGHT2
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightGround2, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#if defined(POINTLIGHT2) || defined(DIRLIGHT2)
-    info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT3
-#ifndef SPECULARTERM
-    vec3 vLightSpecular3 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT3
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData3, vLightDirection3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#ifdef HEMILIGHT3
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightGround3, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#if defined(POINTLIGHT3) || defined(DIRLIGHT3)
-    info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
+#include<pbrLightFunctionsCall>[0]
+#include<pbrLightFunctionsCall>[1]
+#include<pbrLightFunctionsCall>[2]
+#include<pbrLightFunctionsCall>[3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 12 - 713
materialsLibrary/materials/pbr/pbr.fragment.fx

@@ -12,10 +12,6 @@
 
 precision highp float;
 
-// Constants
-#define RECIPROCAL_PI2 0.15915494
-#define FRESNEL_MAXIMUM_ON_ROUGH 0.25
-
 uniform vec3 vEyePosition;
 uniform vec3 vAmbientColor;
 uniform vec3 vReflectionColor;
@@ -40,223 +36,12 @@ uniform vec4 vCameraInfos;
     uniform vec4 vOverloadedShadowIntensity;
 #endif
 
-#ifdef USESPHERICALFROMREFLECTIONMAP
-    uniform vec3 vSphericalX;
-    uniform vec3 vSphericalY;
-    uniform vec3 vSphericalZ;
-    uniform vec3 vSphericalXX;
-    uniform vec3 vSphericalYY;
-    uniform vec3 vSphericalZZ;
-    uniform vec3 vSphericalXY;
-    uniform vec3 vSphericalYZ;
-    uniform vec3 vSphericalZX;
-
-    vec3 EnvironmentIrradiance(vec3 normal)
-    {
-        // Note: 'normal' is assumed to be normalised (or near normalised)
-        // This isn't as critical as it is with other calculations (e.g. specular highlight), but the result will be incorrect nonetheless.
-
-        // TODO: switch to optimal implementation
-        vec3 result =
-            vSphericalX * normal.x +
-            vSphericalY * normal.y +
-            vSphericalZ * normal.z +
-            vSphericalXX * normal.x * normal.x +
-            vSphericalYY * normal.y * normal.y +
-            vSphericalZZ * normal.z * normal.z +
-            vSphericalYZ * normal.y * normal.z +
-            vSphericalZX * normal.z * normal.x +
-            vSphericalXY * normal.x * normal.y;
-
-        return result.rgb;
-    }
-#endif
-
 #if defined(REFLECTION) || defined(REFRACTION)
     uniform vec2 vMicrosurfaceTextureLods;
 #endif
 
-// PBR CUSTOM CONSTANTS
-const float kPi = 3.1415926535897932384626433832795;
-const float kRougnhessToAlphaScale = 0.1;
-const float kRougnhessToAlphaOffset = 0.29248125;
-
-#include<helperFunctions>
-
-// Based on Beckamm roughness to Blinn exponent + http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html 
-float getMipMapIndexFromAverageSlope(float maxMipLevel, float alpha)
-{
-    // do not take in account lower mips hence -1... and wait from proper preprocess.
-    // formula comes from approximation of the mathematical solution.
-    //float mip = maxMipLevel + kRougnhessToAlphaOffset + 0.5 * log2(alpha);
-    
-    // In the mean time 
-    // Always [0..1] goes from max mip to min mip in a log2 way.  
-    // Change 5 to nummip below.
-    // http://www.wolframalpha.com/input/?i=x+in+0..1+plot+(+5+%2B+0.3+%2B+0.1+*+5+*+log2(+(1+-+x)+*+(1+-+x)+%2B+0.0005))
-    float mip = kRougnhessToAlphaOffset + maxMipLevel + (maxMipLevel * kRougnhessToAlphaScale * log2(alpha));
-    
-    return clamp(mip, 0., maxMipLevel);
-}
-
-float getMipMapIndexFromAverageSlopeWithPMREM(float maxMipLevel, float alphaG)
-{
-    float specularPower = clamp(2. / alphaG - 2., 0.000001, 2048.);
-    
-    // Based on CubeMapGen for cosine power with 2048 spec default and 0.25 dropoff 
-    return clamp(- 0.5 * log2(specularPower) + 5.5, 0., maxMipLevel);
-}
-
-// From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
-float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
-{
-    float tanSquared = (1.0 - dot * dot) / (dot * dot);
-    return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
-}
-
-float smithVisibilityG_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
-{
-    return smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
-}
-
-// Trowbridge-Reitz (GGX)
-// Generalised Trowbridge-Reitz with gamma power=2.0
-float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
-{
-    // Note: alphaG is average slope (gradient) of the normals in slope-space.
-    // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
-    // a tangent (gradient) closer to the macrosurface than this slope.
-    float a2 = Square(alphaG);
-    float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
-    return a2 / (kPi * d * d);
-}
-
-vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
-{
-    return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0., 1.), 5.0);
-}
-
-vec3 FresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
-{
-    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
-    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
-    return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
-}
-
-// Cook Torance Specular computation.
-vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 specularColor)
-{
-    float alphaG = convertRoughnessToAverageSlope(roughness);
-    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
-    float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
-    visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
-
-    vec3 fresnel = fresnelSchlickGGX(VdotH, specularColor, vec3(1., 1., 1.));
-
-    float specTerm = max(0., visibility * distribution) * NdotL;
-    return fresnel * specTerm * kPi; // TODO: audit pi constants
-}
-
-float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
-{
-    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
-    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
-    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
-    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
-    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
-    float diffuseFresnelTerm =
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
-
-
-    return diffuseFresnelTerm * NdotL;
-    // PI Test
-    // diffuseFresnelTerm /= kPi;
-}
-
-float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
-{
-    // At small angle this approximation works. 
-    float lightRoughness = lightRadius / lightDistance;
-    // Distribution can sum.
-    float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
-    return totalRoughness;
-}
-
-float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
-{
-    float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
-
-    float reflectivityLuminance = getLuminance(reflectivityColor);
-    float reflectivityLuma = sqrt(reflectivityLuminance);
-    microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
-
-    return microSurface;
-}
-
-vec3 toLinearSpace(vec3 color)
-{
-    return vec3(pow(color.r, 2.2), pow(color.g, 2.2), pow(color.b, 2.2));
-}
-
-vec3 toGammaSpace(vec3 color)
-{
-    return vec3(pow(color.r, 1.0 / 2.2), pow(color.g, 1.0 / 2.2), pow(color.b, 1.0 / 2.2));
-}
-
-float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
-{
-    #ifdef USEPHYSICALLIGHTFALLOFF
-        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
-        return lightDistanceFalloff;
-    #else
-        float lightFalloff = max(0., 1.0 - length(lightOffset) / range);
-        return lightFalloff;
-    #endif
-}
-
-#ifdef CAMERATONEMAP
-    vec3 toneMaps(vec3 color)
-    {
-        color = max(color, 0.0);
-
-        // TONE MAPPING / EXPOSURE
-        color.rgb = color.rgb * vCameraInfos.x;
-
-        float tuning = 1.5; // TODO: sync up so e.g. 18% greys are matched to exposure appropriately
-        // PI Test
-        // tuning *=  kPi;
-        vec3 tonemapped = 1.0 - exp2(-color.rgb * tuning); // simple local photographic tonemapper
-        color.rgb = mix(color.rgb, tonemapped, 1.0);
-        return color;
-    }
-#endif
-
-#ifdef CAMERACONTRAST
-    vec4 contrasts(vec4 color)
-    {
-        color = clamp(color, 0.0, 1.0);
-
-        vec3 resultHighContrast = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb);
-        float contrast = vCameraInfos.y;
-        if (contrast < 1.0)
-        {
-            // Decrease contrast: interpolate towards zero-contrast image (flat grey)
-            color.rgb = mix(vec3(0.5, 0.5, 0.5), color.rgb, contrast);
-        }
-        else
-        {
-            // Increase contrast: apply simple shoulder-toe high contrast curve
-            color.rgb = mix(color.rgb, resultHighContrast, contrast - 1.0);
-        }
-
-        return color;
-    }
-#endif
-// END PBR HELPER METHODS
-
-    uniform vec4 vReflectivityColor;
-    uniform vec3 vEmissiveColor;
+uniform vec4 vReflectivityColor;
+uniform vec3 vEmissiveColor;
 
 // Input
 varying vec3 vPositionW;
@@ -367,177 +152,11 @@ varying vec3 vPositionUVW;
 
 #endif
 
-// Shadows
-#ifdef SHADOWS
-
-float unpack(vec4 color)
-{
-    const vec4 bit_shift = vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
-    return dot(color, bit_shift);
-}
-
-#if defined(POINTLIGHT0) || defined(POINTLIGHT1) || defined(POINTLIGHT2) || defined(POINTLIGHT3)
-uniform vec2 depthValues;
-
-float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float bias)
-{
-	vec3 directionToLight = vPositionW - lightPosition;
-	float depth = length(directionToLight);
-	depth = clamp(depth, 0., 1.0);
-
-	directionToLight = normalize(directionToLight);
-	directionToLight.y = - directionToLight.y;
-
-	float shadow = unpack(textureCube(shadowSampler, directionToLight)) + bias;
-
-    if (depth > shadow)
-    {
-#ifdef OVERLOADEDSHADOWVALUES
-        return mix(1.0, darkness, vOverloadedShadowIntensity.x);
-#else
-        return darkness;
-#endif
-    }
-    return 1.0;
-}
-
-float computeShadowWithPCFCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float bias, float darkness)
-{
-    vec3 directionToLight = vPositionW - lightPosition;
-    float depth = length(directionToLight);
-
-    depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
-    depth = clamp(depth, 0., 1.0);
-
-    directionToLight = normalize(directionToLight);
-    directionToLight.y = -directionToLight.y;
-
-    float visibility = 1.;
-
-    vec3 poissonDisk[4];
-    poissonDisk[0] = vec3(-1.0, 1.0, -1.0);
-    poissonDisk[1] = vec3(1.0, -1.0, -1.0);
-    poissonDisk[2] = vec3(-1.0, -1.0, -1.0);
-    poissonDisk[3] = vec3(1.0, -1.0, 1.0);
-
-    // Poisson Sampling
-    float biasedDepth = depth - bias;
-
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
-
-#ifdef OVERLOADEDSHADOWVALUES
-    return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
-#else
-    return  min(1.0, visibility + darkness);
-#endif
-}
-#endif
-
-#if defined(SPOTLIGHT0) || defined(SPOTLIGHT1) || defined(SPOTLIGHT2) || defined(SPOTLIGHT3) ||  defined(DIRLIGHT0) || defined(DIRLIGHT1) || defined(DIRLIGHT2) || defined(DIRLIGHT3)
-float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness, float bias)
-{
-    vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-    depth = 0.5 * depth + vec3(0.5);
-    vec2 uv = depth.xy;
-
-    if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
-    {
-        return 1.0;
-    }
-
-    float shadow = unpack(texture2D(shadowSampler, uv)) + bias;
-
-    if (depth.z > shadow)
-    {
-#ifdef OVERLOADEDSHADOWVALUES
-        return mix(1.0, darkness, vOverloadedShadowIntensity.x);
-#else
-        return darkness;
-#endif
-    }
-    return 1.;
-}
-
-float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler, float mapSize, float bias, float darkness)
-{
-    vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-    depth = 0.5 * depth + vec3(0.5);
-    vec2 uv = depth.xy;
-
-    if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
-    {
-        return 1.0;
-    }
-
-    float visibility = 1.;
-
-    vec2 poissonDisk[4];
-    poissonDisk[0] = vec2(-0.94201624, -0.39906216);
-    poissonDisk[1] = vec2(0.94558609, -0.76890725);
-    poissonDisk[2] = vec2(-0.094184101, -0.92938870);
-    poissonDisk[3] = vec2(0.34495938, 0.29387760);
-
-    // Poisson Sampling
-    float biasedDepth = depth.z - bias;
-
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
-
-#ifdef OVERLOADEDSHADOWVALUES
-    return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
-#else
-    return  min(1.0, visibility + darkness);
-#endif
-}
-
-// Thanks to http://devmaster.net/
-float unpackHalf(vec2 color)
-{
-    return color.x + (color.y / 255.0);
-}
-
-float linstep(float low, float high, float v) {
-    return clamp((v - low) / (high - low), 0.0, 1.0);
-}
-
-float ChebychevInequality(vec2 moments, float compare, float bias)
-{
-    float p = smoothstep(compare - bias, compare, moments.x);
-    float variance = max(moments.y - moments.x * moments.x, 0.02);
-    float d = compare - moments.x;
-    float p_max = linstep(0.2, 1.0, variance / (variance + d * d));
-
-    return clamp(max(p, p_max), 0.0, 1.0);
-}
-
-float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler, float bias, float darkness)
-{
-    vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-    depth = 0.5 * depth + vec3(0.5);
-    vec2 uv = depth.xy;
-
-    if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0 || depth.z >= 1.0)
-    {
-        return 1.0;
-    }
-
-    vec4 texel = texture2D(shadowSampler, uv);
-
-    vec2 moments = vec2(unpackHalf(texel.xy), unpackHalf(texel.zw));
-#ifdef OVERLOADEDSHADOWVALUES
-    return min(1.0, mix(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness, vOverloadedShadowIntensity.x));
-#else
-    return min(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness);
-#endif
-}
-#endif
-
-#endif
+// PBR
+#include<pbrShadowFunctions>
+#include<pbrFunctions>
+#include<harmonicsFunctions>
+#include<pbrLightFunctions>
 
 #include<bumpFragmentFunctions>
 #include<clipPlaneFragmentDeclaration>
@@ -546,137 +165,6 @@ float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler, flo
 // Fog
 #include<fogFragmentDeclaration>
 
-// Light Computing
-struct lightingInfo
-{
-    vec3 diffuse;
-#ifdef SPECULARTERM
-    vec3 specular;
-#endif
-};
-
-lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightDirection;
-    float attenuation = 1.0;
-    float lightDistance;
-    
-    // Point
-    if (lightData.w == 0.)
-    {
-        vec3 lightOffset = lightData.xyz - vPositionW;
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        lightDistance = sqrt(lightDistanceSquared);
-        lightDirection = normalize(lightOffset);
-    }
-    // Directional
-    else
-    {
-        lightDistance = length(-lightData.xyz);
-        lightDirection = normalize(-lightData.xyz);
-    }
-    
-    // Roughness
-    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-    
-    // diffuse
-    vec3 H = normalize(viewDirectionW + lightDirection);
-    NdotL = max(0.00000000001, dot(vNormal, lightDirection));
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-    result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-    // Specular
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm * attenuation;
-#endif
-
-    return result;
-}
-
-lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightOffset = lightData.xyz - vPositionW;
-    vec3 lightVectorW = normalize(lightOffset);
-
-    // diffuse
-    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
-    
-    if (cosAngle >= lightDirection.w)
-    {
-        cosAngle = max(0., pow(cosAngle, lightData.w));
-        
-        // Inverse squared falloff.
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        float attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        // Directional falloff.
-        attenuation *= cosAngle;
-        
-        // Roughness.
-        float lightDistance = sqrt(lightDistanceSquared);
-        roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-        
-        // Diffuse
-        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
-        NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
-        float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
-
-        float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-        result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-        // Specular
-        float NdotH = max(0.00000000001, dot(vNormal, H));
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-        result.specular = specTerm  * attenuation;
-#endif
-
-        return result;
-    }
-
-    result.diffuse = vec3(0.);
-#ifdef SPECULARTERM
-    result.specular = vec3(0.);
-#endif
-
-    return result;
-}
-
-lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    // Roughness
-    // Do not touch roughness on hemispheric.
-
-    // Diffuse
-    NdotL = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
-    result.diffuse = mix(groundColor, diffuseColor, NdotL);
-
-#ifdef SPECULARTERM
-    // Specular
-    vec3 lightVectorW = normalize(lightData.xyz);
-    vec3 H = normalize(viewDirectionW + lightVectorW);
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-    NdotL = max(0.00000000001, NdotL);
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm;
-#endif
-
-    return result;
-}
-
 void main(void) {
 #include<clipPlaneFragment>
 
@@ -790,201 +278,12 @@ void main(void) {
 #endif
     float notShadowLevel = 1.; // 1 - shadowLevel
     float NdotL = -1.;
+    lightingInfo info;
 
-#ifdef LIGHT0
-#ifndef SPECULARTERM
-    vec3 vLightSpecular0 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT0
-    lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#ifdef HEMILIGHT0
-    lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightGround0, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#if defined(POINTLIGHT0) || defined(DIRLIGHT0)
-    lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#ifdef SHADOW0
-#ifdef SHADOWVSM0
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight0, shadowSampler0, shadowsInfo0.z, shadowsInfo0.x);
-#else
-#ifdef SHADOWPCF0
-#if defined(POINTLIGHT0)
-    notShadowLevel = computeShadowWithPCFCube(vLightData0.xyz, shadowSampler0, shadowsInfo0.y, shadowsInfo0.z, shadowsInfo0.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight0, shadowSampler0, shadowsInfo0.y, shadowsInfo0.z, shadowsInfo0.x);
-#endif
-#else
-#if defined(POINTLIGHT0)
-    notShadowLevel = computeShadowCube(vLightData0.xyz, shadowSampler0, shadowsInfo0.x, shadowsInfo0.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight0, shadowSampler0, shadowsInfo0.x, shadowsInfo0.z);
-#endif
-#endif
-#endif
-#else
-    notShadowLevel = 1.;
-#endif
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT1
-#ifndef SPECULARTERM
-    vec3 vLightSpecular1 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT1
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData1, vLightDirection1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#ifdef HEMILIGHT1
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightGround1, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#if defined(POINTLIGHT1) || defined(DIRLIGHT1)
-    info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#ifdef SHADOW1
-#ifdef SHADOWVSM1
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight1, shadowSampler1, shadowsInfo1.z, shadowsInfo1.x);
-#else
-#ifdef SHADOWPCF1
-#if defined(POINTLIGHT1)
-    notShadowLevel = computeShadowWithPCFCube(vLightData1.xyz, shadowSampler1, shadowsInfo1.y, shadowsInfo1.z, shadowsInfo1.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight1, shadowSampler1, shadowsInfo1.y, shadowsInfo1.z, shadowsInfo1.x);
-#endif
-#else
-#if defined(POINTLIGHT1)
-    notShadowLevel = computeShadowCube(vLightData1.xyz, shadowSampler1, shadowsInfo1.x, shadowsInfo1.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight1, shadowSampler1, shadowsInfo1.x, shadowsInfo1.z);
-#endif
-#endif
-#endif
-#else
-    notShadowLevel = 1.;
-#endif
-
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT2
-#ifndef SPECULARTERM
-    vec3 vLightSpecular2 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT2
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData2, vLightDirection2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#ifdef HEMILIGHT2
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightGround2, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#if defined(POINTLIGHT2) || defined(DIRLIGHT2)
-    info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#ifdef SHADOW2
-#ifdef SHADOWVSM2
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight2, shadowSampler2, shadowsInfo2.z, shadowsInfo2.x);
-#else
-#ifdef SHADOWPCF2
-#if defined(POINTLIGHT2)
-    notShadowLevel = computeShadowWithPCFCube(vLightData2.xyz, shadowSampler2, shadowsInfo2.y, shadowsInfo2.z, shadowsInfo2.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight2, shadowSampler2, shadowsInfo2.y, shadowsInfo2.z, shadowsInfo2.x);
-#endif
-#else
-#if defined(POINTLIGHT2)
-    notShadowLevel = computeShadowCube(vLightData2.xyz, shadowSampler2, shadowsInfo2.x, shadowsInfo2.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight2, shadowSampler2, shadowsInfo2.x, shadowsInfo2.z);
-#endif
-#endif	
-#endif	
-#else
-    notShadowLevel = 1.;
-#endif
-
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT3
-#ifndef SPECULARTERM
-    vec3 vLightSpecular3 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT3
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData3, vLightDirection3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#ifdef HEMILIGHT3
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightGround3, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#if defined(POINTLIGHT3) || defined(DIRLIGHT3)
-    info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#ifdef SHADOW3
-#ifdef SHADOWVSM3
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight3, shadowSampler3, shadowsInfo3.z, shadowsInfo3.x);
-#else
-#ifdef SHADOWPCF3
-#if defined(POINTLIGHT3)
-    notShadowLevel = computeShadowWithPCFCube(vLightData3.xyz, shadowSampler3, shadowsInfo3.y, shadowsInfo3.z, shadowsInfo3.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight3, shadowSampler3, shadowsInfo3.y, shadowsInfo3.z, shadowsInfo3.x);
-#endif
-#else
-#if defined(POINTLIGHT3)
-    notShadowLevel = computeShadowCube(vLightData3.xyz, shadowSampler3, shadowsInfo3.x, shadowsInfo3.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight3, shadowSampler3, shadowsInfo3.x, shadowsInfo3.z);
-#endif
-#endif	
-#endif	
-#else
-    notShadowLevel = 1.;
-#endif
-
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
+#include<pbrLightFunctionsCall>[0]
+#include<pbrLightFunctionsCall>[1]
+#include<pbrLightFunctionsCall>[2]
+#include<pbrLightFunctionsCall>[3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 1 - 1
materialsLibrary/materials/pbr/pbr.vertex.fx

@@ -61,7 +61,7 @@ uniform mat4 reflectivityMatrix;
 
 #ifdef BUMP
 varying vec2 vBumpUV;
-uniform vec2 vBumpInfos;
+uniform vec3 vBumpInfos;
 uniform mat4 bumpMatrix;
 #endif
 

+ 65 - 0
materialsLibrary/test/add/addgrid.js

@@ -0,0 +1,65 @@
+window.prepareGrid = function() {
+	var grid = new BABYLON.GridMaterial("grid", scene);
+
+	registerRangeUI("grid", "LineColorR", 0, 1, function(value) {
+		grid.lineColor.r = value;
+	}, function() {
+		return grid.lineColor.r;
+	});
+    
+    registerRangeUI("grid", "LineColorG", 0, 1, function(value) {
+		grid.lineColor.g = value;
+	}, function() {
+		return grid.lineColor.g;
+	});
+    
+    registerRangeUI("grid", "LineColorB", 0, 1, function(value) {
+		grid.lineColor.b = value;
+	}, function() {
+		return grid.lineColor.b;
+	});
+    
+    registerRangeUI("grid", "MainColorR", 0, 1, function(value) {
+		grid.mainColor.r = value;
+	}, function() {
+		return grid.mainColor.r;
+	});
+    
+    registerRangeUI("grid", "MainColorG", 0, 1, function(value) {
+		grid.mainColor.g = value;
+	}, function() {
+		return grid.mainColor.g;
+	});
+    
+    registerRangeUI("grid", "MainColorB", 0, 1, function(value) {
+		grid.mainColor.b = value;
+	}, function() {
+		return grid.mainColor.b;
+	});
+    
+    registerRangeUI("grid", "GridRatio", 0, 10, function(value) {
+		grid.gridRatio = value;
+	}, function() {
+		return grid.gridRatio;
+	});
+    
+    registerRangeUI("grid", "MajorUnitFrequency", 1, 10, function(value) {
+		grid.majorUnitFrequency = value;
+	}, function() {
+		return grid.majorUnitFrequency;
+	});
+    
+    registerRangeUI("grid", "MinorUnitVisibility", 0, 1, function(value) {
+		grid.minorUnitVisibility = value;
+	}, function() {
+		return grid.minorUnitVisibility;
+	});
+    
+    registerRangeUI("grid", "Opacity", 0, 1, function(value) {
+		grid.opacity = value;
+	}, function() {
+		return grid.opacity;
+	});
+
+	return grid;
+}

+ 8 - 1
materialsLibrary/test/index.html

@@ -15,6 +15,7 @@
 	<script src="../dist/babylon.triPlanarMaterial.js"></script>
 	<script src="../dist/babylon.gradientMaterial.js"></script>
 	<script src="../dist/babylon.skyMaterial.js"></script>
+	<script src="../dist/babylon.gridMaterial.js"></script>
 
 	<style>
 		html, body {
@@ -59,6 +60,7 @@
 	<script src="add/addtriplanar.js"></script>
 	<script src="add/addgradient.js"></script>
 	<script src="add/addsky.js"></script>
+	<script src="add/addgrid.js"></script>
 	
 	<script>
 		if (BABYLON.Engine.isSupported()) {
@@ -204,13 +206,15 @@
 				var triPlanar = prepareTriPlanar();
 				
 				var sky = prepareSky();
+                
+                var grid = prepareGrid();
 				
 				// Default to std
 				var currentMaterial = std;
 				sphere.material = std;				
 				sphere.receiveShadows = true;
 
-				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky']).onFinishChange(function () {
+				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky', 'grid']).onFinishChange(function () {
 					water.enableRenderTargets(false);
 					skybox.material = skyboxMaterial;
 					currentMesh.isVisible = true;
@@ -254,6 +258,9 @@
 							skybox.setEnabled(true);
 							skybox.material = sky;
 							break;
+                        case "grid":
+                            currentMaterial = grid;
+                            break;
 						default:
 							currentMaterial = std;
 							break;