Skip to content

Commit

Permalink
Added support of Screen Space Reflection Post-Process
Browse files Browse the repository at this point in the history
  • Loading branch information
julien-moreau committed Oct 21, 2019
1 parent 07519b6 commit 8f5b7e6
Show file tree
Hide file tree
Showing 8 changed files with 663 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Scene } from "../../../scene";
import { Constants } from "../../../Engines/constants";
import { _TypeStore } from '../../../Misc/typeStore';
import { MotionBlurPostProcess } from "../../motionBlurPostProcess";
import { ScreenSpaceReflectionPostProcess } from "../../screenSpaceReflectionPostProcess";

declare type Animation = import("../../../Animations/animation").Animation;

Expand Down Expand Up @@ -128,6 +129,10 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
* The Fast Approximate Anti-Aliasing post process which attemps to remove aliasing from an image.
*/
public fxaaPostProcess: Nullable<FxaaPostProcess> = null;
/**
* Post-process used to simulate realtime reflections using the screen space and geometry renderer.
*/
public screenSpaceReflectionPostProcess: Nullable<ScreenSpaceReflectionPostProcess> = null;

// Values

Expand Down Expand Up @@ -353,6 +358,7 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
private _hdrEnabled: boolean = false;
private _motionBlurEnabled: boolean = false;
private _fxaaEnabled: boolean = false;
private _screenSpaceReflectionsEnabled: boolean = false;

private _motionBlurSamples: number = 64.0;
private _volumetricLightStepsCount: number = 50.0;
Expand Down Expand Up @@ -491,6 +497,23 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
this._buildPipeline();
}

/**
* Specifies if screen space reflections are enabled.
*/
@serialize()
public get screenSpaceReflectionsEnabled(): boolean {
return this._screenSpaceReflectionsEnabled;
}

public set screenSpaceReflectionsEnabled(enabled: boolean) {
if (this._screenSpaceReflectionsEnabled === enabled) {
return;
}

this._screenSpaceReflectionsEnabled = enabled;
this._buildPipeline();
}

/**
* Specifies the number of steps used to calculate the volumetric lights
* Typically in interval [50, 200]
Expand Down Expand Up @@ -588,16 +611,23 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme

// Create pass post-process
if (!this._basePostProcess) {
this.originalPostProcess = new PostProcess("HDRPass", "standard", [], [], ratio, null, Constants.TEXTURE_BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", this._floatTextureType);
this.originalPostProcess.onApply = () => {
this._currentDepthOfFieldSource = this.originalPostProcess;
};
if (this._screenSpaceReflectionsEnabled) {
this.originalPostProcess = this.screenSpaceReflectionPostProcess = new ScreenSpaceReflectionPostProcess("HDRPass", scene, ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, this._floatTextureType);
this.screenSpaceReflectionPostProcess.onApplyObservable.add((effect: Effect) => {
this._currentDepthOfFieldSource = this.screenSpaceReflectionPostProcess;
});
} else {
this.originalPostProcess = new PostProcess("HDRPass", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", this._floatTextureType);
this.originalPostProcess.onApply = () => {
this._currentDepthOfFieldSource = this.originalPostProcess;
};
}
}
else {
this.originalPostProcess = this._basePostProcess;
}

if (this._bloomEnabled || this._vlsEnabled || this._lensFlareEnabled || this._depthOfFieldEnabled || this._motionBlurEnabled) {
if (this._bloomEnabled || this._vlsEnabled || this._lensFlareEnabled || this._depthOfFieldEnabled || this._motionBlurEnabled || this._screenSpaceReflectionsEnabled) {
this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRPassPostProcess", () => { return this.originalPostProcess; }, true));
}

Expand All @@ -617,8 +647,15 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
this._createTextureAdderPostProcess(scene, ratio);

// Create depth-of-field source post-process
this.textureAdderFinalPostProcess = new PostProcess("HDRDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Constants.TEXTURETYPE_UNSIGNED_INT);
this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBaseDepthOfFieldSource", () => { return this.textureAdderFinalPostProcess; }, true));
// this.textureAdderFinalPostProcess = new PostProcess("HDRDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Constants.TEXTURETYPE_UNSIGNED_INT);
// this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBaseDepthOfFieldSource", () => { return this.textureAdderFinalPostProcess; }, true));
this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBloom", () => [
this.downSampleX4PostProcess!,
this.brightPassPostProcess!,
this.blurHPostProcesses[0],
this.blurVPostProcesses[0],
this.textureAdderPostProcess!
], true));
}

if (this._vlsEnabled) {
Expand Down Expand Up @@ -683,7 +720,6 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
private _createDownSampleX4PostProcess(scene: Scene, ratio: number): void {
var downSampleX4Offsets = new Array<number>(32);
this.downSampleX4PostProcess = new PostProcess("HDRDownSampleX4", "standard", ["dsOffsets"], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define DOWN_SAMPLE_X4", Constants.TEXTURETYPE_UNSIGNED_INT);

this.downSampleX4PostProcess.onApply = (effect: Effect) => {
var id = 0;
let width = (<PostProcess>this.downSampleX4PostProcess).width;
Expand All @@ -701,14 +737,13 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
};

// Add to pipeline
this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRDownSampleX4", () => { return this.downSampleX4PostProcess; }, true));
// this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRDownSampleX4", () => { return this.downSampleX4PostProcess; }, true));
}

// Brightpass Post-Process
private _createBrightPassPostProcess(scene: Scene, ratio: number): void {
var brightOffsets = new Array<number>(8);
this.brightPassPostProcess = new PostProcess("HDRBrightPass", "standard", ["dsOffsets", "brightThreshold"], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define BRIGHT_PASS", Constants.TEXTURETYPE_UNSIGNED_INT);

this.brightPassPostProcess.onApply = (effect: Effect) => {
var sU = (1.0 / (<PostProcess>this.brightPassPostProcess).width);
var sV = (1.0 / (<PostProcess>this.brightPassPostProcess).height);
Expand All @@ -727,7 +762,7 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
};

// Add to pipeline
this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBrightPass", () => { return this.brightPassPostProcess; }, true));
// this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBrightPass", () => { return this.brightPassPostProcess; }, true));
}

// Create blur H&V post-processes
Expand All @@ -747,8 +782,8 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
blurY.kernel = this.horizontalBlur ? 64 * dw : (<any>this)[blurWidthKey] * dw;
});

this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBlurH" + indice, () => { return blurX; }, true));
this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBlurV" + indice, () => { return blurY; }, true));
// this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBlurH" + indice, () => { return blurX; }, true));
// this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBlurV" + indice, () => { return blurY; }, true));

this.blurHPostProcesses.push(blurX);
this.blurVPostProcesses.push(blurY);
Expand All @@ -767,7 +802,7 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
};

// Add to pipeline
this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", () => { return this.textureAdderPostProcess; }, true));
// this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", () => { return this.textureAdderPostProcess; }, true));
}

private _createVolumetricLightPostProcess(scene: Scene, ratio: number): void {
Expand Down Expand Up @@ -1156,6 +1191,7 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
this.depthOfFieldPostProcess = null;
this.motionBlurPostProcess = null;
this.fxaaPostProcess = null;
this.screenSpaceReflectionPostProcess = null;

this.luminanceDownSamplePostProcesses = [];
this.blurHPostProcesses = [];
Expand Down Expand Up @@ -1184,6 +1220,10 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
serializationObject.sourceLightId = this.sourceLight.id;
}

if (this.screenSpaceReflectionPostProcess) {
serializationObject.screenSpaceReflectionPostProcess = SerializationHelper.Serialize(this.screenSpaceReflectionPostProcess);
}

serializationObject.customType = "StandardRenderingPipeline";

return serializationObject;
Expand All @@ -1203,6 +1243,10 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
p.sourceLight = <SpotLight | DirectionalLight>scene.getLightByID(source.sourceLightId);
}

if (source.screenSpaceReflectionPostProcess) {
SerializationHelper.Parse(() => p.screenSpaceReflectionPostProcess, source.screenSpaceReflectionPostProcess, scene, rootUrl);
}

return p;
}

Expand Down
3 changes: 2 additions & 1 deletion src/PostProcesses/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ export * from "./stereoscopicInterlacePostProcess";
export * from "./tonemapPostProcess";
export * from "./volumetricLightScatteringPostProcess";
export * from "./vrDistortionCorrectionPostProcess";
export * from "./vrMultiviewToSingleviewPostProcess";
export * from "./vrMultiviewToSingleviewPostProcess";
export * from "./screenSpaceReflectionPostProcess";
195 changes: 195 additions & 0 deletions src/PostProcesses/screenSpaceReflectionPostProcess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { Nullable } from "../types";
import { Camera } from "../Cameras/camera";
import { Effect } from "../Materials/effect";
import { PostProcess, PostProcessOptions } from "./postProcess";
import { Constants } from "../Engines/constants";
import { Scene } from '../scene';
import { GeometryBufferRenderer } from '../Rendering/geometryBufferRenderer';
import { serialize } from '../Misc/decorators';

import "../Shaders/screenSpaceReflection.fragment";

declare type Engine = import("../Engines/engine").Engine;
/**
* The ScreenSpaceReflectionPostProcess performs realtime reflections using only and only the available informations on the screen (positions and normals).
* Basically, the screen space reflection post-process will compute reflections according the material's roughness.
*/
export class ScreenSpaceReflectionPostProcess extends PostProcess {
/**
* Gets or sets a reflection threshold mainly used to adjust the reflection's height.
*/
@serialize()
public threshold: number = 0;
/**
* Gets or sets the current reflection strength. 1.0 is an ideal value but can be increased/decreased for particular results.
*/
@serialize()
public strength: number = 1;
/**
* Gets or sets the falloff exponent used while computing fresnel. More the exponent is high, more the reflections will be discrete.
*/
@serialize()
public reflectionSpecularFalloffExponent: number = 1;
/**
* Gets or sets the step size used to iterate until the effect finds the color of the reflection's pixel. Typically in interval [0.1, 1.0]
*/
@serialize()
public step: number = 0.2;

/**
* @hidden
*/
public _geometryBufferRenderer: Nullable<GeometryBufferRenderer>;

private _enableSmoothReflections: boolean = true;
private _reflectionSamples: number = 64;
private _smoothSteps: number = 5;

/**
* Creates a new instance ConvolutionPostProcess
* @param name The name of the effect.
* @param scene The scene containing the objects to calculate reflections.
* @param options The required width/height ratio to downsize to before computing the render pass.
* @param camera The camera to apply the render pass to.
* @param samplingMode The sampling mode to be used when computing the pass. (default: 0)
* @param engine The engine which the post process will be applied. (default: current engine)
* @param reusable If the post process can be reused on the same frame. (default: false)
* @param textureType Type of textures used when performing the post process. (default: 0)
* @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
*/
constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
super(name, "screenSpaceReflection", [
"projection", "view", "threshold", "reflectionSpecularFalloffExponent", "strength", "step"
], [
"textureSampler", "normalSampler", "positionSampler", "roughnessSampler"
], options, camera, samplingMode, engine, reusable,
"#define SSR_SUPPORTED\n#define REFLECTION_SAMPLES 64\n#define SMOOTH_STEPS 5\n",
textureType, undefined, null, blockCompilation);

// Get geometry buffer renderer and update effect
const geometryBufferRenderer = scene.enableGeometryBufferRenderer();
if (geometryBufferRenderer) {
if (geometryBufferRenderer.isSupported) {
geometryBufferRenderer.enablePosition = true;
geometryBufferRenderer.enableRoughness = true;
this._geometryBufferRenderer = geometryBufferRenderer;
}
}

this._updateEffectDefines();

// On apply, send uniforms
this.onApply = (effect: Effect) => {
if (!geometryBufferRenderer) {
return;
}

// Samplers
const positionIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE);
const roughnessIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.ROUGHNESS_TEXTURE_TYPE);

effect.setTexture("normalSampler", geometryBufferRenderer.getGBuffer().textures[1]);
effect.setTexture("positionSampler", geometryBufferRenderer.getGBuffer().textures[positionIndex]);
effect.setTexture("roughnessSampler", geometryBufferRenderer.getGBuffer().textures[roughnessIndex]);

// Uniforms
const camera = scene.activeCamera;
if (!camera) {
return;
}

const viewMatrix = camera.getViewMatrix();
const projectionMatrix = camera.getProjectionMatrix();

effect.setMatrix("projection", projectionMatrix);
effect.setMatrix("view", viewMatrix);
effect.setFloat("threshold", this.threshold);
effect.setFloat("reflectionSpecularFalloffExponent", this.reflectionSpecularFalloffExponent);
effect.setFloat("strength", this.strength);
effect.setFloat("step", this.step);
};
}

/**
* Gets wether or not smoothing reflections is enabled.
* Enabling smoothing will require more GPU power and can generate a drop in FPS.
*/
@serialize()
public get enableSmoothReflections(): boolean {
return this._enableSmoothReflections;
}

/**
* Sets wether or not smoothing reflections is enabled.
* Enabling smoothing will require more GPU power and can generate a drop in FPS.
*/
public set enableSmoothReflections(enabled: boolean) {
if (enabled === this._enableSmoothReflections) {
return;
}

this._enableSmoothReflections = enabled;
this._updateEffectDefines();
}

/**
* Gets the number of samples taken while computing reflections. More samples count is high,
* more the post-process wil require GPU power and can generate a drop in FPS.
*/
@serialize()
public get reflectionSamples(): number {
return this._reflectionSamples;
}

/**
* Sets the number of samples taken while computing reflections. More samples count is high,
* more the post-process wil require GPU power and can generate a drop in FPS.
*/
public set reflectionSamples(samples: number) {
if (samples === this._reflectionSamples) {
return;
}

this._reflectionSamples = samples;
this._updateEffectDefines();
}

/**
* Gets the number of samples taken while smoothing reflections. More samples count is high,
* more the post-process will require GPU power and can generate a drop in FPS.
* Default value (5.0) work pretty well in all cases but can be adjust.
*/
@serialize()
public get smoothSteps(): number {
return this._smoothSteps;
}

/*
* Sets the number of samples taken while smoothing reflections. More samples count is high,
* more the post-process will require GPU power and can generate a drop in FPS.
* Default value (5.0) work pretty well in all cases but can be adjust.
*/
public set smoothSteps(steps: number) {
if (steps === this._smoothSteps) {
return;
}

this._smoothSteps = steps;
this._updateEffectDefines();
}

private _updateEffectDefines(): void {
const defines: string[] = [];
if (this._geometryBufferRenderer) {
defines.push("#define SSR_SUPPORTED");
}
if (this._enableSmoothReflections) {
defines.push("#define ENABLE_SMOOTH_REFLECTIONS");
}

defines.push("#define REFLECTION_SAMPLES " + (this._reflectionSamples >> 0));
defines.push("#define SMOOTH_STEPS " + (this._smoothSteps >> 0));

this.updateEffect(defines.join("\n"));
}
}
Loading

0 comments on commit 8f5b7e6

Please sign in to comment.