Procházet zdrojové kódy

Fixed: - leak on engine._releaseTexture()
- size change on post FX (optim possible if we get a global "size changed event" to avoid calculate and compare render width / render height at each frame)

optim : -disable depth buffer test & write on post fx
- avoid creating depthbuffer for the post process render targets if they are not the first on the pipeline

added : -fxaa PostProcess -> todo : make some settings available to tweak the "smoothness" effect depending on the scene
-toggle black and white and fxaa buttons on camera settings (samples)
-jscompaktor.bat to make it easier to build babylon for new contributors

known issues : -if there are multiple post-processes active on the same camera and the user disable the 1st one in the pipeline, the rendering target of the new "first effect in pipeline" has no depth buffer (and so the rendering is broken until the user resize the window).

Simon Ferquel před 11 roky
rodič
revize
261ce13a34

binární
Babylon/JSKompactor.exe


+ 16 - 0
Babylon/PostProcess/babylon.fxaaPostProcess.js

@@ -0,0 +1,16 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.FxaaPostProcess = function (name, ratio, camera) {
+        BABYLON.PostProcess.call(this, name, "fxaa", ["texelSize"], null, ratio, camera);
+    };
+    
+    BABYLON.FxaaPostProcess.prototype = Object.create(BABYLON.PostProcess.prototype);
+    BABYLON.FxaaPostProcess.prototype.onSizeChanged = function () {
+        this.texelWidth = 1.0 / this.width;
+        this.texelHeight = 1.0 / this.height;
+    };
+    BABYLON.FxaaPostProcess.prototype.onApply = function (effect) {
+        effect.setFloat2("texelSize", this.texelWidth, this.texelHeight);
+    };
+})();

+ 23 - 9
Babylon/PostProcess/babylon.postProcess.js

@@ -7,11 +7,9 @@
         this._scene = camera.getScene();
         camera.postProcesses.push(this);
         this._engine = this._scene.getEngine();
-
-        this.width = this._engine._renderingCanvas.width * ratio;
-        this.height = this._engine._renderingCanvas.height * ratio;
-        
-        this._texture = this._engine.createRenderTargetTexture({ width: this.width, height: this.height }, false);
+        this._renderRatio = ratio;
+        this.width = -1;
+        this.height = -1;
 
         samplers = samplers || [];
         samplers.push("textureSampler");
@@ -25,8 +23,22 @@
     // Methods
     BABYLON.PostProcess.prototype.onApply = null;
     BABYLON.PostProcess.prototype._onDispose = null;
-
-    BABYLON.PostProcess.prototype.activate = function() {
+    BABYLON.PostProcess.prototype.onSizeChanged = null;
+    BABYLON.PostProcess.prototype.activate = function () {
+        var desiredWidth = this._engine._renderingCanvas.width * this._renderRatio;
+        var desiredHeight = this._engine._renderingCanvas.height * this._renderRatio;
+        if (this.width !== desiredWidth || this.height !== desiredHeight) {
+            if (this._texture) {
+                this._engine._releaseTexture(this._texture);
+                this._texture = null;
+            }
+            this.width = desiredWidth;
+            this.height = desiredHeight;
+            this._texture = this._engine.createRenderTargetTexture({ width: this.width, height: this.height }, { generateMipMaps: false, generateDepthBuffer: this._camera.postProcesses.indexOf(this) === 0 });
+            if (this.onSizeChanged) {
+                this.onSizeChanged();
+            }
+        }
         this._engine.bindFramebuffer(this._texture);
     };
 
@@ -55,8 +67,10 @@
         if (this._onDispose) {
             this._onDispose();
         }
-
-        this._engine._releaseTexture(this._texture);
+        if (this._texture) {
+            this._engine._releaseTexture(this._texture);
+            this._texture = null;
+        }
         
         var index = this._camera.postProcesses.indexOf(this);
         this._camera.postProcesses.splice(index, 1);

+ 8 - 1
Babylon/PostProcess/babylon.postProcessManager.js

@@ -59,11 +59,18 @@
             if (effect) {
                 // VBOs
                 engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
-
+                // some effect activation have a side effect of putting depthBuffer / dephWrite on
+                // so we force it off at each post process pass
+                engine.setDepthBuffer(false);
+                engine.setDepthWrite(false);
                 // Draw order
                 engine.draw(true, 0, 6);
             }
         }
+        // reenable depth buffer
+
+        engine.setDepthBuffer(true);
+        engine.setDepthWrite(true);
     };
 
     BABYLON.PostProcessManager.prototype.dispose = function () {

+ 50 - 0
Babylon/Shaders/fxaa.fragment.fx

@@ -0,0 +1,50 @@
+#define FXAA_REDUCE_MIN   (1.0/128.0)
+#define FXAA_REDUCE_MUL   (1.0/8.0)
+#define FXAA_SPAN_MAX     8.0
+//#define texelSize  vec2(1.0/1600, 1.0/900)
+varying vec2 vUV;
+uniform sampler2D textureSampler;
+uniform vec2 texelSize;
+
+void main(){
+	vec2 localTexelSize = texelSize;
+	vec3 rgbNW = texture2D(textureSampler, (vUV + vec2(-1.0, -1.0) * localTexelSize)).xyz;
+	vec3 rgbNE = texture2D(textureSampler, (vUV + vec2(1.0, -1.0) * localTexelSize)).xyz;
+	vec3 rgbSW = texture2D(textureSampler, (vUV + vec2(-1.0, 1.0) * localTexelSize)).xyz;
+	vec3 rgbSE = texture2D(textureSampler, (vUV + vec2(1.0, 1.0) * localTexelSize)).xyz;
+	vec3 rgbM = texture2D(textureSampler, vUV ).xyz;
+	vec3 luma = vec3(0.299, 0.587, 0.114);
+	float lumaNW = dot(rgbNW, luma);
+	float lumaNE = dot(rgbNE, luma);
+	float lumaSW = dot(rgbSW, luma);
+	float lumaSE = dot(rgbSE, luma);
+	float lumaM = dot(rgbM, luma);
+	float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
+	float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
+
+	vec2 dir = vec2(-((lumaNW + lumaNE) - (lumaSW + lumaSE)), ((lumaNW + lumaSW) - (lumaNE + lumaSE)));
+
+	float dirReduce = max(
+		(lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL),
+		FXAA_REDUCE_MIN);
+
+	float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
+	dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
+		max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
+		dir * rcpDirMin)) * localTexelSize;
+
+	vec3 rgbA = 0.5 * (
+		texture2D(textureSampler, vUV + dir * (1.0 / 3.0 - 0.5)).xyz +
+		texture2D(textureSampler, vUV + dir * (2.0 / 3.0 - 0.5)).xyz);
+
+	vec3 rgbB = rgbA * 0.5 + 0.25 * (
+		texture2D(textureSampler, vUV + dir *  -0.5).xyz +
+		texture2D(textureSampler, vUV + dir * 0.5).xyz);
+	float lumaB = dot(rgbB, luma);
+	if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
+		gl_FragColor = vec4(rgbA, 1.0);
+	}
+	else {
+		gl_FragColor = vec4(rgbB, 1.0);
+	}
+}

+ 27 - 7
Babylon/babylon.engine.js

@@ -707,7 +707,16 @@
         texture.isReady = true;
     };
 
-    BABYLON.Engine.prototype.createRenderTargetTexture = function (size, generateMipMaps) {
+    BABYLON.Engine.prototype.createRenderTargetTexture = function (size, options) {
+        // old version had a "generateMipMaps" arg instead of options.
+        // if options.generateMipMaps is undefined, consider that options itself if the generateMipmaps value
+        // in the same way, generateDepthBuffer is defaulted to true
+        var generateMipMaps = false;
+        var generateDepthBuffer = true;
+        if (options !== undefined) {
+            generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipmaps;
+            generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
+        }
         var gl = this._gl;
 
         var texture = gl.createTexture();
@@ -722,16 +731,20 @@
         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
         gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
 
+        var depthBuffer;
         // Create the depth buffer
-        var depthBuffer = gl.createRenderbuffer();
-        gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
-        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
-
+        if (generateDepthBuffer) {
+            depthBuffer = gl.createRenderbuffer();
+            gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+            gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
+        }
         // Create the framebuffer
         var framebuffer = gl.createFramebuffer();
         gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
         gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
-        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+        if (generateDepthBuffer) {
+            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+        }
 
         // Unbind
         gl.bindTexture(gl.TEXTURE_2D, null);
@@ -739,7 +752,9 @@
         gl.bindFramebuffer(gl.FRAMEBUFFER, null);
 
         texture._framebuffer = framebuffer;
-        texture._depthBuffer = depthBuffer;
+        if (generateDepthBuffer) {
+            texture._depthBuffer = depthBuffer;
+        }
         texture._width = width;
         texture._height = height;
         texture.isReady = true;
@@ -845,6 +860,11 @@
             this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, null);
             this._activeTexturesCache[channel] = null;
         }
+        // hmm vilain leak !
+        var index = this._loadedTexturesCache.indexOf(texture);
+        if (index !== -1) {
+            this._loadedTexturesCache.splice(index, 1);
+        }
     };
 
     BABYLON.Engine.prototype.bindSamplers = function (effect) {

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
Babylon/jscompaktor.bat


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 8 - 2
Samples/babylon.js


+ 2 - 0
Samples/index.html

@@ -272,6 +272,8 @@
                         Change control method:
                         <button class="buttonControlPanel" id="touchCamera">Switch to touch camera</button>
                         <button class="buttonControlPanel" id="deviceOrientationCamera">Switch to device orientation camera</button>
+                        <button class="buttonControlPanel" id="toggleFxaa">Toggle FXAA (antialiasing)</button>
+                        <button class="buttonControlPanel" id="toggleBandW">Toggle Black and white</button>
                     </p>
                 </div>
                 <div class="cameraTag"><img src="Assets/camera.png" /></div>

+ 22 - 0
Samples/index.js

@@ -92,6 +92,7 @@
     var touchCamera = document.getElementById("touchCamera");
     var deviceOrientationCamera = document.getElementById("deviceOrientationCamera");
     var camerasList = document.getElementById("camerasList");
+    var toggleFxaa = document.getElementById("toggleFxaa");
 
     var sceneChecked;
 
@@ -589,6 +590,27 @@
         }
     });
 
+    toggleFxaa.addEventListener("click", function () {
+        if (scene && scene.activeCamera) {
+            if (scene.activeCamera.__fxaa_cookie) {
+                scene.activeCamera.__fxaa_cookie.dispose(),
+                scene.activeCamera.__fxaa_cookie = null;
+            } else {
+                scene.activeCamera.__fxaa_cookie = new BABYLON.FxaaPostProcess("fxaa", 1.0, scene.activeCamera);
+            }
+        }
+    });
+    toggleBandW.addEventListener("click", function () {
+        if (scene && scene.activeCamera) {
+            if (scene.activeCamera.__bandw_cookie) {
+                scene.activeCamera.__bandw_cookie.dispose(),
+                scene.activeCamera.__bandw_cookie = null;
+            } else {
+                scene.activeCamera.__bandw_cookie = new BABYLON.BlackAndWhitePostProcess("bandw", 1.0, scene.activeCamera);
+            }
+        }
+    });
+
     // Cameras
     camerasList.addEventListener("change", function (ev) {
         scene.activeCamera.detachControl(canvas);

+ 9 - 0
Samples/web.config

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <system.webServer>
+        <staticContent>
+            <mimeMap fileExtension=".fx" mimeType="application/shader" />
+            <mimeMap fileExtension=".babylon" mimeType="application/babylon" />
+        </staticContent>
+    </system.webServer>
+</configuration>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 19 - 0
babylon.js