How to make a dynamic cubemap ? (reflexion project)

Hello, my project is a sphere that will reflect the surroundings every frame, this page show the pc.RenderTarget function and i can use app.assets.get to get the cubemap from the assets, but i want to see a example of that, i have to rotate the camera for each face of the cubemap and i’m having some difficulties geting all together.

Anyone can put some light on this ?

In words:

  1. Put camera at the point of cubemap.
  2. Make FoV 45 degrees and square viewport (RenderTarget).
  3. Rotate camera in direction of each positive and negative axes.
  4. Render camera in separate render targets for each face.
  5. Create/update cubemap texture providing 6 faces you’ve rendered.

If you need prefiltering (Image Based Lighting), that allows to have decent PBR, then you need to call pc.prefilterCubemap although it is not documented, you can still find sources and options from it: https://github.com/playcanvas/engine/blob/c32f63021c5b376671607c80a003435062e313c1/src/graphics/prefilter-cubemap.js#L23

Probably worth to create an example for it, and we already have a ticket to create utility functions to help rendering cubemaps.

Thanks, great dev support, i will post the results later for anyone in the same situation, thanks a lot.

just one more thing, that is the code for update a cubemap face ?

var renderTarget = new pc.RenderTarget(graphicsDevice, colorBuffer, {
    face: pc.CUBEFACE_POSX
});

or it would be, face: 1 ?

It can be either, but remember faces start from 0, not 1 :slight_smile:
Here is list of constants: https://github.com/playcanvas/engine/blob/e5fd594a2907cc4eaa56401632490dba293dc6c6/src/graphics/graphics.js#L159

As engine being open source, it is easy to inspect code, and find things you need there if you want to go deep :wink:

Got it, just don’t tell my teacher that i used 1 instead of 0 to start counting something :grin:.

1 Like

sorry for bother you again, but i’m geting a strange error in the script that i made to teste the pc.RenderTarget, it says: ERROR: FRAMEBUFFER_INCOMPLETE_ATTACHMENT

i did not find any solution or at least a cause for this here is the script code atached to a camera:

var texture = this.app.assets.get(5036732); //Getting the CubeMap

var renderTarget = new pc.RenderTarget(this.app.graphicsDevice, texture,{
    face: 0
});

this.entity.camera.renderTarget = renderTarget;

var texture = this.app.assets.get(5036732); //Getting the CubeMap
Gives you an asset, not a pc.Texture object. To get texture of it:

var asset = this.app.assets.get(5036732); //Getting the CubeMap
var texture = assets.resource;

var renderTarget = new pc.RenderTarget(this.app.graphicsDevice, texture, {
    face: 0
});

this.entity.camera.renderTarget = renderTarget;

But in your case, cubemap will contain multiple resources, each referring to separate face of a cubemap, which can be accessed from resources property.

Hi, could You make some example of dynamic cubemap that can be used as a skybox?
Im trying to do with Your hints but I still get an errors like: “Can’t use prefiltered cubemap: undefined, false, …” It seams like var allMips is not defined couse of the condition:
var allMips = prefilteredCubeMap128 && prefilteredCubeMap64 && prefilteredCubeMap32 && prefilteredCubeMap16 && prefilteredCubeMap8 && prefilteredCubeMap4;

var angles = [
                [ 0, 90, 180 ],
                [ 0, -90, 180 ],
                [ 90, 0, 0 ],
                [ -90, 0, 0 ],
                [ 0, 180, 180 ],
                [ 0, 0, 180 ]
            ];
    
    
    this.colorBuffer = new pc.Texture(this.app.graphicsDevice, {
        cubemap: true,
        rgbm: true,
        fixCubemapSeams: true,
        format: pc.PIXELFORMAT_R8_G8_B8_A8,
        width: 128,
        height: 128
    });
    this.colorBuffer.minFilter = pc.FILTER_LINEAR_MIPMAP_LINEAR;
    this.colorBuffer.magFilter = pc.FILTER_LINEAR;
    
    for (var i = 0; i < 6; i++) {
        var renderTarget = new pc.RenderTarget(this.app.graphicsDevice, this.colorBuffer, {
            depth: true,
            face: i
        });
        var e = new pc.Entity();
        e.addComponent('camera', {
            clearColor: new pc.Color(1, 1, 1),
            aspectRatio: 1,
            fov: 90
        });
        e.camera.renderTarget = renderTarget;
        this.entity.addChild(e);
        e.setLocalEulerAngles(angles[i][0], angles[i][1], angles[i][2]);
    }
    var options = {
        device: this.app.graphicsDevice,
        sourceCubemap: this.colorBuffer,
        method: 1,
        samples: 4096,
        cpuSync: true,
        filteredFixed: [],
        singleFilteredFixed: true,
    };
    pc.prefilterCubemap(options);
    this.app.scene.setSkybox(options.filteredFixed);

I really need this working for my project. Please help.

Ok, I know this is an old subject … how high a resolution can one expect to go by with this method on standard GPUs? (seems/sounds too heavy to take realtime snapshots above 256 or 512 px … in case a useful framerate is to be fulfilled)

It is expensive, indeed. Basically, to bruteforce render CubeMap, almost same as rendering your scene 7 times the framerate, so it is CPU<>GPU intensive.

You could simplify render into CubeMap, make it lower quality, and reduce scene complexity when rendering into CubeMap, that would save a bit. Most important, is to keep drawcalls as low as possible in such case. Because each draw call, is x7 when rendering CubeMap.

Plus, try to render it not as often. You could even render not all faces at the same time, depending on your scene.

ok, so if I ‘take a cubemap snapshot’ every 20th or 30th frame your method should work? (I am not looking for animation on the cubemap texture anyway)

PS like this or close to :

var asset = this.app.assets.get(26314379); //Getting the CubeMap
var texture = this.app.assets.resource;

var renderTarget = new pc.RenderTarget(this.app.graphicsDevice, texture, {
face: 0
});
var cam = this.app.root.findByName(“Camera2”);
renderTarget.face[0] =cam.renderTarget;

asset.loadFaces = true;
this.app.assets.load(asset); // taken from ☑ How to change the scene skybox
this.app.setSkybox(renderTarget.face[0]);

(still getting/having a little trouble with above)