Dynamic Cubemaps / Reflection Probes / Render To Texture in 2020

Hello everyone,
I am currently trying to get dynamic cubemaps to work and have already scoured most of this forum, to no avail. What I need is the following:

  • create an entity in the middle of a room
  • a script renders the cubemap ONLY on application start
  • the cubemap gets assigned to the skybox

Having more than one CubemapRenderer in the scene would be preferable for my use-case.
Sadly, simply rendering the cubemap offline (in a 3d program) is NOT an option for my project.
Severe road blocks I have encountered so far:

  • because of the newer layer system, most old-school camera target approaches do not work
  • using the layer system means creating a dedicated render layer and assigning every single object to it for rendering (even if that were an option, much of the lighting in my scene is lost, because the ‘Skybox’ layer cannot be copied or reassigned)

Still, I have been able to cobble together some code that, IN THEORY, should work (if I interpreted it corretly) - but, as I am not a programmer, most of this stuff is beyond me. The code certainly does something, seeing as

  1. the skybox actually gets replaced (with a plain white one, but still…)
  2. if slightly modified, it works with just one camera and one texture which is assigned to a material
var RenderCubemap = pc.createScript('renderCubemap');

RenderCubemap.prototype.initialize = function() {
    // define camera angles (posx, negx, posy, negy, posz, negz)
    var angles = [
                [ 0, 90, 180 ],
                [ 0, -90, 180 ],
                [ 90, 0, 0 ],
                [ -90, 0, 0 ],
                [ 0, 180, 180 ],
                [ 0, 0, 180 ]
                ];    
    
    //var face = [pc.CUBEFACE_POSX, pc.CUBEFACE_NEGX, pc.CUBEFACE_POSY, pc.CUBEFACE_NEGY, pc.CUBEFACE_POSZ, pc.CUBEFACE_NEGZ];
     
    // create new Cubemap texture
    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 each of the 6 sides of the Cubemap...
    for (var i = 0; i < 6; i++) {
       
        var renderTarget = new pc.RenderTarget(this.app.graphicsDevice, this.colorBuffer, {
            depth: true,
            face: i
        });
        
        // create and position a camera        
        var e = new pc.Entity();
        e.addComponent('camera', {
            clearColor: new pc.Color(1, 1, 1),
            aspectRatio: 1,
            fov: 90
        });
                
        this.entity.addChild(e);
        e.setLocalEulerAngles(angles[i][0], angles[i][1], angles[i][2]);        
        e.setPosition(this.entity.getPosition());        

        this.setRt(undefined, renderTarget);    // set the renderTarget of the currently active camera to renderTarget
        this.app.renderer.renderComposition(this.app.scene.layers);     // render all layers      
        this.setRt(renderTarget, undefined);    // reset the renderTarget of the current camera
        
        e.destroy();    // destroy camera    
    }    
    
    // compute Cubemap (see: github.com/playcanvas/engine/pull/202)
    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);
};


RenderCubemap.prototype.setRt = function(prevRt, newRt) {
    var comp = this.app.scene.layers;
    var layers = comp.layerList;
    for(var i=0; i<layers.length; i++) {
        if (layers[i].renderTarget === prevRt && layers[i].id !== pc.LAYERID_UI) {
            layers[i].renderTarget = newRt;
        }
    }
};

Please - if anybody has an idea on how to approach this, I’d be sooo thankful! I’ve been at this for hours on end now and simply cannot work it out

I based my code on several projects and posts, including (but not limited to):
https://forum.playcanvas.com/t/how-to-make-a-dynamic-cubemap-reflexion-project/2437/7
https://playcanvas.com/project/532659/overview/render-low-res-world-layer
https://playcanvas.com/project/528262/overview/render-to-texture-test
https://playcanvas.com/project/605131/overview/capturing-screenshot-from-camera
https://playcanvas.com/project/578359/overview/tutorial-render-to-texture-once
https://playcanvas.com/project/560797/overview/tutorial-render-to-texture

2 Likes

There’s some work in the PR to create dynamic cubemaps: https://github.com/playcanvas/engine/pull/2523

1 Like

I worked on the PR @yaustar listed to get this working as an engine example, and even though that works, I don’t like the need to create 6 layers, 6 cameras, and especially having to add all objects to those layers. This is even worse for multiple cameras.

And so I’m exploring the option to modify engine’s layer system to allow render target to be specified on the camera instead of the layer. The new cubemap / texture rendering based on this, that I have working locally, is a lot simpler now, but this is not part of PR as that needs few more things to be finished, and it would need a lot of testing as it might break compatibility for some projects. But the hope is that in the not too far future this will get easy to do.

2 Likes