Lightmaps not bright enough

Hello, I am trying to bring a scene from UE4 intro Babylon, and the only problem that I have right now is that my lightmaps are not bright enough. I have tried all the settings recommended in the documentation but so far this is the best result that I have got.

You can notice that the sunlight in the windows is almost white in the UE4 image but in babylon its barely there, I also painted a white stripe to see the max intensity of the lightmap in babylon and its very dim.

I am using materials with diffuse textures and lightmap textures with “Use lightmap as shadowmap” set to true (looks the same with false).

Adding a white ambient light removes the lightmap.

Any suggestion is welcome! Thank you

can you make simple PG

Pinging @sebavan fyi

1 Like

Is there a special setup for unreal as it would look like they do not have the same intensity at all. Also are your lightmaps in the same format in both ? like int vs float ?

Converting exr files to plain standard png files will loose some light dynamic because chances are that your exr file have values > 1. You should convert to RGBD png instead, and flag the texture as RGBD so that it is decoded at runtime (in your code: currentMeshLightmap.isRGBD = true;)

Also, in your PG you are using standard materials: if UE4 uses PBR materials you should use the PBRMaterial and not the StandardMaterial class to make a useful comparison (environment lighting would change things, for one).

1 Like

And totally agree exr files are HDR so can go way over 1 so they do not translate well into jpg. As a quick test you could convert to a .hdr texture and try to use this instead to a least evaluate where the loss of energy is coming from.

1 Like

@Evgeni_Popov That does make sense, could you provide some information about the exr to rgbd convertion? I couldnt find anything online, only that rgbd is the format that the kinect captures to.

I converted my materials to PBR and still got the same result, I even have the same values than in UE4 (just roughness 1 and everything else off). Also there wasnt any difference changing the enviroment light.

@sebavan I tried to use hdrs as the lightmaps but apparently babylonjs doesnt support them.

Is there a way to just increse the intensity of the lightmap inside of Babylonjs?

I don’t know tools that convert .exr to png RGBD (maybe Photostop?). Maybe @PatrickRyan would know?

However, as @sebavan suggested, using .hdr should work, Babylon can read .hdr files.

Do you mind creating a PG so that we can have a look?

1 Like

Yup definitely hdr should be ok and then i could help with converting them to rgbd pngs.

A playground would definitely be the way to go even if with made up assets and not tour product ones.

1 Like

Thanks for the help guys
@sebavan
@Evgeni_Popov I also looked for it in photoshop but couldnt find it.

Heres a PG with hdr and PBR materials: Babylon.js Playground (saved the lightmaps with the same name so only have to change the .hdr to .png for it to work)

Ohhhh I see what s going on, they are not cube textures and we only support HDR cube textures at the moment.

Let me check if there is a quick way to introduce 2d hdr textures.

1 Like

Here the PG updated to load .hdr textures (I think we will have to add in the engine .hdr texture loading in some way):

https://www.babylonjs-playground.com/#8EGGW2#2

Lightmaps as .png:

Lightmaps as .hdr:

That makes a difference!

4 Likes

First you would need to use the same environment texture as this might make a strong difference. About the size as you want to use hdr you need to be able to use float/half float texture and RGBD is a nice format for this. You could basically store your texture in RGBD format and load it later in babylon keeping both your hdr values and the small size.

1 Like

I am not sure I want full hdr support as they require full float support are pretty big, slow and won t be able to generate mip maps in RGB32F format :frowning:

I am more open to create the required toolset to convert into RGBD and use those as lightmaps.

Any thoughts ?

FYI the new required texture would be:

import { Nullable } from "../../../types";
import { Engine } from "../../../Engines/engine";
import { InternalTexture } from "../../../Materials/Textures/internalTexture";
import { IInternalTextureLoader } from "../../../Materials/Textures/internalTextureLoader";
import { StringTools } from '../../../Misc/stringTools';
import { HDRTools } from '../../../Misc/HighDynamicRange/hdr';
import { Constants } from '../../../Engines/constants';

/**
 * Implementation of the HDR Texture Loader.
 * @hidden
 */
export class _HDRTextureLoader implements IInternalTextureLoader {
    /**
     * Defines wether the loader supports cascade loading the different faces.
     */
    public readonly supportCascades = false;

    /**
     * This returns if the loader support the current file information.
     * @param extension defines the file extension of the file being loaded
     * @returns true if the loader can load the specified file
     */
    public canLoad(extension: string): boolean {
        return StringTools.EndsWith(extension, ".hdr");
    }

    /**
     * Uploads the cube texture data to the WebGL texture. It has already been bound.
     * @param data contains the texture data
     * @param texture defines the BabylonJS internal texture
     * @param createPolynomials will be true if polynomials have been requested
     * @param onLoad defines the callback to trigger once the texture is ready
     * @param onError defines the callback to trigger in case of error
     */
    public loadCubeData(data: ArrayBufferView | ArrayBufferView[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
        throw ".hdr not supported in Cube, use the dedicated HDRCubeTexture";
    }

    /**
     * Uploads the 2D texture data to the WebGL texture. It has already been bound once in the callback.
     * @param data contains the texture data
     * @param texture defines the BabylonJS internal texture
     * @param callback defines the method to call once ready to upload
     */
    public loadData(data: ArrayBufferView, texture: InternalTexture,
        callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
        var bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);

        var hdrInfo = HDRTools.RGBE_ReadHeader(bytes);
        var pixels = HDRTools.RGBE_ReadPixels(bytes, hdrInfo);

        texture.type = Constants.TEXTURETYPE_FLOAT;
        texture.format = Constants.TEXTUREFORMAT_RGB;
        // Mip maps can not be generated on FLOAT RGB textures.
        texture.generateMipMaps = false;

        callback(hdrInfo.width, hdrInfo.height, texture.generateMipMaps, false, () => {
            texture.getEngine()._uploadDataToTextureDirectly(texture, pixels, 0, 0, undefined, true);
        });
    }
}

// Register the loader.
Engine._TextureLoaders.push(new _HDRTextureLoader());

1 Like

So about the loader, it might be like this:

https://www.babylonjs-playground.com/#8EGGW2#7

You could create your RGBD Files like this:

https://playground.babylonjs.com/#95Y4D7

replace all your .hdr by the result of this process

And then you could reload them as such:

 let currentLightmap = new BABYLON.Texture(
                    "test.png",
                    scene,
                    false,
                    false);
                currentLightmap.isRGBD = true;
                BABYLON.RGBDTextureTools.ExpandRGBDTexture(currentLightmap);

As we do here:

https://playground.babylonjs.com/#FZS1ES#3

4 Likes

I think enabling the tone mapping help a lot:

No tone mapping:

Default tone mapping:

ACES tone mapping:

That’s great!

However, it does not help for mimapping, as the texture is still a (half) float texture.

To be able to use mipmapping, I think one should not make the BABYLON.RGBDTextureTools.ExpandRGBDTexture(currentLightmap); call and let the shader do the decoding. However, I wonder if the mipmapping process generates valid mip maps for RGBD encoded data…

Also, I think it’s a good idea to still have the ability to create a texture from a .hdr file thanks to your .hdr loader.

1 Like

Woa guys, I knew Babylon’s community was famous for being very helpful but I didnt expect this much!

@sebavan This seems to be the solution! I am going to test it out and come back with the results, but its going to take me some time, I am still getting use to javascript in general.

@Evgeni_Popov It does! My previous comparison is using ACES tone mapping if I remember correctly, I am going to keep tweaking it once I get @sebavan method going.

3 Likes

That’s great!

Removing the line means you do have mipmapping: with the line you don’t have them.

However, it also means a little more work in the shader because the rgbd value has to be decoded each time the lightmap is accessed. However, compared to the other stuff done by the PBR material, the extra work should be negligible.

@sebavan: I think there’s a small bug somewhere as the inspector is still saying that the texture has mipmaps when decoding the rgbd to a float texture => the generateMipMaps property of the internalTexture is not reset to false?

Probably cause it has only been used with prediltered env always having mipmaps despite not being created manually :slight_smile: