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 Aug 30, 2019
1 parent dffc86f commit 4e7ec14
Show file tree
Hide file tree
Showing 8 changed files with 561 additions and 81 deletions.

Large diffs are not rendered by default.

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";
160 changes: 160 additions & 0 deletions src/PostProcesses/screenSpaceReflectionPostProcess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
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 SharpenPostProcess applies a sharpen kernel to every pixel
* See http://en.wikipedia.org/wiki/Kernel_(image_processing)
*/
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 = 3;

private _geometryBufferRenderer: Nullable<GeometryBufferRenderer>;
private _enableSmoothReflections: boolean = true;
private _reflectionSamples: number = 64;

/**
* 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"
], [
"textureSampler", "normalSampler", "positionSampler", "roughnessSampler"
], options, camera, samplingMode, engine, reusable,
"#define SSR_SUPPORTED\n#define REFLECTION_SAMPLES 64\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);
};
}

/**
* 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();
}

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));

this.updateEffect(defines.join("\n"));
}
}
112 changes: 92 additions & 20 deletions src/Rendering/geometryBufferRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import { Material } from "../Materials/material";
import { MaterialHelper } from "../Materials/materialHelper";
import { Scene } from "../scene";
import { AbstractMesh } from "../Meshes/abstractMesh";
import { Color4 } from '../Maths/math.color';
import { StandardMaterial } from '../Materials/standardMaterial';
import { PBRMaterial } from '../Materials/PBR/pbrMaterial';

import "../Shaders/geometry.fragment";
import "../Shaders/geometry.vertex";
import { _DevTools } from '../Misc/devTools';
import { Color4 } from '../Maths/math.color';

/** @hidden */
interface ISavedTransformationMatrix {
Expand All @@ -37,6 +39,11 @@ export class GeometryBufferRenderer {
* using getIndex(GeometryBufferRenderer.VELOCITY_TEXTURE_INDEX)
*/
public static readonly VELOCITY_TEXTURE_TYPE = 2;
/**
* Constant used to retrieve the roughness texture index in the G-Buffer textures array
* using the getIndex(GeometryBufferRenderer.ROUGHNESS_TEXTURE_TYPE)
*/
public static readonly ROUGHNESS_TEXTURE_TYPE = 3;

/**
* Dictionary used to store the previous transformation matrices of each rendered mesh
Expand All @@ -61,9 +68,11 @@ export class GeometryBufferRenderer {
private _ratio: number;
private _enablePosition: boolean = false;
private _enableVelocity: boolean = false;
private _enableRoughness: boolean = false;

private _positionIndex: number = -1;
private _velocityIndex: number = -1;
private _roughnessIndex: number = -1;

protected _effect: Effect;
protected _cachedDefines: string;
Expand Down Expand Up @@ -92,6 +101,7 @@ export class GeometryBufferRenderer {
switch (textureType) {
case GeometryBufferRenderer.POSITION_TEXTURE_TYPE: return this._positionIndex;
case GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE: return this._velocityIndex;
case GeometryBufferRenderer.ROUGHNESS_TEXTURE_TYPE: return this._roughnessIndex;
default: return -1;
}
}
Expand Down Expand Up @@ -133,6 +143,22 @@ export class GeometryBufferRenderer {
this._createRenderTargets();
}

/**
* Gets a boolean indicating if objects roughness are enabled in the G buffer.
*/
public get enableRoughness(): boolean {
return this._enableRoughness;
}

/**
* Sets wether or not objects roughness are enabled for the G buffer.
*/
public set enableRoughness(enable: boolean) {
this._enableRoughness = enable;
this.dispose();
this._createRenderTargets();
}

/**
* Gets the scene associated with the buffer.
*/
Expand Down Expand Up @@ -175,28 +201,54 @@ export class GeometryBufferRenderer {
* @returns true if ready otherwise false
*/
public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
var material: any = subMesh.getMaterial();
var material = subMesh.getMaterial();

if (material && material.disableDepthWrite) {
return false;
}

var defines = [];

var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];

var mesh = subMesh.getMesh();

// Alpha test
if (material && material.needAlphaTesting()) {
defines.push("#define ALPHATEST");
if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
attribs.push(VertexBuffer.UVKind);
defines.push("#define UV1");
if (material) {
let needUv = false;
if (material.needAlphaBlending()) {
defines.push("#define ALPHATEST");
needUv = true;
}
if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
attribs.push(VertexBuffer.UV2Kind);
defines.push("#define UV2");

if (this._enableRoughness) {
if (material instanceof StandardMaterial && material.specularTexture) {
defines.push("#define HAS_SPECULAR");
needUv = true;
} else if (material instanceof PBRMaterial && material.metallicTexture) {
defines.push("#define HAS_METALLIC");
if (material.useRoughnessFromMetallicTextureAlpha) {
defines.push("#define ROUGHNESSSTOREINMETALMAPALPHA");
} else if (material.useRoughnessFromMetallicTextureGreen) {
defines.push("#define ROUGHNESSSTOREINMETALMAPGREEN");
}

if (material.useMetallnessFromMetallicTextureBlue) {
defines.push("#define METALLNESSSTOREINMETALMAPBLUE");
}

needUv = true;
}
}

if (needUv) {
defines.push("#define NEED_UV");
if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
attribs.push(VertexBuffer.UVKind);
defines.push("#define UV1");
}
if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
attribs.push(VertexBuffer.UV2Kind);
defines.push("#define UV2");
}
}
}

Expand All @@ -214,6 +266,11 @@ export class GeometryBufferRenderer {
}
}

if (this._enableRoughness) {
defines.push("#define ROUGHNESS");
defines.push("#define ROUGHNESS_INDEX " + this._roughnessIndex);
}

// Bones
if (mesh.useBones && mesh.computeBonesUsingShaders) {
attribs.push(VertexBuffer.MatricesIndicesKind);
Expand Down Expand Up @@ -258,9 +315,9 @@ export class GeometryBufferRenderer {
this._effect = this._scene.getEngine().createEffect("geometry",
attribs,
["world", "mBones", "viewProjection", "diffuseMatrix", "view", "previousWorld", "previousViewProjection", "mPreviousBones", "morphTargetInfluences"],
["diffuseSampler"], join,
["diffuseSampler", "roughnessSampler"], join,
undefined, undefined, undefined,
{ buffersCount: this._enablePosition ? 3 : 2, maxSimultaneousMorphTargets: numMorphInfluencers });
{ buffersCount: this._multiRenderTarget.textures.length - 1, maxSimultaneousMorphTargets: numMorphInfluencers });
}

return this._effect.isReady();
Expand Down Expand Up @@ -309,6 +366,11 @@ export class GeometryBufferRenderer {
count++;
}

if (this._enableRoughness) {
this._roughnessIndex = count;
count++;
}

this._multiRenderTarget = new MultiRenderTarget("gBuffer",
{ width: engine.getRenderWidth() * this._ratio, height: engine.getRenderHeight() * this._ratio }, count, this._scene,
{ generateMipMaps: false, generateDepthTexture: true, defaultType: Constants.TEXTURETYPE_FLOAT });
Expand Down Expand Up @@ -371,13 +433,23 @@ export class GeometryBufferRenderer {
this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
this._effect.setMatrix("view", scene.getViewMatrix());

// Alpha test
if (material && material.needAlphaTesting()) {
var alphaTexture = material.getAlphaTestTexture();
if (material) {
// Alpha test
if (material.needAlphaTesting()) {
var alphaTexture = material.getAlphaTestTexture();
if (alphaTexture) {
this._effect.setTexture("diffuseSampler", alphaTexture);
this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
}
}

if (alphaTexture) {
this._effect.setTexture("diffuseSampler", alphaTexture);
this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
// Roughness
if (this._enableRoughness) {
if (material instanceof StandardMaterial && material.specularTexture) {
this._effect.setTexture("roughnessSampler", material.specularTexture);
} else if (material instanceof PBRMaterial && material.metallicTexture) {
this._effect.setTexture("roughnessSampler", material.metallicTexture);
}
}
}

Expand Down
Loading

0 comments on commit 4e7ec14

Please sign in to comment.