Explorar o código

adding backbone of filtering API

Benjamin Guignabert %!s(int64=5) %!d(string=hai) anos
pai
achega
f79d5741bd

+ 14 - 52
src/Materials/PBR/pbrBaseMaterial.ts

@@ -35,6 +35,7 @@ import { Constants } from "../../Engines/constants";
 import { IAnimatable } from '../../Animations/animatable.interface';
 
 import "../../Materials/Textures/baseTexture.polynomial";
+import "../../Materials/Textures/baseTexture.prefiltering";
 import "../../Shaders/pbr.fragment";
 import "../../Shaders/pbr.vertex";
 import { EffectFallbacks } from '../effectFallbacks';
@@ -54,8 +55,7 @@ export class PBRMaterialDefines extends MaterialDefines
     IMaterialSubSurfaceDefines {
     public PBR = true;
 
-    // Debug
-    public NUM_SAMPLES = 16;
+    public NUM_SAMPLES = 0;
     public DEBUG_REALTIME_SAMPLING = false;
 
     public MAINUV1 = false;
@@ -798,42 +798,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         return "PBRBaseMaterial";
     }
 
-    public sampleDirections = [0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1,
-                               0, 0, 1
-                               ];
-
-    public weights = [1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-                        1/16,
-    ];
-
     public debugRealtimeSampling = false;
 
     /**
@@ -1076,17 +1040,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     private _prepareEffect(mesh: AbstractMesh, defines: PBRMaterialDefines, onCompiled: Nullable<(effect: Effect) => void> = null, onError: Nullable<(effect: Effect, errors: string) => void> = null, useInstances: Nullable<boolean> = null, useClipPlane: Nullable<boolean> = null): Nullable<Effect> {
         this._prepareDefines(mesh, defines, useInstances, useClipPlane);
 
-        if (defines.NUM_SAMPLES != this.sampleDirections.length / 3) {
-            defines.markAsUnprocessed();
-            defines.NUM_SAMPLES = this.sampleDirections.length / 3;
-        }
-
-        if (defines.DEBUG_REALTIME_SAMPLING != this.debugRealtimeSampling) {
-            defines.markAsUnprocessed();
-            defines.DEBUG_REALTIME_SAMPLING = this.debugRealtimeSampling;
-        }
-
-
         if (!defines.isDirty) {
             return null;
         }
@@ -1331,6 +1284,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
                     defines.LINEARSPECULARREFLECTION = reflectionTexture.linearSpecularLOD;
 
+                    if (reflectionTexture.realTimeFiltering) {
+                        defines.NUM_SAMPLES = reflectionTexture.numSamples;
+                        defines.DEBUG_REALTIME_SAMPLING = true;
+                    }
+                    
                     if (reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE) {
                         defines.INVERTCUBICMAP = true;
                     }
@@ -1707,9 +1665,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             this.bindViewProjection(effect);
             reflectionTexture = this._getReflectionTexture();
 
-            effect.setArray3("sampleDirections", this.sampleDirections);
-            effect.setArray("weights", this.weights);
-
             if (!ubo.useUbo || !this.isFrozen || !ubo.isSync) {
 
                 // Texture uniforms
@@ -1740,6 +1695,13 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize);
                         }
 
+                        if (reflectionTexture.realTimeFiltering) {
+                            // TODO move this into pbrbasematerial
+                            reflectionTexture.generateFilterSamples(this._roughness!);           
+                            effect.setArray3("sampleDirections", reflectionTexture._sampleDirections);
+                            effect.setArray("weights", reflectionTexture._sampleWeights);
+                        }
+
                         if (!defines.USEIRRADIANCEMAP) {
                             var polynomials = reflectionTexture.sphericalPolynomial;
                             if (defines.USESPHERICALFROMREFLECTIONMAP && polynomials) {

+ 98 - 0
src/Materials/Textures/Filtering/hdrFiltering.ts

@@ -0,0 +1,98 @@
+import { Vector3 } from "../../../Maths/math";
+import { InternalTexture } from "../internalTexture"
+import { HDRCubeTexture } from "../hdrCubeTexture"
+import { CubeTexture } from "../cubeTexture"
+/**
+ * Filters HDR maps to get correct  renderings of PBR reflections
+ */
+export class HDRFiltering {
+
+	constructor() {
+		// pass
+	}
+
+	private static _bits = new Uint32Array(1);
+
+	private static _radicalInverse_VdC(i: number) {
+	    HDRFiltering._bits[0] = i;
+	    HDRFiltering._bits[0] = ((HDRFiltering._bits[0] << 16) | (HDRFiltering._bits[0] >> 16)) >>> 0;
+	    HDRFiltering._bits[0] = ((HDRFiltering._bits[0] & 0x55555555) << 1) | ((HDRFiltering._bits[0] & 0xAAAAAAAA) >>> 1) >>> 0;
+	    HDRFiltering._bits[0] = ((HDRFiltering._bits[0] & 0x33333333) << 2) | ((HDRFiltering._bits[0] & 0xCCCCCCCC) >>> 2) >>> 0;
+	    HDRFiltering._bits[0] = ((HDRFiltering._bits[0] & 0x0F0F0F0F) << 4) | ((HDRFiltering._bits[0] & 0xF0F0F0F0) >>> 4) >>> 0;
+	    HDRFiltering._bits[0] = ((HDRFiltering._bits[0] & 0x00FF00FF) << 8) | ((HDRFiltering._bits[0] & 0xFF00FF00) >>> 8) >>> 0;
+	    return HDRFiltering._bits[0] * 2.3283064365386963e-10; // / 0x100000000 or / 4294967296
+	}
+
+	private static _hammersley(i: number, n: number) {
+	    return [i / n, HDRFiltering._radicalInverse_VdC(i)];
+	}
+
+	private static _GGXImportanceSampling(u: number, v: number, alphaG: number) : Vector3 {
+	    // https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
+	    const phi = v * 2.0 * Math.PI;
+	    const theta = Math.atan(alphaG * Math.sqrt(u) / Math.sqrt(1 - u))
+	    return new Vector3(Math.cos(phi) * Math.sin(theta), Math.sin(phi) * Math.sin(theta), Math.cos(theta));
+	}
+
+	private static _convertRoughnessToAlphaG(roughness: number) : number {
+	    return roughness * roughness + 0.0005;
+	}
+
+	public static generateSamples(numSamples: number, roughness: number): Vector3[] {
+	    const result = [];
+	    let vector;
+
+	    for (let i = 0; i < numSamples; i++) {
+	        const rand = HDRFiltering._hammersley(i, numSamples);
+	        vector = HDRFiltering._GGXImportanceSampling(rand[0], rand[1], HDRFiltering._convertRoughnessToAlphaG(roughness));
+
+	        result.push(vector);
+	    }
+
+	    return result;
+	}
+
+	public static generateWeights(samples: Vector3[], roughness: number): number[] {
+	    // float a2 = square(alphaG);
+	    // float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
+	    // return a2 / (PI * d * d);
+	    const result = [];
+	    const alphaG = HDRFiltering._convertRoughnessToAlphaG(roughness);
+
+	    for (let i = 0; i < samples.length; i++) {
+	        const a2 = alphaG * alphaG;
+	        // NdotH = samples[i].z
+	        const d = samples[i].z * samples[i].z * (a2 - 1) + 1;
+	        result.push(a2  / (Math.PI * d * d));
+	    }
+
+	    return result;
+	}
+
+	// Todo merge hdrCubeTexture with CubeTexture
+	public static prefilter(texture: CubeTexture | HDRCubeTexture) {
+		//
+		const nbRoughnessStops = 10;
+		const samples = 1024;
+		const mipmaps: InternalTexture[] = [];
+
+		for (let i = 0; i < nbRoughnessStops; i++) {
+			const roughness = i / nbRoughnessStops;
+			const kernel = HDRFiltering.generateSamples(samples, roughness);
+
+			const filteredTexture = HDRFiltering.filter(texture, kernel);
+			mipmaps.push(filteredTexture);
+		}
+
+		HDRFiltering.setMipmaps(texture, mipmaps);
+	}
+
+	public static filter(texture: CubeTexture | HDRCubeTexture, kernel: Vector3[]) : InternalTexture {
+		// pass
+		return null as any;
+	}
+
+	public static setMipmaps(texture: CubeTexture | HDRCubeTexture, mipmaps: InternalTexture[]) {
+		// pass
+	}
+}

+ 72 - 0
src/Materials/Textures/baseTexture.prefiltering.ts

@@ -0,0 +1,72 @@
+import { BaseTexture } from "./baseTexture";
+import { HDRFiltering } from "./Filtering/hdrFiltering";
+import { Constants } from "../../Engines/constants";
+import { Vector3 } from "../../Maths/math";
+
+declare module "./baseTexture" {
+    export interface BaseTexture {
+        /**
+         * Only turn this on for dynamic HDR textures (real-time probes)
+         */
+        realTimeFiltering: boolean;
+        _realTimeFiltering: boolean;
+        numSamples: number;
+        _numSamples: number;
+        _sampleDirections: number[];
+        _sampleDirectionV3: Vector3[]; // debug, remove
+        _sampleWeights: number[];
+        generateFilterSamples(roughness: number) : void;
+    }
+}
+
+BaseTexture.prototype._realTimeFiltering = false;
+BaseTexture.prototype._numSamples = 16;
+
+Object.defineProperty(BaseTexture.prototype, "numSamples", {
+    get: function(this: BaseTexture) {
+    	return this._numSamples;
+    },
+    set: function(this: BaseTexture, value: number) {
+    	let scene = this.getScene();
+    	this._numSamples = value;
+    	if (!scene) {
+    		return;
+    	}
+    	scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag, (mat) => {
+    	    return mat.hasTexture(this);
+    	});
+
+    },
+    enumerable: true,
+    configurable: true
+});
+
+Object.defineProperty(BaseTexture.prototype, "realTimeFiltering", {
+    get: function(this: BaseTexture) {
+    	return this._realTimeFiltering;
+    },
+    set: function(this: BaseTexture, value: boolean) {
+    	let scene = this.getScene();
+    	this._realTimeFiltering = value;
+    	if (!scene) {
+    		return;
+    	}
+    	scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag, (mat) => {
+    	    return mat.hasTexture(this);
+    	});
+
+    },
+    enumerable: true,
+    configurable: true
+});
+
+BaseTexture.prototype.generateFilterSamples = function(roughness) {
+	const sampleDirections = HDRFiltering.generateSamples(this._numSamples, roughness);
+	this._sampleDirectionV3 = sampleDirections;
+	this._sampleWeights = HDRFiltering.generateWeights(sampleDirections, roughness);
+	this._sampleDirections = [];
+
+	for (let i = 0; i < sampleDirections.length; i++) {
+		this._sampleDirections.push(sampleDirections[i].x, sampleDirections[i].y, sampleDirections[i].z);
+	}
+}

+ 1 - 0
src/Materials/Textures/index.ts

@@ -1,5 +1,6 @@
 export * from "./baseTexture";
 export * from "./baseTexture.polynomial";
+export * from "./baseTexture.prefiltering";
 export * from "./colorGradingTexture";
 export * from "./cubeTexture";
 export * from "./dynamicTexture";