Benjamin Guignabert 5 rokov pred
rodič
commit
15fcdee1de

+ 158 - 5
src/Materials/Textures/Filtering/hdrFiltering.ts

@@ -2,13 +2,31 @@ import { Vector3 } from "../../../Maths/math";
 import { InternalTexture } from "../internalTexture"
 import { HDRCubeTexture } from "../hdrCubeTexture"
 import { CubeTexture } from "../cubeTexture"
+import { Scene } from "../../../scene";
+import { Engine } from "../../../Engines/engine";
+import { Effect } from "../../../Materials/effect";
+import { Material } from "../../../Materials/material";
+import { DataBuffer } from "../../../Meshes/DataBuffer"
+import { VertexBuffer } from "../../../Meshes/Buffer"
+import "../../../Shaders/postprocess.vertex";
 /**
  * Filters HDR maps to get correct  renderings of PBR reflections
  */
 export class HDRFiltering {
 
-	constructor() {
+	private _scene: Scene;
+	private _engine: Engine;
+	private _effect: Effect;
+
+	private _numSamples: number = 128;
+
+	constructor(scene: Scene) {
 		// pass
+		this._scene = scene;
+		this._engine = scene.getEngine();
+
+		this.createEffect();
+		this._prepareBuffers();
 	}
 
 	private static _bits = new Uint32Array(1);
@@ -79,20 +97,155 @@ export class HDRFiltering {
 		for (let i = 0; i < nbRoughnessStops; i++) {
 			const roughness = i / nbRoughnessStops;
 			const kernel = HDRFiltering.generateSamples(samples, roughness);
+			const weights = HDRFiltering.generateWeights(kernel, roughness);
 
-			const filteredTexture = HDRFiltering.filter(texture, kernel);
+			const filteredTexture = HDRFiltering.filter(texture, kernel, weights);
 			mipmaps.push(filteredTexture);
 		}
 
 		HDRFiltering.setMipmaps(texture, mipmaps);
 	}
 
-	public static filter(texture: CubeTexture | HDRCubeTexture, kernel: Vector3[]) : InternalTexture {
-		// pass
-		return null as any;
+	public static filter(texture: CubeTexture | HDRCubeTexture, kernel: Vector3[], weights: number[]) : InternalTexture {
+		const sides = [
+
+		];
+
+		this.
+		for (let i = 0; i < sides.length; i++) {
+
+		}
 	}
 
 	public static setMipmaps(texture: CubeTexture | HDRCubeTexture, mipmaps: InternalTexture[]) {
 		// pass
 	}
+
+	public static flatten(arr: Vector3[]) : number[] {
+		const result = [];
+
+		for (let i = 0; i < arr.length; i++) {
+			result.push(arr[i].x, arr[i].y, arr[i].z);
+		}
+
+		return result;
+	}
+
+	/**
+	 * Binds all textures and uniforms to the shader, this will be run on every pass.
+	 * @returns the effect corresponding to this post process. Null if not compiled or not ready.
+	 */
+	public apply(texture: InternalTexture, kernel: Vector3[], weights: number[]) {
+	    // Check
+	    if (!this._effect || !this._effect.isReady()) {
+	        return null;
+	    }
+
+	    // States
+	    this._engine.enableEffect(this._effect);
+	    this._engine.setState(false);
+	    this._engine.setDepthBuffer(false);
+	    this._engine.setDepthWrite(false);
+
+	    this._effect._bindTexture("inputTexture", texture);
+
+	    // Parameters
+	    this._effect.setArray3("sampleDirections", HDRFiltering.flatten(kernel));
+	    this._effect.setArray("weights", weights);
+
+	    return this._effect;
+	}
+
+	/**
+	 * Get a value indicating if the post-process is ready to be used
+	 * @returns true if the post-process is ready (shader is compiled)
+	 */
+	public isReady(): boolean {
+	    return this._effect && this._effect.isReady();
+	}
+
+	/**
+	 * Updates the effect with the current post process compile time values and recompiles the shader.
+	 * @param defines Define statements that should be added at the beginning of the shader. (default: null)
+	 * @param uniforms Set of uniform variables that will be passed to the shader. (default: null)
+	 * @param samplers Set of Texture2D variables that will be passed to the shader. (default: null)
+	 * @param indexParameters The index parameters to be used for babylons include syntax "#include<kernelBlurVaryingDeclaration>[0..varyingCount]". (default: undefined) See usage in babylon.blurPostProcess.ts and kernelBlur.vertex.fx
+	 * @param onCompiled Called when the shader has been compiled.
+	 * @param onError Called if there is an error when compiling a shader.
+	 */
+	public createEffect() {
+		const defines = "#define NUM_SAMPLES " + this._numSamples;
+	    this._effect = this._engine.createEffect({ vertex: "postprocess", fragment: "hdrFiltering" },
+	        ["position"],
+	        ["sampleDirections", "weights"],
+	        ["inputTexture"],
+	        defines
+	    );
+	}
+
+   /**
+     * Manually render a set of post processes to a texture.
+     * @param targetTexture The target texture to render to.
+     * @param faceIndex defines the face to render to if a cubemap is defined as the target
+     * @param lodLevel defines which lod of the texture to render to
+     */
+    public directRender(targetTexture: InternalTexture, faceIndex = 0, lodLevel = 0): void {
+    	if (!this.isReady()) {
+    		return;
+    	}
+
+        var engine = this._engine;
+        engine.bindFramebuffer(targetTexture, faceIndex, undefined, undefined, false, lodLevel);
+
+        this.apply();
+
+        // VBOs
+        this._prepareBuffers();
+        engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
+        engine.drawElementsType(Material.TriangleFillMode, 0, 6);
+
+        engine.restoreDefaultFramebuffer();
+        // Restore depth buffer
+	    engine.setState(true);
+        engine.setDepthBuffer(true);
+        engine.setDepthWrite(true);
+    }
+
+    private _indexBuffer: DataBuffer;
+    private _vertexBuffers: { [key: string]: VertexBuffer } = {};
+
+    /**
+     * Creates a new instance PostProcess
+     * @param scene The scene that the post process is associated with.
+     */
+    private _prepareBuffers(): void {
+        if (this._vertexBuffers[VertexBuffer.PositionKind]) {
+            return;
+        }
+
+        // VBO
+        var vertices = [];
+        vertices.push(1, 1);
+        vertices.push(-1, 1);
+        vertices.push(-1, -1);
+        vertices.push(1, -1);
+
+        this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(this._scene.getEngine(), vertices, VertexBuffer.PositionKind, false, false, 2);
+
+        this._buildIndexBuffer();
+    }
+
+    private _buildIndexBuffer(): void {
+        // Indices
+        var indices = [];
+        indices.push(0);
+        indices.push(1);
+        indices.push(2);
+
+        indices.push(0);
+        indices.push(2);
+        indices.push(3);
+
+        this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
+    }
 }

+ 23 - 17
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -186,31 +186,37 @@
     uniform vec3 sampleDirections[NUM_SAMPLES];
     uniform float weights[NUM_SAMPLES];
     vec4 sampleUnfiltered(samplerCube sampler, vec3 direction, float lod) {
-        // Making a transformation matrix to convert z oriented sample direction to input direction
-        vec3 tangent = abs(direction.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
-        tangent = normalize(cross(tangent, direction));
-        vec3 bitangent = cross(direction, tangent);
-        mat3 tbn = mat3(tangent, bitangent, direction);
+        // Rotation by PI around y is necessary for consistency with IBLBaker
+        vec3 n = vec3(-direction.x, direction.y, -direction.z);
+        vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
+        tangent = normalize(cross(tangent, n));
+        vec3 bitangent = cross(n, tangent);
+        mat3 tbn = mat3(tangent, bitangent, n);
 
         vec3 color = vec3(0.);
-        vec3 v;
-        float vDotD;
+        vec3 h;
+        vec3 l;
+        float NoH;
+        float NoL;
         float totalWeight = 0.;
         for (int i = 0; i < NUM_SAMPLES; i++) {
-            v = tbn * sampleDirections[i];
-            // v = 2. * dot(v, direction) * v - direction;
-            vDotD = clamp(dot(v, direction), 0.0, 1.0);
-            float solidAngleTexel = 4. * 3.14159 / (6. * 512. * 512.);
-            float solidAngleSample = 1.0 / (float(NUM_SAMPLES) * weights[i]);
-            float lod = 0.5 * log2(solidAngleSample/solidAngleTexel) + 1.;
-            if (vDotD > 0.) {
+            h = tbn * sampleDirections[i];
+            l = 2. * dot(h, n) * h - n;
+            NoH = clamp(dot(h, n), 0.0, 1.0);
+            NoL = clamp(dot(l, n), 0.0, 1.0);
+            if (NoL > 0.) {
+                float solidAngleTexel = 4. * 3.14159 / (6. * 128. * 128.);
+                float solidAngleSample = 4.0 / (float(NUM_SAMPLES) * weights[i]);
+                float lod = 0.5 * log2(solidAngleSample/solidAngleTexel);
                 // gamma correction needed ?
-                color += textureCubeLodEXT(sampler, v, lod).xyz * vDotD;
-                totalWeight += vDotD;            
+                color += textureCubeLodEXT(sampler, l, lod).xyz * NoL;
+                totalWeight += NoL;            
             }
         }
 
-        color /= totalWeight;
+        if (totalWeight != 0.) {
+            color /= totalWeight;
+        }
         return vec4(color, 1.0);
     }