jMonkeyEngine / jmonkeyengine

A complete 3-D game development suite written in Java.
http://jmonkeyengine.org
BSD 3-Clause "New" or "Revised" License
3.83k stars 1.12k forks source link

3.2 Core profile renders some PBR materials darker than Compatibility profile #1903

Open stephengold opened 1 year ago

stephengold commented 1 year ago

I recently discovered this issue recently in JME 3.6.0-alpha1 and created a test case. Here's how it renders with the 3.2 Core profile: Core

Here's how it renders with Compatibility profile: Compatibility

Here's the test-case java:

/**
 * Reproduces an issue where PBR materials render much darker with the Core 3.2
 * profile than with the Compatibility profile.
 *
 * <p>
 * This test also uses 4 textures (AO, Diffuse, GL Normal, and Rough), all at 2K
 * resolution in PNG format, which can be downloaded from
 * https://polyhaven.com/a/marble_01 and unpacked to the
 * src/main/resources/marble_01/textures directory.
 *
 * <p>
 * This test also uses a precomputed light probe which can be obtained from
 * https://github.com/stephengold/jme-vehicles/blob/master/MavCommon/src/main/resources/Textures/skies/moon/probe-day.j3o
 * and saved to the src/main/resources directory.
 */
public class TestIssue extends SimpleApplication {

    public static void main(String[] args) {
        boolean loadDefaults = true;
        AppSettings appSettings = new AppSettings(loadDefaults);
        appSettings.setGammaCorrection(true);
//        appSettings.setRenderer(AppSettings.LWJGL_OPENGL2); // to test Compatibility profile
        appSettings.setRenderer(AppSettings.LWJGL_OPENGL32); // to test Core 3.2 profile

        TestIssue application = new TestIssue();
        application.setSettings(appSettings);
        application.start();
    }

    @Override
    public void simpleInitApp() {
        flyCam.setDragToRotate(true);

        // Attach a 9x9 quad at the origin.
        Mesh mesh = new CenterQuad(9f, 9f);
        Geometry quad = new Geometry("quad", mesh);
        rootNode.attachChild(quad);

        // Apply a PBR material to the quad.
        String materialAssetPath = "marble_01.j3m";
        Material material = assetManager.loadMaterial(materialAssetPath);
        quad.setMaterial(material);

        // Add a LightProbe.
        String probeAssetPath = "probe-day.j3o";
        LightProbe probe = (LightProbe) assetManager.loadAsset(probeAssetPath);
        rootNode.addLight(probe);
    }
}

Here's the "marble_01.j3m" material for the test case:

Material marble_01: /Common/MatDefs/Light/PBRLighting.j3md {
    MaterialParameters {
        BaseColor: 1.4 1.4 1.4 1
        BaseColorMap: Repeat marble_01/textures/marble_01_diff_2k.png
        LightMap: Repeat marble_01/textures/marble_01_ao_2k.png
        LightMapAsAOMap: true
        Metallic: 0.01
        NormalMap: Repeat marble_01/textures/marble_01_nor_gl_2k.png
        NormalType: 1
        RoughnessMap: Repeat marble_01/textures/marble_01_rough_2k.png
    }
    AdditionalRenderState {
        FaceCull Off
    }
}

My configuration: x86_64 Mint Linux 21.1 (Ubuntu 5.15.0-57-generic) single monitor

Jan 10, 2023 11:18:25 AM com.jme3.system.JmeDesktopSystem initialize
INFO: Running on jMonkeyEngine 3.6.0-SNAPSHOT
 * Branch: master
 * Git Hash: b0bd1a5
 * Build Date: 2023-01-10
Jan 10, 2023 11:18:26 AM com.jme3.system.lwjgl.LwjglContext printContextInitInfo
INFO: LWJGL 2.9.3 context running on thread jME3 Main
 * Graphics Adapter: null
 * Driver Version: null
 * Scaling Factor: 1
Jan 10, 2023 11:18:26 AM com.jme3.renderer.opengl.GLRenderer loadCapabilitiesCommon
INFO: OpenGL Renderer Information
 * Vendor: NVIDIA Corporation
 * Renderer: GeForce GT 545/PCIe/SSE2
 * OpenGL Version: 3.2.0 NVIDIA 390.157
 * GLSL Version: 1.50 NVIDIA via Cg compiler
stephengold commented 1 year ago

I reproduced this issue using JME v3.5.2-stable, so I don't consider it a regression. However, due to PR #1752, we'll soon have a lot more developers using the OpenGL 3.2 Core profile.

Further effort to characterize the issue:

Ali-RS commented 1 year ago

Are you using assets from test data? I can not find probe-day.j3o and textures used in marble_01.j3m.

stephengold commented 1 year ago
/**
 * This test also uses 4 textures (AO, Diffuse, GL Normal, and Rough), all at 2K
 * resolution in PNG format, which can be downloaded from
 * https://polyhaven.com/a/marble_01 and unpacked to the
 * src/main/resources/marble_01/textures directory.
 *
 * <p>
 * This test also uses a precomputed light probe which can be obtained from
 * https://github.com/stephengold/jme-vehicles/blob/master/MavCommon/src/main/resources/Textures/skies/moon/probe-day.j3o
 * and saved to the src/main/resources directory.
 */
zzuegg commented 1 year ago

As far i remember i stumbled also on this when switching to core profile. Not sure and not at the pc to test but i think adding a a post processor fixed it. Without tracking the issue further down and without checking the specs i kind of assumed additive blending is not allowed in core profile on the 0 framebuffer. imho not an issue since most games beside test projects use a postprocessor and in a hdr pipeline it is kind of required.

As a note. All scenes with multiple lights render also wrong without fpp (core profile only)

Ali-RS commented 1 year ago

Downloaded texture from the said URL but there were no marble_01_ao_2k.png in there so I removed LightMap param from the material file also except marble_01_disp_2k.png the others were not in png format so I converted them to png using gimp.

In my case, the output is the same in both LWJGL_OPENGL2 and LWJGL_OPENGL32.

Screenshot_2023-01-11_00-41-26

Ali-RS commented 1 year ago

Can you please also try TestPBRLighting with both core and compatibility profiles?

stephengold commented 1 year ago

Downloaded texture from the said URL but there were no marble_01_ao_2k.png

Sorry! Poly Haven uses a fancy download interface which requires you to specify the textures/formats you want. In the right sidebar, specify 2K resolution and ZIP archive, then open the hamburger menu button and tick the checkboxes for the 4 textures in PNG format.

stephengold commented 1 year ago

Can you please also try TestPBRLighting with both core and compatibility profiles?

I tried this and didn't see any difference between the 2 profiles.

Ali-RS commented 1 year ago

Sorry! Poly Haven uses a fancy download interface which requires you to specify the textures/formats you want. In the right sidebar, specify 2K resolution and ZIP archive, then open the hamburger menu button and tick the checkboxes for the 4 textures in PNG format.

Ok, grabbed the textures as you explained above.

In my case, the output is still the same in both profiles. They both look dark.

stephengold commented 1 year ago

In my case, the output is still the same in both profiles. They both look dark. Not what I expected!

I continue to investigate...

On my system, the following J3M file is sufficient to reproduce the issue:

Material TestIssue1903: Common/MatDefs/Light/PBRLighting.j3md {
    MaterialParameters {
        Metallic: 0.01
        NormalMap: Repeat marble_01/textures/marble_01_nor_gl_2k.png
    }
}

1903sidebyside

Increasing "Metallic" reduces the difference between the render output of the 2 profiles. I therefore suspect that the issue is connected with how PBR uses normals in non-metallic materials.

zzuegg commented 1 year ago

I cant reproduce this. Could you test if this happens with other light types then a light probe too?

Actually since you mentioned it. If increasing metallic reduces the issue it might be a mipmap issue.

pspeed42 commented 1 year ago

Random thought... this reminds me of the cases where things that the material requires are left unset. Then it becomes up to the specific graphics card + driver to decide what values to stick there (or leave garbage or whatever). Like leaving a tangent buffer out when using normal maps will look odd but work on some cards and be totally black on others.

I haven't been following closely enough to know if that's the case in these examples.

stephengold commented 1 year ago

I reproduced the issue using a texture and light probe from jme3-testdata. For convenience, I've committed the simplified test to the "master" branch of the project repo.

Note that these tests rely on AppSettings configured in main(). Therefore they shouldn't be run from the jme3-examples TestChooser!

stephengold commented 1 year ago

Could you test if this happens with other light types then a light probe too?

For PBR, a light probe is required. I discovered the issue using Probe+Ambient+Direct.

zzuegg commented 1 year ago

The probe is not required to render. If rendering without a probe works as expected it would rule out all framebuffer/srgb/normals/tangent related stuff. It would come down to the access/upload/format of the specularEnvMap.

I have access to other computer at the weekend, intry to reproduce then and launch the debugger

Ali-RS commented 1 year ago

For convenience, I've committed the simplified test to the "master" branch of the project repo.

My result

compatibility-vs-core-profile

As mentioned by pspeed42, might be a graphics card + driver issue.

stephengold commented 1 year ago

To test whether the issue is hardware-, system-, or driver-dependent, I ran the tests on my Windows 11 laptop with GeForce RTX 2070 and LWJGL v3. (All my previous test were with Mint Linux and a GeForce GT 545.)

I reproduced it with ease: image

@zzuegg please provide screenshots for comparison.

stephengold commented 1 year ago

The probe is not required to render. If rendering without a probe works as expected it would rule out all framebuffer/srgb/normals/tangent related stuff. It would come down to the access/upload/format of the specularEnvMap.

When I replaced the probe with an AmbientLight, both renders were solid black, as I expected.

zzuegg commented 1 year ago

Yeah. AmbientLight is the only one not working. Not sure if intentional or not since that one should be replaced by the light probe. However the code is in there guarded by a define. i test and report back today evening.

Notes: (mostly to myself for the testing) a possible reason could also be the use of gl_FragColor. Following the specs it should not be availabe in a core profile Especially when reading from FragColor. I remember having the same issue when not using a floating point render targert when switching to gl4 core profile. (only wih jme shaders, my own rendered the same)

stephengold commented 1 year ago

It occurred to me that there's a commonality between my Windows 11 laptop and my Mint Linux desktop: both have NVDIA graphics adapters.

@zzuegg and @Ali-RS, what brand(s) of graphics adapter did you test?

Ali-RS commented 1 year ago

what brand(s) of graphics adapter did you test?

Advanced Micro Devices, Inc. [AMD/ATI] Robson CE [Radeon HD 6370M/7370M]

and

Intel Corporation 2nd Generation Core Processor Family Integrated Graphics
zzuegg commented 1 year ago

Seems again that the issue comes down to nvidia+windows beeing the worst programming environment. The nividia driver somehow managed in compat mode to work around missing tangents.

If you add: MikktspaceTangentGenerator.generate(quad);

The results are bright as they should be.

pspeed42 commented 1 year ago

From me above: "Random thought... this reminds me of the cases where things that the material requires are left unset. Then it becomes up to the specific graphics card + driver to decide what values to stick there (or leave garbage or whatever). Like leaving a tangent buffer out when using normal maps will look odd but work on some cards and be totally black on others."

nVidia is really nice to program on in one respect because it will take anything you throw at it and "just work". Nice for prototyping and they are great performers even outside of the "one path that most games use". Great solid cards, in general.

But yeah, when you want to deploy on something else then you better not be testing in compatibility mode anymore.

re: Tangents, I think nvidia initializes missing tangent buffers to 1,0,0 or something while other cards just leave it at 0,0,0. So tangents will look like they work from certain directions. It's been a long time, though.

stephengold commented 1 year ago

If you add: MikktspaceTangentGenerator.generate(quad); The results are bright as they should be.

Indeed they are. Thank you, @zzuegg and @pspeed42!

I hadn't realized tangents were required for PBR.

Is there anything more to be done here? Perhaps: