소스 검색

Merge pull request #2088 from julien-moreau/master

Standard Rendering pipeline now computes luminance adaptation
David Catuhe 8 년 전
부모
커밋
0a9c498768
3개의 변경된 파일294개의 추가작업 그리고 33개의 파일을 삭제
  1. 1 1
      src/LensFlare/babylon.lensFlareSystem.ts
  2. 196 32
      src/PostProcess/babylon.standardRenderingPipeline.ts
  3. 97 0
      src/Shaders/standard.fragment.fx

+ 1 - 1
src/LensFlare/babylon.lensFlareSystem.ts

@@ -275,7 +275,7 @@
 
             for (var index = 0; index < parsedLensFlareSystem.flares.length; index++) {
                 var parsedFlare = parsedLensFlareSystem.flares[index];
-                var flare = new LensFlare(parsedFlare.size, parsedFlare.position, Color3.FromArray(parsedFlare.color), rootUrl + parsedFlare.textureName, lensFlareSystem);
+                var flare = new LensFlare(parsedFlare.size, parsedFlare.position, Color3.FromArray(parsedFlare.color), parsedFlare.textureName ? rootUrl + parsedFlare.textureName : "", lensFlareSystem);
             }
 
             return lensFlareSystem;

+ 196 - 32
src/PostProcess/babylon.standardRenderingPipeline.ts

@@ -13,9 +13,15 @@ module BABYLON {
         public gaussianBlurVPostProcesses: PostProcess[] = [];
         public textureAdderPostProcess: PostProcess = null;
 
+        public luminancePostProcess: PostProcess = null;
+        public luminanceDownSamplePostProcesses: PostProcess[] = [];
+        public hdrPostProcess: PostProcess = null;
+
         public textureAdderFinalPostProcess: PostProcess = null;
         public lensFlareFinalPostProcess: PostProcess = null;
 
+        public hdrFinalPostProcess: PostProcess = null;
+
         public lensFlarePostProcess: PostProcess = null;
         public lensFlareComposePostProcess: PostProcess = null;
 
@@ -41,6 +47,13 @@ module BABYLON {
         @serializeAsTexture("lensTexture")
         public lensTexture: Texture = null;
 
+        @serialize()
+        public hdrMinimumLuminance: number = 1.0;
+        @serialize()
+        public hdrDecreaseRate: number = 0.5;
+        @serialize()
+        public hdrIncreaseRate: number = 0.5;
+
         @serializeAsTexture("lensColorTexture")
         public lensColorTexture: Texture = null;
         @serialize()
@@ -69,21 +82,24 @@ module BABYLON {
         * Private members
         */
         private _scene: Scene;
-
         private _depthRenderer: DepthRenderer = null;
         private _currentDepthOfFieldSource: PostProcess = null;
 
+        private _currentHDRSource: PostProcess = null;
+        private _hdrCurrentLuminance: number = 1.0;
+
         // Getters and setters
         private _depthOfFieldEnabled: boolean = true;
         private _lensFlareEnabled: boolean = true;
+        private _hdrEnabled: boolean = true;
 
         public set DepthOfFieldEnabled(enabled: boolean) {
             var blurIndex = this.gaussianBlurHPostProcesses.length - 1;
 
             if (enabled && !this._depthOfFieldEnabled) {
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._cameras);
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._cameras);
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRDepthOfField", this._cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRDepthOfField", this._scene.cameras);
                 this._depthRenderer = this._scene.enableDepthRenderer();
             }
             else if (!enabled && this._depthOfFieldEnabled) {
@@ -104,22 +120,20 @@ module BABYLON {
             var blurIndex = this.gaussianBlurHPostProcesses.length - 2;
 
             if (enabled && !this._lensFlareEnabled) {
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLensFlare", this._cameras);
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLensFlareShift", this._cameras);
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._cameras);
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._cameras);
-                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLensFlareCompose", this._cameras);
-
-                this._setDepthOfFieldSavePostProcess("HDRPostLensFlareDepthOfFieldSource");
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLensFlare", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLensFlareShift", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLensFlareCompose", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRPostLensFlareDepthOfFieldSource", this._scene.cameras);
             }
             else if (!enabled && this._lensFlareEnabled) {
-                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlare", this._cameras);
-                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlareShift", this._cameras);
-                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._cameras);
-                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._cameras);
-                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlareCompose", this._cameras);
-
-                this._setDepthOfFieldSavePostProcess("HDRBaseDepthOfFieldSource");
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlare", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlareShift", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlareCompose", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRPostLensFlareDepthOfFieldSource", this._scene.cameras);
             }
 
             this._lensFlareEnabled = enabled;
@@ -130,6 +144,32 @@ module BABYLON {
             return this._lensFlareEnabled;
         }
 
+        public set HDREnabled(enabled: boolean) {
+            if (enabled && !this._hdrEnabled) {
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLuminance", this._scene.cameras);
+                for (var i = 0; i < this.luminanceDownSamplePostProcesses.length; i++) {
+                    this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLuminanceDownSample" + i, this._scene.cameras);
+                }
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDR", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRPostHDReDepthOfFieldSource", this._scene.cameras);
+            }
+            else if (!enabled && this._hdrEnabled) {
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLuminance", this._scene.cameras);
+                for (var i = 0; i < this.luminanceDownSamplePostProcesses.length; i++) {
+                    this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLuminanceDownSample" + i, this._scene.cameras);
+                }
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDR", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRPostHDReDepthOfFieldSource", this._scene.cameras);
+            }
+
+            this._hdrEnabled = enabled;
+        }
+
+        @serialize()
+        public get HDREnabled(): boolean {
+            return this._hdrEnabled;
+        }
+
         /**
          * @constructor
          * @param {string} name - The rendering pipeline name
@@ -181,6 +221,16 @@ module BABYLON {
             this.lensFlareFinalPostProcess = new PostProcess("HDRPostLensFlareDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRPostLensFlareDepthOfFieldSource", () => { return this.lensFlareFinalPostProcess; }, true));
 
+            // Create luminance
+            this._createLuminancePostProcesses(scene);
+
+            // Create HDR
+            this._createHdrPostProcess(scene, ratio);
+
+            // Create depth-of-field source post-process post lens-flare and disable it now
+            this.hdrFinalPostProcess = new PostProcess("HDRPostHDReDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRPostHDReDepthOfFieldSource", () => { return this.hdrFinalPostProcess; }, true));
+
             // Create gaussian blur used by depth-of-field
             this._createGaussianBlurPostProcesses(scene, ratio / 2, 5, "depthOfFieldBlurWidth");
 
@@ -197,20 +247,7 @@ module BABYLON {
             // Deactivate
             this.LensFlareEnabled = false;
             this.DepthOfFieldEnabled = false;
-        }
-
-        // Sets depth-of-field save post-process
-        private _setDepthOfFieldSavePostProcess(name: string): void {
-            
-            this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRPostLensFlareDepthOfFieldSource", this._cameras);
-
-            this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, name, this._cameras);
-
-            switch (name) {
-                case "HDRBaseDepthOfFieldSource": this._currentDepthOfFieldSource = this.textureAdderFinalPostProcess; break;
-                case "HDRPostLensFlareDepthOfFieldSource": this._currentDepthOfFieldSource = this.lensFlareFinalPostProcess; break;
-                default: break;
-            }
+            this.HDREnabled = false;
         }
 
         // Down Sample X4 Post-Processs
@@ -327,12 +364,133 @@ module BABYLON {
                 effect.setTexture("lensSampler", this.lensTexture);
 
                 effect.setFloat("exposure", this.exposure);
+
+                this._currentDepthOfFieldSource = this.textureAdderFinalPostProcess;
+                this._currentHDRSource = this.textureAdderFinalPostProcess;
             };
 
             // Add to pipeline
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", () => { return this.textureAdderPostProcess; }, true));
         }
 
+        // Create luminance
+        private _createLuminancePostProcesses(scene: Scene): void {
+            // Create luminance
+            var size = Math.pow(3, StandardRenderingPipeline.LuminanceSteps);
+            this.luminancePostProcess = new PostProcess("HDRLuminance", "standard", ["lumOffsets"], [], { width: size, height: size }, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define LUMINANCE", Engine.TEXTURETYPE_FLOAT);
+
+            var offsets: number[] = [];
+            this.luminancePostProcess.onApply = (effect: Effect) => {
+                var sU = (1.0 / this.luminancePostProcess.width);
+                var sV = (1.0 / this.luminancePostProcess.height);
+
+                offsets[0] = -0.5 * sU;
+                offsets[1] = 0.5 * sV;
+                offsets[2] = 0.5 * sU;
+                offsets[3] = 0.5 * sV;
+                offsets[4] = -0.5 * sU;
+                offsets[5] = -0.5 * sV;
+                offsets[6] = 0.5 * sU;
+                offsets[7] = -0.5 * sV;
+
+                effect.setArray2("lumOffsets", offsets);
+            };
+
+            // Add to pipeline
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRLuminance", () => { return this.luminancePostProcess; }, true));
+
+            // Create down sample luminance
+            for (var i = StandardRenderingPipeline.LuminanceSteps - 1; i >= 0; i--) {
+                var size = Math.pow(3, i);
+
+                var defines = "#define LUMINANCE_DOWN_SAMPLE\n";
+                if (i === 0) {
+                    defines += "#define FINAL_DOWN_SAMPLER";
+                }
+
+                var postProcess = new PostProcess("HDRLuminanceDownSample" + i, "standard", ["dsOffsets", "halfDestPixelSize"], [], { width: size, height: size }, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, defines, Engine.TEXTURETYPE_FLOAT);
+                this.luminanceDownSamplePostProcesses.push(postProcess);
+            }
+
+            // Create callbacks and add effects
+            var lastLuminance = this.luminancePostProcess;
+
+            this.luminanceDownSamplePostProcesses.forEach((pp, index) => {
+                var downSampleOffsets = new Array<number>(18);
+
+                pp.onApply = (effect: Effect) => {
+                    var id = 0;
+                    for (var x = -1; x < 2; x++) {
+                        for (var y = -1; y < 2; y++) {
+                            downSampleOffsets[id] = x / lastLuminance.width;
+                            downSampleOffsets[id + 1] = y / lastLuminance.height;
+                            id += 2;
+                        }
+                    }
+
+                    effect.setArray2("dsOffsets", downSampleOffsets);
+                    effect.setFloat("halfDestPixelSize", 0.5 / lastLuminance.width);
+
+                    if (index === this.luminanceDownSamplePostProcesses.length - 1) {
+                        lastLuminance = this.luminancePostProcess;
+                    } else {
+                        lastLuminance = pp;
+                    }
+                };
+
+                if (index === this.luminanceDownSamplePostProcesses.length - 1) {
+                    pp.onAfterRender = (effect: Effect) => {
+                        var pixel = scene.getEngine().readPixels(0, 0, 1, 1);
+                        var bit_shift = new Vector4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
+                        this._hdrCurrentLuminance = (pixel[0] * bit_shift.x + pixel[1] * bit_shift.y + pixel[2] * bit_shift.z + pixel[3] * bit_shift.w) / 100.0;
+                    };
+                }
+
+                this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRLuminanceDownSample" + index, () => { return pp; }, true));
+            });
+        }
+
+        // Create HDR post-process
+        private _createHdrPostProcess(scene: Scene, ratio: number): void {
+            this.hdrPostProcess = new PostProcess("HDR", "standard", ["averageLuminance"], ["textureAdderSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define HDR", Engine.TEXTURETYPE_UNSIGNED_INT);
+
+            var outputLiminance = 1;
+            var time = 0;
+            var lastTime = 0;
+
+            this.hdrPostProcess.onApply = (effect: Effect) => {
+                effect.setTextureFromPostProcess("textureAdderSampler", this._currentHDRSource);
+
+                time += scene.getEngine().getDeltaTime();
+
+                if (outputLiminance < 0) {
+                    outputLiminance = this._hdrCurrentLuminance;
+                } else {
+                    var dt = (lastTime - time) / 1000.0;
+
+                    if (this._hdrCurrentLuminance < outputLiminance + this.hdrDecreaseRate * dt) {
+                        outputLiminance += this.hdrDecreaseRate * dt;
+                    }
+                    else if (this._hdrCurrentLuminance > outputLiminance - this.hdrIncreaseRate * dt) {
+                        outputLiminance -= this.hdrIncreaseRate * dt;
+                    }
+                    else {
+                        outputLiminance = this._hdrCurrentLuminance;
+                    }
+                }
+
+                outputLiminance = MathTools.Clamp(outputLiminance, this.hdrMinimumLuminance, 1e20);
+
+                effect.setFloat("averageLuminance", outputLiminance);
+
+                lastTime = time;
+
+                this._currentDepthOfFieldSource = this.hdrFinalPostProcess;
+            };
+
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDR", () => { return this.hdrPostProcess; }, true));
+        }
+
         // Create lens flare post-process
         private _createLensFlarePostProcess(scene: Scene, ratio: number): void {
             this.lensFlarePostProcess = new PostProcess("HDRLensFlare", "standard", ["strength", "ghostDispersal", "haloWidth", "resolution", "distortionStrength"], ["lensColorSampler"], ratio / 2, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define LENS_FLARE", Engine.TEXTURETYPE_UNSIGNED_INT);
@@ -397,6 +555,9 @@ module BABYLON {
                 var lensStarMatrix = scaleBias2.multiply(starRotation).multiply(scaleBias1);
 
                 effect.setMatrix("lensStarMatrix", lensStarMatrix);
+
+                this._currentDepthOfFieldSource = this.lensFlareFinalPostProcess;
+                this._currentHDRSource = this.lensFlareFinalPostProcess;
             };
         }
 
@@ -455,5 +616,8 @@ module BABYLON {
         public static Parse(source: any, scene: Scene, rootUrl: string): StandardRenderingPipeline {
             return SerializationHelper.Parse(() => new StandardRenderingPipeline(source._name, scene, source._ratio), source, scene, rootUrl);
         }
+
+        // Luminance steps
+        public static LuminanceSteps: number = 6;
     }
 }

+ 97 - 0
src/Shaders/standard.fragment.fx

@@ -112,6 +112,103 @@ void main(void)
 }
 #endif
 
+#if defined(LUMINANCE)
+uniform vec2 lumOffsets[4];
+
+void main()
+{
+	float average = 0.0;
+	vec4 color = vec4(0.0);
+	float maximum = -1e20;
+	vec3 weight = vec3(0.299, 0.587, 0.114);
+
+	for (int i = 0; i < 4; i++)
+	{
+		color = texture2D(textureSampler, vUV+ lumOffsets[i]);
+
+		//#ifdef SIMPLE
+		float GreyValue = dot(color.rgb, vec3(0.33, 0.33, 0.33));
+		//#endif
+
+		#ifdef WEIGHTED_AVERAGE
+		float GreyValue = dot(color.rgb, weight);
+		#endif
+
+		#ifdef BRIGHTNESS
+		float GreyValue = max(color.r, max(color.g, color.b));
+		#endif
+
+		#ifdef HSL_COMPONENT
+		float GreyValue = 0.5 * (max(color.r, max(color.g, color.b)) + min(color.r, min(color.g, color.b)));
+		#endif
+
+		#ifdef MAGNITUDE
+		float GreyValue = length(color.rgb);
+		#endif
+
+		maximum = max(maximum, GreyValue);
+		average += (0.25 * log(1e-5 + GreyValue));
+	}
+
+	average = exp(average);
+
+	gl_FragColor = vec4(average, maximum, 0.0, 1.0);
+}
+#endif
+
+#if defined(LUMINANCE_DOWN_SAMPLE)
+uniform vec2 dsOffsets[9];
+uniform float halfDestPixelSize;
+
+#ifdef FINAL_DOWN_SAMPLER
+vec4 pack(float value) {
+	const vec4 bit_shift = vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0);
+	const vec4 bit_mask = vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
+
+	vec4 res = fract(value * bit_shift);
+	res -= res.xxyz * bit_mask;
+
+	return res;
+}
+#endif
+
+void main()
+{
+	vec4 color = vec4(0.0);
+	float average = 0.0;
+
+	for (int i = 0; i < 9; i++)
+	{
+		color = texture2D(textureSampler, vUV + vec2(halfDestPixelSize, halfDestPixelSize) + dsOffsets[i]);
+		average += color.r;
+	}
+
+	average /= 9.0;
+
+	#ifdef FINAL_DOWN_SAMPLER
+	gl_FragColor = pack(average);
+	#else
+	gl_FragColor = vec4(average, average, 0.0, 1.0);
+	#endif
+}
+#endif
+
+#if defined(HDR)
+uniform sampler2D textureAdderSampler;
+uniform float averageLuminance;
+
+void main()
+{
+	vec4 color = texture2D(textureAdderSampler, vUV);
+	vec4 adjustedColor = color / averageLuminance;
+
+	color = adjustedColor;
+	color.a = 1.0;
+
+	gl_FragColor = color;
+}
+#endif
+
 #if defined(LENS_FLARE)
 #define GHOSTS 3