rvandoosselaer / Blocks

A block (voxel) engine for jMonkeyEngine
BSD 3-Clause "New" or "Revised" License
42 stars 14 forks source link

Feature request:Add Water PBR material #17

Closed Ali-RS closed 4 years ago

Ali-RS commented 4 years ago

It will be nice to add a PBR version of the water material. :)

rvandoosselaer commented 4 years ago

Could you help me with creating a PBR water material @Ali-RS ? I made some changes to the TypeRegistry (see #23) so it's possible to load custom materials for a block.

I would like to create a new example showcasing how you can use a PBR material as a type for a block. I guess a water block is an ideal example for this.

Ali-RS commented 4 years ago

Could you help me with creating a PBR water material @Ali-RS ?

Do you mean you need help with the shader side?

tbh, I have not worked with shaders so I am not sure if I can be of any help. If I am understanding it right, you just copied Lighting material frag and vert shaders and modified it for your water material, can't the same thing be done for PBR too?

rvandoosselaer commented 4 years ago

I meant more in general. I haven’t worked with PBR materials before and have not yet looked into it. I don’t know what settings or lights you need to supply to make it look good. You have lights, lightprobes, environment maps, ... It’s still a bit unclear for me.

The shader part to simulate waves, I can adapt later.

Ali-RS commented 4 years ago

I meant more in general. I haven’t worked with PBR materials before and have not yet looked into it. I don’t know what settings or lights you need to supply to make it look good. You have lights, lightprobes, environment maps, ... It’s still a bit unclear for me.

Ah, yes, I can help with this stuff.

You can read about PBR workflow in JME wiki:

https://wiki.jmonkeyengine.org/jme3/advanced/pbr_part1.html# (it's in 3 parts)

In short, the light probe is the thing that contains environment map data. You can use a pre-generated light probe or generate your own light probe.

Then you can attach the light probe to the root node as you do for regular lights. (you can also add directional (if you want shadow or using have normal map on your models) and ambient light (for adjusting env map lighting i.e day/night lighting).

For generating a light probe you just need to attach a SkyBox into the scene and run this code:

Here is an example code of how to generate and export light probe into j3o:


public class LightProbExporter extends SimpleApplication {

    private Timer makeProbeTimer;

    @Override
    public void simpleInitApp() {

        initSky();
        initLight();

    }

    @Override
    public void simpleUpdate(float tpf) {
        makeProbeTimer.update(tpf);
    }

    public static void main(String[] args) {
        LightProbExporter app = new LightProbExporter();
        app.start();
    }

    private void initLight() {
        final EnvironmentCamera envCam = new EnvironmentCamera(256, Vector3f.ZERO);
        stateManager.attach(envCam);

        makeProbeTimer = new Timer();
        makeProbeTimer.schedule(new TimerTask() {
            @Override
            public void run(long time) {
                final LightProbe probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode, EnvMapUtils.GenerationType.Fast,  new JobProgressAdapter<LightProbe>() {
                    @Override
                    public void done(LightProbe result) {
                        System.out.println("Done rendering env maps");
                        //Saving
                        Node probeNode = new Node("lightprobe node");
                        probeNode.addLight(result);
                        BinaryExporter ex = new BinaryExporter();
                        try {
                            ex.save(probeNode, new File("path to file/probe.j3o"));
                            System.out.println("Done exporting env maps");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        //done saving
                    }
                });

                ((SphereProbeArea) probe.getArea()).setRadius(500);
                rootNode.addLight(probe);
                makeProbeTimer.setEnable(false);
            }
        }, 5);
    }

    private void initSky() {

        /*Texture west = assetManager.loadTexture("envmap/fantasy/Sunny_01A_left.tga");
        Texture east = assetManager.loadTexture("envmap/fantasy/Sunny_01A_right.tga");
        Texture north = assetManager.loadTexture("envmap/fantasy/Sunny_01A_back.tga");
        Texture south = assetManager.loadTexture("envmap/fantasy/Sunny_01A_front.tga");
        Texture up = assetManager.loadTexture("envmap/fantasy/Sunny_01A_up.tga");
        Texture down = assetManager.loadTexture("envmap/fantasy/Sunny_01A_down.tga");*/

        //Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
        //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", SkyFactory.EnvMapType.CubeMap);
        Spatial sky = SkyFactory.createSky(assetManager, "Textures/sky/outside.hdr", SkyFactory.EnvMapType.EquirectMap);
        rootNode.attachChild(sky);
    }
}

and load it like below:

Node probeNode = (Node) assetManager.loadModel("Scenes/lightprobe/probe.j3o");
        LightProbe probe = (LightProbe) probeNode.getLocalLightList().iterator().next();
        probe.setPosition(new Vector3f(0, 0, 0));
        probe.getArea().setRadius(500);
        rootNode.addLight(probe);
Ali-RS commented 4 years ago

Regarding the material parameters, you have BaseColorMap, it's the same DiffuseMap we have in Phong lighting.

You can adjust the metalness of the material using the "Metallic" parameter. It's a float value in [0,1]. You can adjust the roughness of the material using the "Roughness" parameter. It's a float value in [0,1].

You can optionally use Normal and Parallex textures (same as Phong lighting) and MetallicRoughness map.

Ali-RS commented 4 years ago

Also, you can see some PBR examples here: https://github.com/jMonkeyEngine/jmonkeyengine/tree/master/jme3-examples/src/main/java/jme3test/light/pbr

And here are some pre-generated light probes: lightprobes.zip

Please feel free to ask if you have any questions.

rvandoosselaer commented 4 years ago

Awesome explanation, thanks! I’ll try to create an example with this info.

I hope to get this update released soon.

Ali-RS commented 4 years ago

You're welcome.

btw, in case you need, here you can download a free water texture: https://lowlypoly.com/collections/free/products/stylized-water-texture

Some useful tips:

Ali-RS commented 4 years ago

@rvandoosselaer I forgot to mention that you also need to add this in simpleInitApp():

// Configure the scene for PBR
renderManager.setPreferredLightMode(TechniqueDef.LightMode.SinglePassAndImageBased);
renderManager.setSinglePassLightBatchSize(10);
rvandoosselaer commented 4 years ago

I committed a PBR test case in the examples subproject. If you have remarks or improvements please let me know. Will test some more myself (still need to test with a ‘spritesheet’ texture) and begin preparing for a release.

I did notice that the parallax mapping doesn’t appear to be working.

Ali-RS commented 4 years ago

Thanks, going to try it soon :)

Ali-RS commented 4 years ago

Just tried it, looks very nice :)

I did notice that the parallax mapping doesn’t appear to be working.

It does work with original PBRLighting material. Maybe something has broken while adapting it for water.

rvandoosselaer commented 4 years ago

It does work with original PBRLighting material. Maybe something has broken while adapting it for water.

You were correct, there was an issue in the fragment shader, it's fixed.