Преглед изворни кода

Merge pull request #4930 from CraigFeldspar/master

Tweaking and fixing SSAO2
sebavan пре 7 година
родитељ
комит
65020373a1

+ 2 - 0
dist/preview release/what's new.md

@@ -174,6 +174,8 @@
 - Angular and linear velocity were using the wrong method to copy values to the physics engine ([RaananW](https://github.com/RaananW))
 - Fixed env texture generation in Byte Mode ([sebavan](http://www.github.com/sebavan))
 - Oimo.js now receives quaternion and not euler when a body is being constructed ([RaananW](https://github.com/RaananW))
+- Improving visual quality on SSAO2 shader ([CraigFeldspar](https://github.com/CraigFeldspar))
+- Fixed a bug where changing the sample count on `PostProcess` would not update the WebGL Texture ([CraigFeldspar](https://github.com/CraigFeldspar))
 
 ### Viewer
 

+ 71 - 31
src/PostProcess/RenderPipeline/Pipelines/babylon.ssao2RenderingPipeline.ts

@@ -41,11 +41,39 @@
         @serialize()
         public minZAspect: number = 0.2;
 
+        @serialize("samples")
+        private _samples: number = 8;
         /**
         * Number of samples used for the SSAO calculations. Default value is 8
         */
-        @serialize("samples")
-        private _samples: number = 8;
+        public set samples(n: number) {
+            this._ssaoPostProcess.updateEffect("#define SAMPLES " + n + "\n#define SSAO");
+            this._samples = n;
+            this._sampleSphere = this._generateHemisphere();
+
+            this._firstUpdate = true;
+        }
+        public get samples(): number {
+            return this._samples;
+        }
+
+        @serialize("textureSamples")
+        private _textureSamples: number = 1;
+        /**
+        * Number of samples to use for antialiasing
+        */
+        public set textureSamples(n: number) {
+            this._textureSamples = n;
+
+            this._originalColorPostProcess.samples = n;
+            this._blurHPostProcess.samples = n;
+            this._blurVPostProcess.samples = n;
+            this._ssaoPostProcess.samples = n;
+            this._ssaoCombinePostProcess.samples = n;
+        }
+        public get textureSamples(): number {
+            return this._textureSamples;
+        }
 
         /**
          * Ratio object used for SSAO ratio and blur ratio
@@ -63,18 +91,6 @@
         */
         private _samplerOffsets: number[];
 
-        public set samples(n: number) {
-            this._ssaoPostProcess.updateEffect("#define SAMPLES " + n + "\n#define SSAO");
-            this._samples = n;
-            this._sampleSphere = this._generateHemisphere();
-
-            this._firstUpdate = true;
-        }
-
-        public get samples(): number {
-            return this._samples;
-        }
-
         /**
         * Are we using bilateral blur ?
         */
@@ -104,7 +120,7 @@
         * The final result is "base + ssao" between [0, 1]
         */
         @serialize()
-        public base: number = 0.1;
+        public base: number = 0;
 
         /**
         *  Support test.
@@ -158,6 +174,7 @@
             this._normalTexture = geometryBufferRenderer.getGBuffer().textures[1];
 
             this._originalColorPostProcess = new PassPostProcess("SSAOOriginalSceneColor", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._originalColorPostProcess.samples = this.textureSamples;
             this._createSSAOPostProcess(1.0);
             this._createBlurPostProcess(ssaoRatio, blurRatio);
             this._createSSAOCombinePostProcess(blurRatio);
@@ -245,6 +262,9 @@
                     this._firstUpdate = false;
                 }
             };
+
+            this._blurHPostProcess.samples = this.textureSamples;
+            this._blurVPostProcess.samples = this.textureSamples;
         }
 
         /** @hidden */
@@ -254,26 +274,44 @@
             super._rebuild();
         }
 
+        private _bits = new Uint32Array(1);
+
+        //Van der Corput radical inverse
+        private _radicalInverse_VdC(i: number) {
+            this._bits[0] = i;
+            this._bits[0] = ((this._bits[0] << 16) | (this._bits[0] >> 16)) >>> 0;
+            this._bits[0] = ((this._bits[0] & 0x55555555) << 1) | ((this._bits[0] & 0xAAAAAAAA) >>> 1) >>> 0;
+            this._bits[0] = ((this._bits[0] & 0x33333333) << 2) | ((this._bits[0] & 0xCCCCCCCC) >>> 2) >>> 0;
+            this._bits[0] = ((this._bits[0] & 0x0F0F0F0F) << 4) | ((this._bits[0] & 0xF0F0F0F0) >>> 4) >>> 0;
+            this._bits[0] = ((this._bits[0] & 0x00FF00FF) << 8) | ((this._bits[0] & 0xFF00FF00) >>> 8) >>> 0;
+            return this._bits[0] * 2.3283064365386963e-10; // / 0x100000000 or / 4294967296
+        }
+
+        private _hammersley(i: number, n: number) {
+            return [i / n, this._radicalInverse_VdC(i)];
+        }
+
+        private _hemisphereSample_uniform(u: number, v: number): Vector3 {
+            var phi = v * 2.0 * Math.PI;
+            // rejecting samples that are close to tangent plane to avoid z-fighting artifacts
+            var cosTheta = 1.0 - (u * 0.85 + 0.15); 
+            var sinTheta = Math.sqrt(1.0 - cosTheta * cosTheta);
+            return new Vector3(Math.cos(phi) * sinTheta, Math.sin(phi) * sinTheta, cosTheta );
+        }
+
         private _generateHemisphere(): number[] {
             var numSamples = this.samples;
             var result = [];
-            var vector, scale;
-
-            var rand = (min: number, max: number) => {
-                return Math.random() * (max - min) + min;
-            }
+            var vector;
 
             var i = 0;
             while (i < numSamples) {
-                vector = new Vector3(
-                    rand(-1.0, 1.0),
-                    rand(-1.0, 1.0),
-                    rand(0.30, 1.0));
-                vector.normalize();
-                scale = i / numSamples;
-                scale = Scalar.Lerp(0.1, 1.0, scale * scale);
-                vector.scaleInPlace(scale);
-
+                if (numSamples < 16) {
+                    vector = this._hemisphereSample_uniform(Math.random(), Math.random());
+                } else {
+                    var rand = this._hammersley(i, numSamples);
+                    vector = this._hemisphereSample_uniform(rand[0], rand[1]);
+                }
 
                 result.push(vector.x, vector.y, vector.z);
                 i++;
@@ -301,7 +339,7 @@
             this._ssaoPostProcess.onApply = (effect: Effect) => {
                 if (this._firstUpdate) {
                     effect.setArray3("sampleSphere", this._sampleSphere);
-                    effect.setFloat("randTextureTiles", 4.0);
+                    effect.setFloat("randTextureTiles", 32.0);
                 }
 
                 if (!this._scene.activeCamera) {
@@ -325,6 +363,7 @@
                 effect.setTexture("normalSampler", this._normalTexture);
                 effect.setTexture("randomSampler", this._randomTexture);
             };
+            this._ssaoPostProcess.samples = this.textureSamples;
         }
 
         private _createSSAOCombinePostProcess(ratio: number): void {
@@ -337,10 +376,11 @@
                 effect.setVector4("viewport", Tmp.Vector4[0].copyFromFloats(viewport.x, viewport.y, viewport.width, viewport.height));
                 effect.setTextureFromPostProcess("originalColor", this._originalColorPostProcess);
             };
+            this._ssaoCombinePostProcess.samples = this.textureSamples;
         }
 
         private _createRandomTexture(): void {
-            var size = 512;
+            var size = 128;
 
             this._randomTexture = new DynamicTexture("SSAORandomTexture", size, this._scene, false, Texture.TRILINEAR_SAMPLINGMODE);
             this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;

+ 16 - 1
src/PostProcess/babylon.postProcess.ts

@@ -73,10 +73,25 @@
         * Force textures to be a power of two (default: false)
         */
         public alwaysForcePOT = false;
+        
+        private _samples = 1;
         /**
         * Number of sample textures (default: 1)
         */
-        public samples = 1;
+        public get samples () {
+            return this._samples;
+        }
+
+        public set samples (n: number) {
+            this._samples = n;
+
+            this._textures.forEach(texture => {
+                if (texture.samples !== this._samples) {
+                    this._engine.updateRenderTargetTextureSampleCount(texture, this._samples);
+                }
+            });
+        }
+
         /**
         * Modify the scale of the post process to be the same as the viewport (default: false)
         */

+ 27 - 13
src/Shaders/ssao2.fragment.fx

@@ -5,6 +5,25 @@ uniform float near;
 uniform float far;
 uniform float radius;
 
+float scales[16] = float[16](
+0.1,
+0.11406250000000001,
+0.131640625,
+0.15625,
+0.187890625,
+0.2265625,
+0.272265625,
+0.325,
+0.384765625,
+0.4515625,
+0.525390625,
+0.60625,
+0.694140625,
+0.7890625,
+0.891015625,
+1.0
+);
+
 varying vec2 vUV;
 
 float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {
@@ -51,20 +70,19 @@ void main()
 	vec3 origin = vViewRay * depth;
 	vec3 rvec = random * 2.0 - 1.0;
 	rvec.z = 0.0;
+
+	// Avoid numerical precision issue while applying Gram-Schmidt
+	float dotProduct = dot(rvec, normal);
+	rvec = 1.0 - abs(dotProduct) > 1e-2 ? rvec : vec3(-rvec.y, 0.0, rvec.x);
 	vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
 	vec3 bitangent = cross(normal, tangent);
 	mat3 tbn = mat3(tangent, bitangent, normal);
 
 	float difference;
 
-	if (depth > maxZ) {
-		gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
-		return;
-	}
-
 	for (int i = 0; i < SAMPLES; ++i) {
 		// get sample position:
-	   vec3 samplePosition = tbn * sampleSphere[i];
+	   vec3 samplePosition = scales[(i + int(random.x * 16.0)) % 16] * tbn * sampleSphere[(i + int(random.y * 16.0)) % 16];
 	   samplePosition = samplePosition * correctedRadius + origin;
 	  
 		// project sample position:
@@ -80,15 +98,11 @@ void main()
 		// get sample linearDepth:
 	   float sampleDepth = abs(texture2D(textureSampler, offset.xy).r);
 		// range check & accumulate:
-	   float rangeCheck = abs(depth - sampleDepth) < correctedRadius ? 1.0 : 0.0;
 	   difference = depthSign * samplePosition.z - sampleDepth;
-	  //occlusion += step(fallOff, difference) * (1.0 - smoothstep(fallOff, area, difference)) * rangeCheck;
-	   occlusion += (difference >= 1e-5 ? 1.0 : 0.0) * rangeCheck;
+	   float rangeCheck = 1.0 - smoothstep(correctedRadius*0.5, correctedRadius, difference);
+	   occlusion += (difference >= 0.0 ? 1.0 : 0.0) * rangeCheck;
 	}
-
-
-	// float screenEdgeFactor = clamp(vUV.x * 10.0, 0.0, 1.0) * clamp(vUV.y * 10.0, 0.0, 1.0) * clamp((1.0 - vUV.x) * 10.0, 0.0, 1.0) * clamp((1.0 - vUV.y) * 10.0, 0.0, 1.0);
-
+	occlusion = occlusion*(1.0 - smoothstep(maxZ * 0.75, maxZ, depth));
 	float ao = 1.0 - totalStrength * occlusion * samplesFactor;
 	float result = clamp(ao + base, 0.0, 1.0);
 	gl_FragColor = vec4(vec3(result), 1.0);