Переглянути джерело

Simplify Background Material

sebastien 7 роки тому
батько
коміт
410ed04c39

+ 42 - 57
materialsLibrary/test/addbackground.js

@@ -26,10 +26,19 @@ window.prepareBackgroundMaterial = function() {
 	mirror.mirrorPlane = new BABYLON.Plane(0, -1.0, 0, 0.0);
 	mirror.adaptiveBlurKernel = 64;
 	
+	var perceptualColor = new BABYLON.Color3(1, 1, 1);
+	var primaryColor = new BABYLON.Color3(1, 1, 1);
+
 	registerRangeUI("background", "primaryColorR", 0, 1, function(value) {
 		mirror.clearColor.r = value;
 		back.primaryColor.r = value;
 		backSky.primaryColor.r = value;
+		if (back.perceptualColor) {
+			back.perceptualColor.r = value;
+			backSky.perceptualColor.r = value;
+		}
+		perceptualColor.r = value;
+		primaryColor.r = value;
 	}, function() {
 		return back.primaryColor.r;
 	});
@@ -38,6 +47,12 @@ window.prepareBackgroundMaterial = function() {
 		mirror.clearColor.g = value;
 		back.primaryColor.g = value;
 		backSky.primaryColor.g = value;
+		if (back.perceptualColor) {
+			back.perceptualColor.g = value;
+			backSky.perceptualColor.g = value;
+		}
+		perceptualColor.g = value;
+		primaryColor.g = value;
 	}, function() {
 		return back.primaryColor.g;
 	});
@@ -46,71 +61,28 @@ window.prepareBackgroundMaterial = function() {
 		mirror.clearColor.b = value;
 		back.primaryColor.b = value;
 		backSky.primaryColor.b = value;
+		if (back.perceptualColor) {
+			back.perceptualColor.b = value;
+			backSky.perceptualColor.b = value;
+		}
+		perceptualColor.b = value;
+		primaryColor.b = value;
 	}, function() {
 		return back.primaryColor.b;
 	});
 
-	registerRangeUI("background", "primaryLevel", 0, 30, function(value) {
-		back.primaryLevel = value;
-		backSky.primaryLevel = value;
-	}, function() {
-		return back.primaryLevel;
-	});
-
-	registerRangeUI("background", "secondaryColorR", 0, 1, function(value) {
-		back.secondaryColor.r = value;
-		backSky.secondaryColor.r = value;
-	}, function() {
-		return back.secondaryColor.r;
-	});
-
-	registerRangeUI("background", "secondaryColorG", 0, 1, function(value) {
-		back.secondaryColor.g = value;
-		backSky.secondaryColor.g = value;
-	}, function() {
-		return back.secondaryColor.g;
-	});
-
-	registerRangeUI("background", "secondaryColorB", 0, 1, function(value) {
-		back.secondaryColor.b = value;
-		backSky.secondaryColor.b = value;
-	}, function() {
-		return back.secondaryColor.b;
-	});
-
-	registerRangeUI("background", "secondaryLevel", 0, 30, function(value) {
-		back.secondaryLevel = value;
-		backSky.secondaryLevel = value;
-	}, function() {
-		return back.secondaryLevel;
-	});
-
-	registerRangeUI("background", "tertiaryColorR", 0, 1, function(value) {
-		back.tertiaryColor.r = value;
-		backSky.tertiaryColor.r = value;
+	registerRangeUI("background", "primaryColorShadowLevel", 0, 1, function(value) {
+		back.primaryColorShadowLevel = value;
+		backSky.primaryColorShadowLevel = value;
 	}, function() {
-		return back.tertiaryColor.r;
+		return back.primaryColorShadowLevel;
 	});
 
-	registerRangeUI("background", "tertiaryColorG", 0, 1, function(value) {
-		back.tertiaryColor.g = value;
-		backSky.tertiaryColor.g = value;
+	registerRangeUI("background", "primaryColorHighlightLevel", 0, 1, function(value) {
+		back.primaryColorHighlightLevel = value;		
+		backSky.primaryColorHighlightLevel = value;
 	}, function() {
-		return back.tertiaryColor.g;
-	});
-
-	registerRangeUI("background", "tertiaryColorB", 0, 1, function(value) {
-		back.tertiaryColor.b = value;
-		backSky.tertiaryColor.b = value;
-	}, function() {
-		return back.tertiaryColor.b;
-	});
-
-	registerRangeUI("background", "tertiaryLevel", 0, 30, function(value) {
-		back.tertiaryLevel = value;		
-		backSky.tertiaryLevel = value;
-	}, function() {
-		return back.tertiaryLevel;
+		return back.primaryColorHighlightLevel;
 	});
 
 	registerRangeUI("background", "reflectionBlur", 0, 1, function(value) {
@@ -148,6 +120,19 @@ window.prepareBackgroundMaterial = function() {
 		back.useRGBColor = !back.useRGBColor;
 	});
 
+	registerButtonUI("background", "TogglePerceptualColor", function() {
+		if (back.perceptualColor) {
+			back.perceptualColor = null;
+			backSky.perceptualColor = null;
+			back.primaryColor = primaryColor.clone();
+			backSky.primaryColor = primaryColor.clone();
+		}
+		else {
+			back.perceptualColor = perceptualColor.clone();
+			backSky.perceptualColor = perceptualColor.clone();
+		}
+	});
+
 	registerButtonUI("background", "ToggleSkyRGB", function() {
 		backSky.useRGBColor = !backSky.useRGBColor;
 	});

+ 15 - 10
src/Helpers/babylon.environmentHelper.ts

@@ -395,15 +395,26 @@ module BABYLON {
 
         /**
          * Sets the primary color of all the available elements.
-         * @param color 
+         * @param color the main color to affect to the ground and the background
+         * @param perceptual Specifies wether the chosen color has been set as intented to be seen e.g. in gamma space not accounting for exposure and tone mapping
          */
-        public setMainColor(color: Color3): void {
+        public setMainColor(color: Color3, perceptual = false): void {
             if (this.groundMaterial) {
-                this.groundMaterial.primaryColor = color;
+                if (perceptual) {
+                    this.groundMaterial.perceptualColor = color;
+                }
+                else {
+                    this.groundMaterial.primaryColor = color;
+                }
             }
 
             if (this.skyboxMaterial) {
-                this.skyboxMaterial.primaryColor = color;
+                if (perceptual) {
+                    this.skyboxMaterial.perceptualColor = color;
+                }
+                else {
+                    this.skyboxMaterial.primaryColor = color;
+                }
             }
 
             if (this.groundMirror) {
@@ -535,10 +546,7 @@ module BABYLON {
             this._groundMaterial.alpha = this._options.groundOpacity;
             this._groundMaterial.alphaMode = Engine.ALPHA_PREMULTIPLIED_PORTERDUFF;
             this._groundMaterial.shadowLevel = this._options.groundShadowLevel;
-            this._groundMaterial.primaryLevel = 1;
             this._groundMaterial.primaryColor = this._options.groundColor;
-            this._groundMaterial.secondaryLevel = 0;
-            this._groundMaterial.tertiaryLevel = 0;
             this._groundMaterial.useRGBColor = false;
             this._groundMaterial.enableNoise = true;
 
@@ -645,10 +653,7 @@ module BABYLON {
                 this._skyboxMaterial = new BackgroundMaterial("BackgroundSkyboxMaterial", this._scene);
             }
             this._skyboxMaterial.useRGBColor = false;
-            this._skyboxMaterial.primaryLevel = 1;
             this._skyboxMaterial.primaryColor = this._options.skyboxColor;
-            this._skyboxMaterial.secondaryLevel = 0;
-            this._skyboxMaterial.tertiaryLevel = 0;
             this._skyboxMaterial.enableNoise = true;
 
             this._skybox.material = this._skyboxMaterial;

+ 127 - 45
src/Materials/Background/babylon.backgroundMaterial.ts

@@ -60,6 +60,12 @@
         public USERGBCOLOR = false;
 
         /**
+         * True if highlight and shadow levels have been specified. It can help ensuring the main perceived color
+         * stays aligned with the desired configuration.
+         */
+        public USEHIGHLIGHTANDSHADOWCOLORS = false;
+
+        /**
          * True to add noise in order to reduce the banding effect.
          */
         public NOISE = false;
@@ -142,50 +148,58 @@
 
         @serializeAsColor3()
         protected _primaryColor: Color3;
-        /**
-         * Key light Color (multiply against the R channel of the environement texture)
-         */
         @expandToProperty("_markAllSubMeshesAsLightsDirty")
         public primaryColor = Color3.White();
-
-        @serialize()
-        protected _primaryLevel: float;
-        /**
-         * Key light Level (allowing HDR output of the background)
-         */
-        @expandToProperty("_markAllSubMeshesAsLightsDirty")
-        public primaryLevel: float = 1;
+        
         @serializeAsColor3()
-        protected _secondaryColor: Color3;
+        protected _perceptualColor: Nullable<Color3>;
         /**
-         * Secondary light Color (multiply against the G channel of the environement texture)
+         * Key light Color in "perceptual value" meaning the color you would like to see on screen.
+         * This acts as a helper to set the primary color to a more "human friendly" value.
+         * Conversion to linear space as well as exposure and tone mapping correction will be applied to keep the
+         * output color as close as possible from the chosen value.
+         * (This does not account for contrast color grading and color curves as they are considered post effect and not directly
+         * part of lighting setup.)
          */
-        @expandToProperty("_markAllSubMeshesAsLightsDirty")
-        public secondaryColor = Color3.Gray();
+        public get perceptualColor(): Nullable<Color3> {
+            return this._perceptualColor;
+        }
+        public set perceptualColor(value: Nullable<Color3>) {
+            this._perceptualColor = value;
+            this._computePrimaryColorFromPerceptualColor();
+            this._markAllSubMeshesAsLightsDirty();
+        }
 
         @serialize()
-        protected _secondaryLevel: float;
+        protected _primaryColorShadowLevel: float = 0;
         /**
-         * Secondary light Level (allowing HDR output of the background)
+         * Defines the level of the shadows (dark area of the reflection map) in order to help scaling the colors.
+         * The color opposite to the primary color is used at the level chosen to define what the black area would look.
          */
-        @expandToProperty("_markAllSubMeshesAsLightsDirty")
-        public secondaryLevel: float = 1;
-
-        @serializeAsColor3()
-        protected _tertiaryColor: Color3;
-        /**
-         * Tertiary light Color (multiply against the B channel of the environement texture)
-         */
-        @expandToProperty("_markAllSubMeshesAsLightsDirty")
-        public tertiaryColor = Color3.Black();
+        public get primaryColorShadowLevel(): float {
+            return this._primaryColorShadowLevel;
+        }
+        public set primaryColorShadowLevel(value: float) {
+            this._primaryColorShadowLevel = value;
+            this._computePrimaryColors();
+            this._markAllSubMeshesAsLightsDirty();
+        }
 
         @serialize()
-        protected _tertiaryLevel: float;
+        protected _primaryColorHighlightLevel: float = 0;
         /**
-         * Tertiary light Level (allowing HDR output of the background)
+         * Defines the level of the highliights (highlight area of the reflection map) in order to help scaling the colors.
+         * The primary color is used at the level chosen to define what the white area would look.
          */
         @expandToProperty("_markAllSubMeshesAsLightsDirty")
-        public tertiaryLevel: float = 1;
+        public get primaryColorHighlightLevel(): float {
+            return this._primaryColorHighlightLevel;
+        }
+        public set primaryColorHighlightLevel(value: float) {
+            this._primaryColorHighlightLevel = value;
+            this._computePrimaryColors();
+            this._markAllSubMeshesAsLightsDirty();
+        }
 
         @serializeAsTexture()
         protected _reflectionTexture: Nullable<BaseTexture>;
@@ -225,15 +239,6 @@
         public shadowLights: Nullable<IShadowLight[]> = null;
 
         @serialize()
-        protected _shadowBlurScale: int;
-        /**
-         * For the lights having a blurred shadow generator, this can add a second blur pass in order to reach
-         * soft lighting on the background.
-         */
-        @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-        public shadowBlurScale: int = 1;
-
-        @serialize()
         protected _shadowLevel: float;
         /**
          * Helps adjusting the shadow to a softer level if required.
@@ -400,6 +405,7 @@
 
             // Attaches observer.
             this._imageProcessingObserver = this._imageProcessingConfiguration.onUpdateParameters.add(conf => {
+                this._computePrimaryColorFromPerceptualColor();
                 this._markAllSubMeshesAsImageProcessingDirty();
             });
         }
@@ -534,6 +540,9 @@
         // Temp values kept as cache in the material.
         private _renderTargets = new SmartArray<RenderTargetTexture>(16);
         private _reflectionControls = Vector4.Zero();
+        private _white = Color3.White();
+        private _primaryShadowColor = Color3.Black();
+        private _primaryHighlightColor = Color3.Black();
 
         /**
          * Instantiates a Background Material in the given scene
@@ -723,6 +732,10 @@
                 defines.NOISE = this._enableNoise;
             }
 
+            if (defines._areLightsDirty) {
+                defines.USEHIGHLIGHTANDSHADOWCOLORS = !this._useRGBColor && (this._primaryColorShadowLevel !== 0 || this._primaryColorHighlightLevel !== 0);
+            }
+
             if (defines._areImageProcessingDirty) {
                 if (!this._imageProcessingConfiguration.isReady()) {
                     return false;
@@ -790,7 +803,7 @@
                     "vFogInfos", "vFogColor", "pointSize",
                     "vClipPlane", "mBones",
 
-                    "vPrimaryColor", "vSecondaryColor", "vTertiaryColor",
+                    "vPrimaryColor", "vPrimaryColorShadow",
                     "vReflectionInfos", "reflectionMatrix", "vReflectionMicrosurfaceInfos", "fFovMultiplier",
 
                     "shadowLevel", "alpha",
@@ -849,13 +862,78 @@
         }
 
         /**
+         * Tone Mapping calibration (should match image processing tone mapping calibration value).
+         */
+        private static readonly _tonemappingCalibration = 1.590579;
+
+        /**
+         * Compute the primary color according to the chosen perceptual color.
+         */
+        private _computePrimaryColorFromPerceptualColor(): void {
+            if (!this._perceptualColor) {
+                return;
+            }
+
+            this._primaryColor.copyFrom(this._perceptualColor);
+
+            // Revert gamma space.
+            this._primaryColor.toLinearSpaceToRef(this._primaryColor);
+
+            // Revert image processing configuration.
+            if (this._imageProcessingConfiguration) {
+                // Revert tone mapping.
+                if (this._imageProcessingConfiguration.toneMappingEnabled) {
+                    // shader reference.
+                    // tonemapped.rgb = 1.0 - exp2(-tonemappingCalibration * color.rgb);
+                    // providing
+                    // log2(1.0 - tonemapped.rgb) / -tonemappingCalibration = color.rgb;
+
+                    // 1.0 - tonemapped.rgb
+                    this._white.subtractToRef(this._primaryColor, this._primaryColor);
+
+                    // log2(1.0 - tonemapped.rgb)
+                    this._primaryColor.r = Scalar.Log2(this._primaryColor.r);
+                    this._primaryColor.g = Scalar.Log2(this._primaryColor.g);
+                    this._primaryColor.b = Scalar.Log2(this._primaryColor.b);
+                    
+                    // log2(1.0 - tonemapped.rgb) / -tonemappingCalibration
+                    this._primaryColor.scaleToRef(-1 / BackgroundMaterial._tonemappingCalibration, this._primaryColor);
+                }
+
+                // Revert Exposure.
+                this._primaryColor.scaleToRef(1 / this._imageProcessingConfiguration.exposure, this._primaryColor);
+            }
+
+            this._computePrimaryColors();
+        }
+
+        /**
+         * Compute the highlights and shadow colors according to their chosen levels.
+         */
+        private _computePrimaryColors(): void {
+            if (this._primaryColorShadowLevel === 0 && this._primaryColorHighlightLevel === 0) {
+                return;
+            }
+
+            // Find the highlight color based on the configuration.
+            this._primaryColor.scaleToRef(this._primaryColorShadowLevel, this._primaryShadowColor);
+            this._primaryColor.subtractToRef(this._primaryShadowColor, this._primaryShadowColor);
+            this._primaryShadowColor.clampToRef(0, 1, this._primaryShadowColor);
+
+            // Find the shadow color based on the configuration.
+            this._white.subtractToRef(this._primaryColor, this._primaryHighlightColor);
+            this._primaryHighlightColor.scaleToRef(this._primaryColorHighlightLevel, this._primaryHighlightColor);
+            this._primaryColor.addToRef(this._primaryHighlightColor, this._primaryHighlightColor);
+            this._primaryHighlightColor.clampToRef(0, 1, this._primaryHighlightColor);
+        }
+
+        /**
          * Build the uniform buffer used in the material.
          */
         public buildUniformLayout(): void {
             // Order is important !
             this._uniformBuffer.addUniform("vPrimaryColor", 4);
-            this._uniformBuffer.addUniform("vSecondaryColor", 4);
-            this._uniformBuffer.addUniform("vTertiaryColor", 4);
+            this._uniformBuffer.addUniform("vPrimaryColorShadow", 4);
             this._uniformBuffer.addUniform("vDiffuseInfos", 2);
             this._uniformBuffer.addUniform("vReflectionInfos", 2);
             this._uniformBuffer.addUniform("diffuseMatrix", 16);
@@ -956,9 +1034,13 @@
                         this._uniformBuffer.updateFloat("pointSize", this.pointSize);
                     }
 
-                    this._uniformBuffer.updateColor4("vPrimaryColor", this._primaryColor, this._primaryLevel);
-                    this._uniformBuffer.updateColor4("vSecondaryColor", this._secondaryColor, this._secondaryLevel);
-                    this._uniformBuffer.updateColor4("vTertiaryColor", this._tertiaryColor, this._tertiaryLevel);
+                    if (defines.USEHIGHLIGHTANDSHADOWCOLORS) {
+                        this._uniformBuffer.updateColor4("vPrimaryColor", this._primaryHighlightColor, 1.0);
+                        this._uniformBuffer.updateColor4("vPrimaryColorShadow", this._primaryShadowColor, 1.0);
+                    }
+                    else {
+                        this._uniformBuffer.updateColor4("vPrimaryColor", this._primaryColor, 1.0);
+                    }
                 }
 
                 this._uniformBuffer.updateFloat("fFovMultiplier", this._fovMultiplier);

+ 3 - 2
src/Shaders/ShadersInclude/backgroundFragmentDeclaration.fx

@@ -1,6 +1,7 @@
     uniform vec4 vPrimaryColor;
-    uniform vec4 vSecondaryColor;
-    uniform vec4 vTertiaryColor;
+#ifdef USEHIGHLIGHTANDSHADOWCOLORS
+    uniform vec4 vPrimaryColorShadow;
+#endif
     uniform float shadowLevel;
     uniform float alpha;
 

+ 1 - 2
src/Shaders/ShadersInclude/backgroundUboDeclaration.fx

@@ -3,8 +3,7 @@ layout(std140, column_major) uniform;
 uniform Material
 {
 	uniform vec4 vPrimaryColor;
-	uniform vec4 vSecondaryColor;
-	uniform vec4 vTertiaryColor;
+	uniform vec4 vPrimaryColorShadow;
 	uniform vec2 vDiffuseInfos;
 	uniform vec2 vReflectionInfos;
 	uniform mat4 diffuseMatrix;

+ 7 - 3
src/Shaders/background.fragment.fx

@@ -229,9 +229,13 @@ float finalAlpha = alpha;
 #ifdef USERGBCOLOR
     vec3 finalColor = colorBase;
 #else
-    vec3 finalColor = colorBase.r * vPrimaryColor.rgb * vPrimaryColor.a;
-    finalColor += colorBase.g * vSecondaryColor.rgb * vSecondaryColor.a;
-    finalColor += colorBase.b * vTertiaryColor.rgb * vTertiaryColor.a;
+    #ifdef USEHIGHLIGHTANDSHADOWCOLORS
+        vec3 mainColor = mix(vPrimaryColorShadow.rgb, vPrimaryColor.rgb, colorBase);
+    #else
+        vec3 mainColor = vPrimaryColor.rgb;
+    #endif
+
+    vec3 finalColor = colorBase * mainColor;
 #endif
 
 // ___________________________ FRESNELS _______________________________________