Dynamic Cubemap Reflection

Hello,
for a while i am trying to accomplish a Script to render a Cubemap on Runtime and use it for Reflections on various Objects in the Scene.
All Scripts and Hints i can find are for older Versions of Cubemap Renderer or Reflections.

Below is the Code i currently try to use to render the Cubemap.
It throws the following error:

TypeError: Cannot read properties of null (reading 'original')
    at TextureHandler.load (texture.js:290:29)
    at ResourceLoader._loadNull (loader.js:191:17)
    at ResourceLoader.load (loader.js:112:18)
    at AssetRegistry.load (asset-registry.js:399:26)
    at CubemapHandler.loadAssets (cubemap.js:328:26)
    at CubemapHandler.load (cubemap.js:37:14)
    at ResourceLoader._loadNull (loader.js:191:17)
    at ResourceLoader.load (loader.js:112:18)
    at AssetRegistry.load (asset-registry.js:399:26)
    at AssetRegistry.add (asset-registry.js:257:18)

Does someone know how to fix this?
Or does someone has a more efficent way to do real time reflections?

var ReflectionCubemapScript = pc.createScript('reflectionCubemapScript');

ReflectionCubemapScript.attributes.add('cubemapResolution', {
    type: 'number',
    default: 512,
    description: 'The resolution of the reflection cubemap.'
});

ReflectionCubemapScript.prototype.initialize = function () {
    this.frameCount = 0;
    this.reflectionCamera = null;
    this.createReflectionCamera();
};

ReflectionCubemapScript.prototype.createReflectionCamera = function () {
    this.reflectionCamera = new pc.Entity();
    this.reflectionCamera.name = 'ReflectionCamera';
    this.reflectionCamera.addComponent('camera', {
        clearColor: new pc.Color(0, 0, 0), // Set the clear color to black (or any other color you prefer)
        priority: -1 // Set the priority to a lower value than the main camera to render first
    });

    this.reflectionCamera.camera.nearClip = 0.1;
    this.reflectionCamera.camera.farClip = 1000;

    this.entity.addChild(this.reflectionCamera);
};

ReflectionCubemapScript.prototype.update = function (dt) {
    this.frameCount++;

    if (this.frameCount % 20 === 0) {
        // Capture the cubemap every 20 frames
        this.captureCubemap();
    }
};

ReflectionCubemapScript.prototype.captureCubemap = function () {
    var cubemapAsset = new pc.Asset('ReflectionCubemap', 'cubemap', {
        url: null
    });

    cubemapAsset.on('load', function () {
        this.entity.render.material.cubeMap = cubemapAsset.resource;
    }, this);

    cubemapAsset.on('error', function (err) {
        console.error('Error loading cubemap asset: ', err);
    });

    this.app.assets.add(cubemapAsset);
    this.app.assets.load(cubemapAsset);

    this.reflectionCamera.camera.cubeMap = cubemapAsset.resource;

    var targets = [
        new pc.Vec3(1, 0, 0),
        new pc.Vec3(-1, 0, 0),
        new pc.Vec3(0, 1, 0),
        new pc.Vec3(0, -1, 0),
        new pc.Vec3(0, 0, 1),
        new pc.Vec3(0, 0, -1)
    ];

    var upVectors = [
        new pc.Vec3(0, -1, 0),
        new pc.Vec3(0, -1, 0),
        new pc.Vec3(0, 0, 1),
        new pc.Vec3(0, 0, -1),
        new pc.Vec3(0, -1, 0),
        new pc.Vec3(0, -1, 0)
    ];

    for (var i = 0; i < 6; i++) {
        var target = targets[i];
        var up = upVectors[i];

        this.reflectionCamera.setPosition(this.entity.getPosition());
        this.reflectionCamera.lookAt(this.entity.getPosition().add(target), up);

        this.reflectionCamera.camera.renderTarget = this.reflectionCamera.camera.cubeMap;
        this.app.render();
    }

    this.entity.render.material.cubemap = this.reflectionCamera.camera.cubeMap;
};

Here is up to date example:
https://playcanvas.github.io/#/graphics/reflection-cubemap

source code: https://github.com/playcanvas/engine/blob/main/examples/src/examples/graphics/reflection-cubemap.tsx

and it uses this script, which can be used in the Editor project as well: https://github.com/playcanvas/engine/blob/main/scripts/utils/cubemap-renderer.js

It’s possible there is some Editor example demonstrating its usage, but I’m not sure.

Actually, we have one more example, using a box projected cubemap too
https://playcanvas.github.io/#/graphics/reflection-box

I try using the script for the Editor Project, but it is not Capturing the Objects in the Scene. When i manually add a Environment Cubemap it reflects it. but the Captured one is not working.
Am i doing something wrong?

https://playcanvas.com/project/1115995/overview/reflect

It’s capturing - I can see it when I capture the frame using SpectorJS.
But you need to do other things the example does as well … specifically at least assign the captured cubemap to other materials to use.

You also need to set up layers (you might have already) to make sure the meshes that use the captured cubemap are not rendered to a cubemap.

Study the examples provided, including comments in them.

1 Like

Okay sorry for my missunderstanding, im a newbie to PlayCanvas.

It is reflecting now, but states the Error:

Trying to bind current color buffer as a texture

on every Frame. Is there a way to fix this issue? (i used the Editor Project as Reference)

yes, this part of what I mentioned:

You also need to set up layers (you might have already) to make sure the meshes that use the captured cubemap are not rendered to a cubemap.

Oh my god, thank you ^^
It finally worked. I forgot to add the layers to the render order, after adding them in the settings.

Thank you for your patients!!

2 Likes