Screen Space Reflection - SSR
A reflection effect implementation based on screen space, which simulates the visual effect where a smooth object surface can reflect the images of surrounding objects in real time. The advantage of this reflection effect is real-time rendering: when an object moves, the object in the reflected image also moves; it can reflect accurately from each pixel. The disadvantage is that it cannot reflect the back of an object, and objects outside the screen range cannot be reflected onto other objects either.
ts
//Initialize the engine
let engine = await Engine3D.init();
engine.setting.render.postProcessing.ssr.fadeEdgeRatio = 0.2;
engine.setting.render.postProcessing.ssr.rayMarchRatio = 0.5;
engine.setting.render.postProcessing.ssr.fadeDistanceMin = 600;
engine.setting.render.postProcessing.ssr.fadeDistanceMax = 2000;
engine.setting.render.postProcessing.ssr.roughnessThreshold = 0.5;
engine.setting.render.postProcessing.ssr.powDotRN = 0.2;
// Add SSRPost
let postProcessing = this.scene.addComponent(PostProcessingComponent);
postProcessing.addPost(SSRPost);
//Start rendering
let view = new View3D();
view.scene = this.scene;
view.camera = this.camera;
engine.startRenderView(view);engine.setting.render.postProcessing.ssr configuration parameters.
| Parameter | Type | Description |
|---|---|---|
| fadeEdgeRatio | number | Gradient rate |
| rayMarchRatio | number | Ray marching rate |
| fadeDistanceMin | number | Minimum fade distance |
| fadeDistanceMax | number | Maximum fade distance |
| roughnessThreshold | number | Roughness threshold |
| powDotRN | number | The pow parameter of the dot product of normal and reflection |
ts
import { DirectLight, Engine3D, View3D, LitMaterial, HoverCameraController, KelvinUtil, MeshRenderer, Object3D, PlaneGeometry, Scene3D, SphereGeometry, SSRPost, Time, CameraUtil, PostProcessingComponent, BloomPost, AtmosphericComponent } from '@orillusion/core';
import * as dat from 'dat.gui';
class Sample_SSR {
lightObj: Object3D;
scene: Scene3D;
mats: any[];
engine: Engine3D;
constructor() {}
async run() {
this.engine = await Engine3D.init({
canvasConfig: {
devicePixelRatio: 1
},
renderLoop: () => this.loop(),
setting: {
shadow: {
enable: true,
shadowSize: 2048,
shadowBound: 200,
shadowBias: 0.05
}
}
});
this.scene = new Scene3D();
this.scene.addComponent(AtmosphericComponent).sunY = 0.6;
let mainCamera = CameraUtil.createCamera3DObject(this.scene, 'camera');
mainCamera.perspective(60, this.engine.aspect, 1, 2000.0);
let ctrl = mainCamera.object3D.addComponent(HoverCameraController);
ctrl.setCamera(-75, -20, 40);
await this.initScene(this.scene);
let view = new View3D();
view.scene = this.scene;
view.camera = mainCamera;
this.engine.startRenderView(view);
let postProcessing = this.scene.addComponent(PostProcessingComponent);
postProcessing.addPost(SSRPost);
postProcessing.addPost(BloomPost);
}
async initScene(scene: Scene3D) {
/******** light *******/
{
this.lightObj = new Object3D();
this.lightObj.rotationX = 15;
this.lightObj.rotationY = 110;
this.lightObj.rotationZ = 0;
let lc = this.lightObj.addComponent(DirectLight);
lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355);
lc.castShadow = true;
lc.intensity = 10;
scene.addChild(this.lightObj);
}
// load test model
let minimalObj = await this.engine.res.loadGltf('https://cdn.orillusion.com/PBR/ToyCar/ToyCar.gltf');
minimalObj.scaleX = minimalObj.scaleY = minimalObj.scaleZ = 1000;
minimalObj.y = -1.1
scene.addChild(minimalObj);
minimalObj.forChild((obj: Object3D) => {
let mr = obj.getComponent(MeshRenderer)
if (mr && mr.material) {
if (mr.material.name == 'ToyCar') {
let mat = mr.material as LitMaterial;
mat.metallic = 0.9;
mat.roughness = 0.1;
mat.clearcoatFactor = 0.5;
}
}
})
await this.createPlane(scene);
return true;
}
private sphere: Object3D;
private async createPlane(scene: Scene3D) {
const GUIHelp = new dat.GUI();
{
let floorMaterial = new LitMaterial();
floorMaterial.roughness = 0.1;
floorMaterial.metallic = 1;
let planeGeometry = new PlaneGeometry(200, 200);
let floor: Object3D = new Object3D();
let mr = floor.addComponent(MeshRenderer);
mr.material = floorMaterial;
mr.geometry = planeGeometry;
scene.addChild(floor);
let f = GUIHelp.addFolder('floor')
f.add(floorMaterial, 'roughness', 0.01, 1, 0.01);
f.add(floorMaterial, 'metallic', 0.01, 1, 0.01);
f.open()
}
{
let mat = new LitMaterial();
mat.roughness = 0.1;
mat.metallic = 0.9;
let sphereGeometry = new SphereGeometry(10, 50, 50);
let obj: Object3D = new Object3D();
let mr = obj.addComponent(MeshRenderer);
mr.material = mat;
mr.geometry = sphereGeometry;
obj.x = 30;
obj.y = 10;
scene.addChild(obj);
this.sphere = obj;
let f = GUIHelp.addFolder('Sphere')
f.add(mat, 'roughness', 0.01, 1, 0.01);
f.add(mat, 'metallic', 0.01, 1, 0.01);
f.open()
}
{
let sphereGeometry = new SphereGeometry(2, 50, 50);
for (let i = 0; i < 10; i += 2) {
for (let j = 0; j < 10; j += 2) {
let rmMaterial = new LitMaterial();
rmMaterial.roughness = j / 10;
rmMaterial.metallic = i / 10;
let obj: Object3D = new Object3D();
let mr = obj.addComponent(MeshRenderer);
mr.material = rmMaterial;
mr.geometry = sphereGeometry;
obj.y = j * 5 + 10;
obj.x = 50;
obj.z = i * 5 - 25;
scene.addChild(obj);
}
}
}
}
private loop(): void {
if (this.sphere) {
this.sphere.x = Math.sin(Time.time * 0.0001) * 30;
this.sphere.z = Math.cos(Time.time * 0.0001) * 30;
}
}
}
new Sample_SSR().run();
