[SOLVED] Dynamic environment mapping for ambient light

Hi, we have a day-cycle and we need the ambient light to match the scene (as well as some reflections). Previously we rendered a cubemap, prefiltered it and called app.scene.setSkybox with it. For reflections we used the cubemap directly. With v1.51.4 only the reflections appear to work, so what is the recommended way to update the ambient part? We used a script called IBLProbe, which did the prefiltering, I think you provided it.

@Elliott may be able to offer more insight on your solution.

But for the scene ambient light specifically you can set it like this:

this.app.scene.ambientLight.set(0.5, 0.0, 1.0);

https://developer.playcanvas.com/api/pc.Scene.html#ambientLight

We want the ambient light to come from the downscaled cubemap, not a single value. I’m sure it’s supported also with the recent changes, I just can’t work it out. I think @slimbuck coded the new system.

1 Like

You should probably look at this example:
http://playcanvas.github.io/#/graphics/box-reflection

It renders the cubemap, and prefilters it at runtime. The prefiltered data is stored in 2D texture, which contains different roughness levels for specular, but also small prefiltered version used for diffuse (ambient) lighting.

1 Like

Thanks. I can see that a cubemap is generated (guess it happens inside cubemapRenderer script) and from that an “envAtlas” is generated. This is used for reflection on several objects. I don’t see how it’s used for ambient, though.

When an envAtlas is assigned (to the scene to be global, or to a material), its used for both reflections also ambient color.

I captured the box-reflection example, and looked at the fragment shader of the floor for example. It contains texture_envAtlas uniform with this mentioned prefiltered 2d texture.

it then uses it for reflections:

vec3 calcReflection(vec3 tReflDirW, float tGlossiness) {
...
 vec3 linearA = decodeRGBM(texture2D(texture_envAtlas, uv0));
...
}

but also ambient

void addAmbient() {
    vec3 dir = cubeMapRotate(dNormalW) * vec3(-1.0, 1.0, 1.0);
    vec2 uv = mapUv(toSphericalUv(dir), vec4(128.0, 256.0 + 128.0, 64.0, 32.0) / atlasSize);
    vec4 raw = texture2D(texture_envAtlas, uv);
    vec3 linear = decodeRGBM(raw);
    dDiffuseLight += processEnvironment(linear);
}
2 Likes

That’s really cool. Sometimes I change the shaders, but right now, for standard material, how do I apply the envAtlas for ambience? Or maybe setting it to the scene is better, so all ambience uses the generated envAtlas, how is that done? It used to be setSkybox, but that takes a prefiltered cubemap.

Ah, scene.envAtlas = envAtlas
or material.envAtlas = envAtlas
?

It works now! You need to document those two properties :slight_smile: . I have a small test scene if anyone else needs dynamically updated ambience: https://playcanvas.com/editor/scene/1149115 (it only updates it once, but easy to change that)

1 Like

yep, that’s the one.

You set shaders dirty, hopefully that’s just for the shader properties, I don’t want it to change shader code and recompile.

in the latest (local) engine, the Scene.envAtlas is public … and Material should be exposed for next release as well … few APIs are still under review and more things should go public when these are finalized.

You set shaders dirty, hopefully that’s just for the shader properties, I don’t want it to change shader code and recompile.

Internally it makes sure the shader is the same as before … and would not compile if not. This handles the case here where env lighting is stored in different form … for example old cubemaps solution switched to new 2d atlas solution.

2 Likes