Skip to content

屏幕空间反射 - SSR

一种基于屏幕空间的反射效果实现,模拟光滑物体表面可以实时反射其周边物体影像的视觉效果。该反射效果优点为实时渲染,某一个物体发生移动,反射画面中物体也会发生移动;能精确的从每个像素反射。缺点为不能够反射出物体的背面,且屏幕范围外的物体也不能够被反射到其他的物体上。

ts
//初始化引擎
await Engine3D.init();

Engine3D.setting.render.postProcessing.ssr.fadeEdgeRatio = 0.2;
Engine3D.setting.render.postProcessing.ssr.rayMarchRatio = 0.5;
Engine3D.setting.render.postProcessing.ssr.fadeDistanceMin = 600;
Engine3D.setting.render.postProcessing.ssr.fadeDistanceMax = 2000;
Engine3D.setting.render.postProcessing.ssr.roughnessThreshold = 0.5;
Engine3D.setting.render.postProcessing.ssr.powDotRN = 0.2;

// 添加 SSRPost
let postProcessing = this.scene.addComponent(PostProcessingComponent);
postProcessing.addPost(SSRPost);

//开始渲染
let view = new View3D();
view.scene = this.scene;
view.camera = this.camera;
Engine3D.startRenderView(view);

Engine3D.setting.render.postProcessing.ssr 配置参数。

参数类型描述
fadeEdgeRationumber渐变速率。
rayMarchRationumber光线步进速率。
fadeDistanceMinnumber消退距离最小值。
fadeDistanceMaxnumber消退距离最大值。
roughnessThresholdnumber粗糙度阈值。
powDotRNnumbernormal和reflection点积的pow参数。

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { DirectLight, Engine3D, View3D, LitMaterial, HoverCameraController, KelvinUtil, MeshRenderer, Object3D, PlaneGeometry, Scene3D, SphereGeometry, SSRPost, Time, CameraUtil, webGPUContext, PostProcessingComponent, BloomPost, AtmosphericComponent } from '@orillusion/core';
import * as dat from 'dat.gui';

class Sample_SSR {
    lightObj: Object3D;
    scene: Scene3D;
    mats: any[];

    constructor() {}

    async run() {
        Engine3D.setting.shadow.enable = true;
        await Engine3D.init({
            canvasConfig: {
                devicePixelRatio: 1
            },
            renderLoop: () => this.loop()
        });

        this.scene = new Scene3D();
        this.scene.addComponent(AtmosphericComponent).sunY = 0.6;

        let mainCamera = CameraUtil.createCamera3DObject(this.scene, 'camera');
        mainCamera.perspective(60, webGPUContext.aspect, 1, 2000.0);
        let ctrl = mainCamera.object3D.addComponent(HoverCameraController);
        ctrl.setCamera(180, -5, 60);
        await this.initScene(this.scene);

        let view = new View3D();
        view.scene = this.scene;
        view.camera = mainCamera;
        Engine3D.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 = 27;
            scene.addChild(this.lightObj);
        }

        // load test model
        let minimalObj = await Engine3D.res.loadGltf('https://cdn.orillusion.com/PBR/ToyCar/ToyCar.gltf');
        minimalObj.scaleX = minimalObj.scaleY = minimalObj.scaleZ = 1000;
        scene.addChild(minimalObj);

        await this.createPlane(scene);
        return true;
    }

    private sphere: Object3D;

    private async createPlane(scene: Scene3D) {
        let mat = new LitMaterial();
        mat.roughness = 0.2;
        mat.metallic = 0.5;

        {
            let floorMaterial = new LitMaterial();
            floorMaterial.roughness = 0.5;
            floorMaterial.metallic = 0.5;

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

            const GUIHelp = new dat.GUI();
            GUIHelp.add(floorMaterial, 'roughness', 0, 1, 0.01);
            GUIHelp.add(floorMaterial, 'metallic', 0, 1, 0.01);
        }

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