MONOGRID / gainmap-js

A Javascript (TypeScript) Port of Adobe Gainmap Technology for storing HDR Images using an SDR Image + a gainmap
https://monogrid.github.io/gainmap-js/
MIT License
70 stars 5 forks source link

Confusion about gainmap-js #32

Closed nieyuan1980 closed 3 months ago

nieyuan1980 commented 3 months ago

Hello, I have used gainmap-js. But the effect is not ideal. This is a comparison chart. As you can see Using HDR_JPG to apply background, the image displays jagged. But using HDR is relatively smooth. 74e1ca6153ddfef40c510d4eed9155a72359fbbc_2_1035x507

ecc0ab8e45e4f16cc5558352034d998f53840338_2_1035x505

My steps are: 1、Download a 4K HDR file (sunflowers_puresky_4k. hdr) 2、Use tool convert it to HDR_JPG (sunflowers_puresky_4k. jpg) 3、Refer to the official code of gainmap-js

This is my code:

<template>
    <div>
    </div>
</template>

<script>

    import * as THREE from 'three';
    import {GUI} from 'three/examples/jsm/libs/lil-gui.module.min.js';
    import Stats from 'three/examples/jsm/libs/stats.module.js';
    import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
    import {RGBELoader} from 'three/examples/jsm/loaders/RGBELoader.js';
    import {GainMapLoader, HDRJPGLoader} from '@monogrid/gainmap-js';

    const params = {
        envMap: 'HDR JPG',
        roughness: 0.0,
        metalness: 1.0,
        exposure: 1.0
    };

    let container, stats;
    let camera, scene, renderer, controls;
    let torusMesh;
    let hdrJpg, hdrJpgPMREMRenderTarget, hdrJpgEquirectangularMap;
    let gainMap, gainMapPMREMRenderTarget, gainMapBackground;
    let hdrPMREMRenderTarget, hdrEquirectangularMap;

    export default {
        mounted() {
            this.init();

            this.animate();

            // 监听窗口变化
            window.addEventListener("resize", () => {
                // 重置渲染器宽高比
                renderer.setSize(window.innerWidth, window.innerHeight);
                // 重置相机宽高比
                camera.aspect = window.innerWidth / window.innerHeight;
                // 更新相机投影矩阵
                camera.updateProjectionMatrix();
            });
        },
        methods: {
            animate() {
                requestAnimationFrame(this.animate);
                stats.begin();
                this.render();
                stats.end();
            },
            render() {

                let pmremRenderTarget, equirectangularMap;

                switch (params.envMap) {
                    case 'HDR JPG':
                        pmremRenderTarget = hdrJpgPMREMRenderTarget;
                        equirectangularMap = hdrJpgEquirectangularMap;
                        break;
                    case 'Webp Gain map (separate)':
                        pmremRenderTarget = gainMapPMREMRenderTarget;
                        equirectangularMap = gainMapBackground;
                        break;
                    case 'HDR':
                        pmremRenderTarget = hdrPMREMRenderTarget;
                        equirectangularMap = hdrEquirectangularMap;
                        break;
                }

                torusMesh.material.roughness = params.roughness;
                torusMesh.material.metalness = params.metalness;

                scene.environment = equirectangularMap;
                scene.background = equirectangularMap;
                renderer.toneMappingExposure = params.exposure;

                renderer.render(scene, camera);
            },
            init() {
                //
                container = document.createElement('div');
                document.body.appendChild(container);

                //
                camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 500);
                camera.position.set(0, 0, -120);

                //
                scene = new THREE.Scene();

                //
                renderer = new THREE.WebGLRenderer();
                renderer.toneMapping = THREE.ACESFilmicToneMapping;
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(window.innerWidth, window.innerHeight);
                container.appendChild(renderer.domElement);

                // let geometry = new THREE.TorusKnotGeometry(18, 8, 200, 40, 1, 3);
                let geometry = new THREE.BoxGeometry(10, 10, 10);

                let material = new THREE.MeshStandardMaterial({
                    color: 0xffffff,
                    metalness: params.metalness,
                    roughness: params.roughness
                });
                torusMesh = new THREE.Mesh(geometry, material);
                scene.add(torusMesh);
                geometry = new THREE.PlaneGeometry(200, 200);
                material = new THREE.MeshBasicMaterial();
                const pmremGenerator = new THREE.PMREMGenerator(renderer);
                pmremGenerator.compileEquirectangularShader();

                THREE.DefaultLoadingManager.onLoad = function () {
                    pmremGenerator.dispose();
                };

                hdrJpg = new HDRJPGLoader(renderer)
                    .load('textures/sunflowers_puresky_4k.jpg', function () {
                        hdrJpgEquirectangularMap = hdrJpg.renderTarget.texture;
                        hdrJpgPMREMRenderTarget = pmremGenerator.fromEquirectangular(hdrJpgEquirectangularMap);
                        hdrJpgEquirectangularMap.mapping = THREE.EquirectangularReflectionMapping;
                        hdrJpgEquirectangularMap.needsUpdate = true;
                        hdrJpg.dispose();

                    }, function (progress) {
                        console.log('jpg', progress);
                    });

                gainMap = new GainMapLoader(renderer)
                    .load([
                        'textures/gainmap/spruit_sunrise_4k.webp',
                        'textures/gainmap/spruit_sunrise_4k-gainmap.webp',
                        'textures/gainmap/spruit_sunrise_4k.json'
                    ], function () {
                        gainMapBackground = hdrJpg.renderTarget.texture;
                        gainMapPMREMRenderTarget = pmremGenerator.fromEquirectangular(gainMapBackground);
                        gainMapBackground.mapping = THREE.EquirectangularReflectionMapping;
                        gainMapBackground.needsUpdate = true;
                        gainMap.dispose();
                    }, function (progress) {
                    });

                hdrEquirectangularMap = new RGBELoader()
                    .load('textures/sunflowers_puresky_4k.hdr', function () {
                        hdrPMREMRenderTarget = pmremGenerator.fromEquirectangular(hdrEquirectangularMap);
                        hdrEquirectangularMap.mapping = THREE.EquirectangularReflectionMapping;
                        hdrEquirectangularMap.minFilter = THREE.LinearFilter;
                        hdrEquirectangularMap.magFilter = THREE.LinearFilter;
                        hdrEquirectangularMap.needsUpdate = true;
                    }, function (progress) {
                    });

                //
                controls = new OrbitControls(camera, renderer.domElement);
                controls.minDistance = 50;
                controls.maxDistance = 300;

                //
                stats = new Stats();
                container.appendChild(stats.dom);

                //
                const gui = new GUI();
                gui.add(params, 'envMap', ['HDR JPG', 'Webp Gain map (separate)', 'HDR']).onChange();
                gui.add(params, 'roughness', 0, 1, 0.01);
                gui.add(params, 'metalness', 0, 1, 0.01);
                gui.add(params, 'exposure', 0, 2, 0.01);
                gui.open();
            },
        },
    };
</script>

Excuse me, is there an error in any step?

daniele-pelagatti commented 3 months ago

Looking at your images: this is 90% because of JPEG compression. If you use our tool at https://gainmap-creator.monogrid.com try to raise the JPEG compression quality until the result satisfies you.

Gainmap's gains in file size imply the acceptance of some compromises: HDR files are lossless while JPEG is very much lossy and, sometimes, incurs in this kind of data loss.

let me know if this solves your issue, thanks

nieyuan1980 commented 3 months ago

Following your instructions, I improved the JPEG compression quality (from 0.9 to 1). This has indeed made a significant improvement. There is a new issue now that the exposure of the generated jpeg seems to be insufficient compared to the original image. May I ask how this situation needs to be resolved?Thank you! This is the setting parameter: 1、Display exposure(1.59) 2、Display(SDR + Applied Gain map) 3、Compression quality(1) tool

This is the generated jpeg: 捕获

daniele-pelagatti commented 3 months ago

I suspect you expect the "Display Exposure" parameter to affect your image,

"Display Exposure" does not brighten the encoded output, it is only a "Viewer parameter": a parameter which affects what you see in the preview image inside the converter web page.

You will need to brighten the skycube by increasing the backgroundIntensity in your threejs scene in order to achieve the effect you desire

nieyuan1980 commented 3 months ago

I increased the exposure from 1 to 6, and the final result was very satisfactory! This is really great. Thank you for your reply and I would like to send you my best wishes. The code is here

daniele-pelagatti commented 3 months ago

Thank you for letting me know! I'm going to close this then

hartonoda commented 3 months ago

@nieyuan1980 are you a developer ? I'm a colleague of @daniele-pelagatti can you contact me or leave me your email ? thanks