Pārlūkot izejas kodu

diffusion profiles as uniforms and serialization

Benjamin Guignabert 5 gadi atpakaļ
vecāks
revīzija
3579939820

+ 1 - 0
src/Materials/PBR/pbrSubSurfaceConfiguration.ts

@@ -77,6 +77,7 @@ export class PBRSubSurfaceConfiguration {
      * Diffusion profile for subsurface scattering.
      * Useful for better scattering in the skins or foliages.
      */
+    @serialize()
     public scatteringDiffusionProfileIndex = 0;
 
     /**

+ 3 - 66
src/PostProcesses/SubSurfaceScatteringPostProcess.ts

@@ -5,7 +5,6 @@ import { Texture } from "../Materials/Textures/texture";
 import { PostProcess, PostProcessOptions } from "./postProcess";
 import { Engine } from "../Engines/engine";
 import { Scene } from "../scene";
-import { Color3 } from "../Maths/math.color";
 import { Constants } from "../Engines/constants";
 import { Logger } from "../Misc/logger";
 
@@ -21,10 +20,6 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
     /** @hidden */
     public texelHeight: number;
 
-    private _diffusionS: number[] = [];
-    private _filterRadii: number[] = [];
-    private _diffusionD: number[] = [];
-
     constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera> = null, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT) {
         super(name, "subSurfaceScattering", ["texelSize", "viewportSize", "metersPerUnit"], ["diffusionS", "diffusionD", "filterRadii", "irradianceSampler", "depthSampler", "albedoSampler"], options, camera, samplingMode || Texture.BILINEAR_SAMPLINGMODE, engine, reusable, null, textureType, "postprocess", undefined, true);
         this._scene = scene;
@@ -32,9 +27,6 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
         const defines = this._getDefines();
         this.updateEffect(defines);
 
-        // Adding default diffusion profile
-        this.addDiffusionProfile(new Color3(1, 1, 1));
-
         this.onApplyObservable.add((effect: Effect) => {
             if (!scene.prePassRenderer) {
                 Logger.Error("PrePass needs to be enabled for subsurface scattering.");
@@ -49,9 +41,9 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
             effect.setFloat2("viewportSize",
                 Math.tan(scene.activeCamera!.fov / 2) * scene.getEngine().getAspectRatio(scene.activeCamera!, true),
                 Math.tan(scene.activeCamera!.fov / 2));
-            effect.setArray3("diffusionS", this._diffusionS);
-            effect.setArray("diffusionD", this._diffusionD);
-            effect.setArray("filterRadii", this._filterRadii);
+            effect.setArray3("diffusionS", scene.prePassRenderer.ssDiffusionS);
+            effect.setArray("diffusionD", scene.prePassRenderer.ssDiffusionD);
+            effect.setArray("filterRadii", scene.prePassRenderer.ssFilterRadii);
         });
 
     }
@@ -71,59 +63,4 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
 
         return defines;
     }
-
-    public addDiffusionProfile(color: Color3) : number {
-        if (this._diffusionD.length >= 5) {
-            // We only suppport 5 diffusion profiles
-            Logger.Error("You already reached the maximum number of diffusion profiles.");
-            return -1;
-        }
-
-        // Do not add doubles
-        for (let i = 0; i < this._diffusionS.length / 3; i++) {
-            if (this._diffusionS[i * 3] === color.r && 
-                this._diffusionS[i * 3 + 1] === color.g && 
-                this._diffusionS[i * 3 + 2] === color.b) {
-                return i;
-            }
-        }
-
-        this._diffusionS.push(color.r, color.b, color.g);
-        this._diffusionD.push(Math.max(Math.max(color.r, color.b), color.g));
-        this._filterRadii.push(this.getDiffusionProfileParameters(color));
-
-        return this._diffusionD.length - 1;
-    }
-
-    public getDiffusionProfileParameters(color: Color3)
-    {
-        const cdf = 0.997;
-        // Importance sample the normalized diffuse reflectance profile for the computed value of 's'.
-        // ------------------------------------------------------------------------------------
-        // R[r, phi, s]   = s * (Exp[-r * s] + Exp[-r * s / 3]) / (8 * Pi * r)
-        // PDF[r, phi, s] = r * R[r, phi, s]
-        // CDF[r, s]      = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
-        // ------------------------------------------------------------------------------------
-        // We importance sample the color channel with the widest scattering distance.
-        const maxScatteringDistance = Math.max(color.r, color.g, color.b);
-
-        return this._sampleBurleyDiffusionProfile(cdf, maxScatteringDistance);
-    }
-    // https://zero-radiance.github.io/post/sampling-diffusion/
-    // Performs sampling of a Normalized Burley diffusion profile in polar coordinates.
-    // 'u' is the random number (the value of the CDF): [0, 1).
-    // rcp(s) = 1 / ShapeParam = ScatteringDistance.
-    // Returns the sampled radial distance, s.t. (u = 0 -> r = 0) and (u = 1 -> r = Inf).
-    private _sampleBurleyDiffusionProfile(u: number, rcpS: number)
-    {
-        u = 1 - u; // Convert CDF to CCDF
-
-        let g = 1 + (4 * u) * (2 * u + Math.sqrt(1 + (4 * u) * u));
-        let n = Math.pow(g, -1.0 / 3.0);                      // g^(-1/3)
-        let p = (g * n) * n;                                   // g^(+1/3)
-        let c = 1 + p + n;                                     // 1 + g^(+1/3) + g^(-1/3)
-        let x = 3 * Math.log(c / (4 * u));
-
-        return x * rcpS;
-    }
 }

+ 98 - 5
src/Rendering/prePassRenderer.ts

@@ -8,6 +8,7 @@ import { SubSurfaceScatteringPostProcess } from "../PostProcesses/subSurfaceScat
 import { Effect } from "../Materials/effect";
 import { Logger } from "../Misc/logger";
 import { _DevTools } from '../Misc/devTools';
+import { Color3 } from "../Maths/math.color";
 
 export class PrePassRenderer {
     /** @hidden */
@@ -31,6 +32,10 @@ export class PrePassRenderer {
     private _defaultAttachments: number[];
     private _clearAttachments: number[];
 
+    public ssDiffusionS: number[] = [];
+    public ssFilterRadii: number[] = [];
+    public ssDiffusionD: number[] = [];
+
     public sceneCompositorPostProcess: SceneCompositorPostProcess;
     public subSurfaceScatteringPostProcess: SubSurfaceScatteringPostProcess;
     private _enabled: boolean = false;
@@ -39,23 +44,47 @@ export class PrePassRenderer {
         return this._enabled;
     }
 
+    public get samples() {
+        return this.prePassRT.samples;
+    }
+
+    public set samples(n: number) {
+        this.prePassRT.samples = n;
+    }
+
     constructor(scene: Scene) {
         this._scene = scene;
         this._engine = scene.getEngine();
+        PrePassRenderer._SceneComponentInitialization(this._scene);
 
         this.prePassRT = new MultiRenderTarget("sceneprePassRT", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, this.mrtCount, this._scene,
             { generateMipMaps: false, generateDepthTexture: true, defaultType: Constants.TEXTURETYPE_UNSIGNED_INT, types: this._mrtTypes });
         this.prePassRT.samples = 1;
 
-        let gl = this._engine._gl;
-        this._clearAttachments = [gl.NONE, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT3];
-        this._multiRenderAttachments = [gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT3];
-        this._defaultAttachments = [gl.COLOR_ATTACHMENT0, gl.NONE, gl.NONE, gl.NONE];
+        this._initializeAttachments();
+
+        // Adding default diffusion profile
+        this.addDiffusionProfile(new Color3(1, 1, 1));
         this.sceneCompositorPostProcess = new SceneCompositorPostProcess("sceneCompositor", 1, null, undefined, this._engine);
         this.sceneCompositorPostProcess.inputTexture = this.prePassRT.getInternalTexture()!;
         this.subSurfaceScatteringPostProcess = new SubSurfaceScatteringPostProcess("subSurfaceScattering", this._scene, 1, null, undefined, this._engine);
+    }
 
-        PrePassRenderer._SceneComponentInitialization(this._scene);
+    private _initializeAttachments() {
+        let gl = this._engine._gl;
+
+        this._multiRenderAttachments = [];
+        this._clearAttachments = [gl.NONE];
+        this._defaultAttachments = [gl.COLOR_ATTACHMENT0];
+
+        for (let i = 0; i < this.mrtCount; i++) {
+            this._multiRenderAttachments.push((<any>gl)["COLOR_ATTACHMENT" + i]);
+
+            if (i > 0) {
+                this._clearAttachments.push((<any>gl)["COLOR_ATTACHMENT" + i])
+                this._defaultAttachments.push(gl.NONE)
+            }
+        }
     }
 
     public get isSupported() {
@@ -166,6 +195,70 @@ export class PrePassRenderer {
         }
     }
 
+
+    public addDiffusionProfile(color: Color3) : number {
+        if (this.ssDiffusionD.length >= 5) {
+            // We only suppport 5 diffusion profiles
+            Logger.Error("You already reached the maximum number of diffusion profiles.");
+            return -1;
+        }
+
+        // Do not add doubles
+        for (let i = 0; i < this.ssDiffusionS.length / 3; i++) {
+            if (this.ssDiffusionS[i * 3] === color.r && 
+                this.ssDiffusionS[i * 3 + 1] === color.g && 
+                this.ssDiffusionS[i * 3 + 2] === color.b) {
+                return i;
+            }
+        }
+
+        this.ssDiffusionS.push(color.r, color.b, color.g);
+        this.ssDiffusionD.push(Math.max(Math.max(color.r, color.b), color.g));
+        this.ssFilterRadii.push(this.getDiffusionProfileParameters(color));
+        this._scene.ssDiffusionProfileColors.push(color);
+
+        return this.ssDiffusionD.length - 1;
+    }
+
+    public clearAllDiffusionProfiles() {
+        this.ssDiffusionD = [];
+        this.ssDiffusionS = [];
+        this.ssFilterRadii = [];
+        this._scene.ssDiffusionProfileColors = [];
+    }
+
+    public getDiffusionProfileParameters(color: Color3)
+    {
+        const cdf = 0.997;
+        // Importance sample the normalized diffuse reflectance profile for the computed value of 's'.
+        // ------------------------------------------------------------------------------------
+        // R[r, phi, s]   = s * (Exp[-r * s] + Exp[-r * s / 3]) / (8 * Pi * r)
+        // PDF[r, phi, s] = r * R[r, phi, s]
+        // CDF[r, s]      = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
+        // ------------------------------------------------------------------------------------
+        // We importance sample the color channel with the widest scattering distance.
+        const maxScatteringDistance = Math.max(color.r, color.g, color.b);
+
+        return this._sampleBurleyDiffusionProfile(cdf, maxScatteringDistance);
+    }
+    // https://zero-radiance.github.io/post/sampling-diffusion/
+    // Performs sampling of a Normalized Burley diffusion profile in polar coordinates.
+    // 'u' is the random number (the value of the CDF): [0, 1).
+    // rcp(s) = 1 / ShapeParam = ScatteringDistance.
+    // Returns the sampled radial distance, s.t. (u = 0 -> r = 0) and (u = 1 -> r = Inf).
+    private _sampleBurleyDiffusionProfile(u: number, rcpS: number)
+    {
+        u = 1 - u; // Convert CDF to CCDF
+
+        let g = 1 + (4 * u) * (2 * u + Math.sqrt(1 + (4 * u) * u));
+        let n = Math.pow(g, -1.0 / 3.0);                      // g^(-1/3)
+        let p = (g * n) * n;                                   // g^(+1/3)
+        let c = 1 + p + n;                                     // 1 + g^(+1/3) + g^(-1/3)
+        let x = 3 * Math.log(c / (4 * u));
+
+        return x * rcpS;
+    }
+
     public dispose() {
         this.sceneCompositorPostProcess.dispose();
         this.subSurfaceScatteringPostProcess.dispose();

+ 56 - 4
src/Rendering/prePassRendererSceneComponent.ts

@@ -1,10 +1,12 @@
 import { Nullable } from "../types";
 import { Scene } from "../scene";
-import { ISceneComponent, SceneComponentConstants } from "../sceneComponent";
+import { ISceneSerializableComponent, SceneComponentConstants } from "../sceneComponent";
 import { PrePassRenderer } from "./prePassRenderer";
+import { AbstractScene } from "../abstractScene";
+import { Color3 } from "../Maths/math.color";
 
-declare module "../scene" {
-    export interface Scene {
+declare module "../abstractScene" {
+    export interface AbstractScene {
         /** @hidden (Backing field) */
         _prePassRenderer: Nullable<PrePassRenderer>;
 
@@ -23,6 +25,8 @@ declare module "../scene" {
          * Disables the prepass associated with the scene
          */
         disablePrepassRenderer(): void;
+
+        ssDiffusionProfileColors: Color3[];
     }
 }
 
@@ -65,7 +69,7 @@ Scene.prototype.disablePrepassRenderer = function(): void {
  * Defines the Geometry Buffer scene component responsible to manage a G-Buffer useful
  * in several rendering techniques.
  */
-export class PrePassRendererSceneComponent implements ISceneComponent {
+export class PrePassRendererSceneComponent implements ISceneSerializableComponent {
     /**
      * The component name helpful to identify the component in the list of scene components.
      */
@@ -82,6 +86,8 @@ export class PrePassRendererSceneComponent implements ISceneComponent {
      */
     constructor(scene: Scene) {
         this.scene = scene;
+
+        scene.ssDiffusionProfileColors = [];
     }
 
     /**
@@ -112,6 +118,52 @@ export class PrePassRendererSceneComponent implements ISceneComponent {
     }
 
     /**
+     * Serializes the component data to the specified json object
+     * @param serializationObject The object to serialize to
+     */
+    public serialize(serializationObject: any): void {
+        const ssDiffusionProfileColors = this.scene.ssDiffusionProfileColors;
+        serializationObject.ssDiffusionProfileColors = [];
+
+        for (let i = 0; i < ssDiffusionProfileColors.length; i++) {
+            serializationObject.ssDiffusionProfileColors.push(ssDiffusionProfileColors[i].r,
+                                                              ssDiffusionProfileColors[i].g,
+                                                              ssDiffusionProfileColors[i].b);
+        }
+    }
+
+    /**
+     * Adds all the elements from the container to the scene
+     * @param container the container holding the elements
+     */
+    public addFromContainer(container: AbstractScene): void {
+        if (!container.ssDiffusionProfileColors) {
+            return;
+        }
+
+        if (this.scene.prePassRenderer) {
+            container.ssDiffusionProfileColors.forEach((color) => {
+                this.scene.prePassRenderer!.addDiffusionProfile(color);
+            });
+        }
+    }
+
+    /**
+     * Removes all the elements in the container from the scene
+     * @param container contains the elements to remove
+     * @param dispose if the removed element should be disposed (default: false)
+     */
+    public removeFromContainer(container: AbstractScene, dispose?: boolean): void {
+        if (!container.ssDiffusionProfileColors) {
+            return;
+        }
+
+        if (this.scene.prePassRenderer) {
+            this.scene.prePassRenderer.clearAllDiffusionProfiles();
+        }
+    }
+
+    /**
      * Rebuilds the elements related to this component in case of
      * context lost for instance.
      */

+ 1 - 1
src/Shaders/subSurfaceScattering.fragment.fx

@@ -144,7 +144,7 @@ void main(void)
 {
 	vec4 irradianceAndDiffusionProfile  = texture2D(irradianceSampler, vUV);
     vec3 centerIrradiance = irradianceAndDiffusionProfile.rgb;
-    int diffusionProfileIndex = int(irradianceAndDiffusionProfile.a * 255.);
+    int diffusionProfileIndex = int(round(irradianceAndDiffusionProfile.a * 255.));
 
 	float  centerDepth       = 0.;
     vec4 inputColor = texture2D(textureSampler, vUV);