[SOLVED]: PBR not properly rendered:[don't forget to gen tangents for your Mesh]

Hi,guys:
I’m trying to use PBR in my project.
I downloaded PBR materials from free material site, this is what I got:


these are material samples


something is wrong, I think
:thinking: :thinking: :thinking:

sApp = (SimpleApplication) app;
        sApp.getFlyByCamera().setUpVector(Vector3f.UNIT_Z);
        ArrayList <ByteBuffer> data = new ArrayList<>();
        data.add(ByteBuffer.allocateDirect(320*240*4));
        ima = new Image(Image.Format.RGBA8,320,240,32, data, ColorSpace.Linear);
        t2d = new Texture2D(ima);
        Box b = new Box(1, 1, 1);
        Quad qd = new Quad(2,2);
        Sphere sph = new Sphere(20,20,.6f);
        Geometry geomBox = new Geometry("box", b);
        Geometry geomSphere = new Geometry("qd",sph);
        Geometry geomSphere1 = new Geometry("qd1",sph);
        BaseGsw geomSurface = new BaseGsw("surface",rowx,columy,(20f/rowx),(20f/columy),
                1f,BaseGsw.CwType.POLYGONMIRRO);
        this.sur = geomSurface;
        Material mat00 = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        mat00.setColor("Color", ColorRGBA.Blue);
        Material mat01 = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        mat01.setTexture("ColorMap", app.getAssetManager().loadTexture("Textures/icon/am10.png"));


        Material mat10 = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
        mat10.setColor("GlowColor", new ColorRGBA(0f,1f,0,0.17f));

        Material mat20 = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
        mat20.setBoolean("UseMaterialColors",true);
        mat20.setFloat("Shininess", 1f);
        mat20.setColor("Diffuse", ColorRGBA.Brown ); // with Lighting.j3md
        mat20.setColor("Ambient", ColorRGBA.Pink );
        mat20.setColor("Specular", ColorRGBA.Green );

        Material mat21 = new Material(app.getAssetManager(), "Common/MatDefs/Light/PBRLighting.j3md");

        mat21.setTexture("MetallicMap",app.getAssetManager().loadTexture("Textures/pbr/badlands-boulders-bl/metallic.png"));
        mat21.setTexture("RoughnessMap",app.getAssetManager().loadTexture("Textures/pbr/badlands-boulders-bl/roughness.png"));
        mat21.setTexture("NormalMap",app.getAssetManager().loadTexture("Textures/pbr/badlands-boulders-bl/normal-ogl.png"));
        mat21.setTexture("BaseColorMap",app.getAssetManager().loadTexture("Textures/pbr/badlands-boulders-bl/albedo.png"));
        mat21.setTexture("LightMap",app.getAssetManager().loadTexture("Textures/pbr/badlands-boulders-bl/ao.png"));
        mat21.setTexture("ParallaxMap",app.getAssetManager().loadTexture("Textures/pbr/badlands-boulders-bl/height.png"));
        mat21.setBoolean("LightMapAsAOMap",true);
        mat21.setFloat("NormalType",1.0f);


        Material mat22 = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
        mat22.setTexture("NormalMap",app.getAssetManager().loadTexture("Textures/pbr/steelplate1-ue/normal-dx.png"));
        mat22.setTexture("DiffuseMap",app.getAssetManager().loadTexture("Textures/pbr/steelplate1-ue/albedo.png"));
        mat22.setTexture("ParallaxMap",app.getAssetManager().loadTexture("Textures/pbr/steelplate1-ue/height.png"));
 
        Material mat23 = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
        mat23.setTexture("DiffuseMap",t2d);

        if(true){
            sun = new DirectionalLight();
            sun.setColor(ColorRGBA.White);
            ((DirectionalLight)sun).setDirection(new Vector3f(0,0,-1f).normalizeLocal());
            sApp.getRootNode().addLight(sun);
            if(false){
                int shadlesize = 16;
                DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(app.getAssetManager(), shadlesize,3);
                dlsr.setLight((DirectionalLight)sun);
                app.getViewPort().addProcessor(dlsr);
                sApp.getRootNode().setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
            }
        }else if(true){
            Light sun2 = new PointLight();
            sun2.setColor(ColorRGBA.White);
            ((PointLight)sun2).setRadius(40);
            ((PointLight)sun2).setPosition(new Vector3f(3f,3f,23f));
            sApp.getRootNode().addLight(sun2);
            if(false){
                int shadlesize = 1024;
                PointLightShadowRenderer plsr = new PointLightShadowRenderer(app.getAssetManager(), shadlesize);
                plsr.setLight((PointLight) sun2);
                app.getViewPort().addProcessor(plsr);
                sApp.getRootNode().setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
            }
        }

        geomBox.setMaterial(mat21);
        geomBox.setQueueBucket(RenderQueue.Bucket.Translucent);
        geomSurface.setMaterial(mat22);
        geomSphere.setMaterial(mat22);
        geomSphere.setQueueBucket(RenderQueue.Bucket.Translucent);
        geomSphere1.setMaterial(mat22);
        geomSphere1.setQueueBucket(RenderQueue.Bucket.Translucent);

        geomSurface.move(-3,-3,3);
        geomSphere.move(1,1,7);
        geomSphere1.move(3,5,8);
        geomBox.move(4f,4f,7f);
        // GUI part
        Locale locale = Locale.getDefault();
        rb = ResourceBundle.getBundle("message",locale);
        BitmapFont myFont = app.getAssetManager().loadFont("Interface/Fonts/b32.fnt");
        hudText = new BitmapText(myFont,false, false);
        //hudText.setSize(guiFont.getCharSet().getRenderedSize());      // font size
        hudText.setColor(ColorRGBA.Orange);                             // font color
        hudText.setText(rb.getString("a2"));             // the text
        hudText.setLocalTranslation(300, hudText.getLineHeight(), 0); // position
        //sApp.getGuiNode().attachChild(hudText);
        geomSurface.getMesh().updateBound();


        sApp.getRootNode().attachChild(geomBox);
        sApp.getRootNode().attachChild(geomSurface);
        sApp.getRootNode().attachChild(geomSphere);
        sApp.getRootNode().attachChild(geomSphere1);

        sApp.getCamera().lookAt(geomBox.getWorldTranslation(),Vector3f.UNIT_Z);

        InputManager im = sApp.getInputManager();
        im.addMapping("touched",new TouchTrigger(TouchInput.ALL));
        im.addListener(new TouchListener() {
            @Override
            public void onTouch(String name, TouchEvent event, float tpf) {
                if(event.getType()== TouchEvent.Type.SCALE_MOVE){
                    hudText.setText("Scale Move "+ name);
                }
                if(event.getType()== TouchEvent.Type.MOVE){
                    hudText.setText("Move "+ name);
                }
            }
        },"touched");

It looks like your scene still needs a LightProbe, which is required to simulate indirect lighting in PBR.

You can generate your own unique light probes, but I’d suggest starting off by using this premade light probe that is available in the jme-testdata library: jmonkeyengine/jme3-testdata/src/main/resources/Scenes/defaultProbe.j3o at master · jMonkeyEngine/jmonkeyengine · GitHub

Also here’s a video that uses the sdk and scene composer to show how light probes work with pbr in jme:

1 Like

thanks :smiley:
I’ll try to add a light prob.
There is little documentations that describe the PBR part.


No~~ :sob:

Spatial probeHolder = getApplication().getAssetManager().loadModel("Scenes/defaultProbe.j3o");
        LightProbe probe = (LightProbe)probeHolder.getLocalLightList().get(0);
        probe.setPosition(Vector3f.ZERO);
        probeHolder.removeLight(probe);
        sApp.getRootNode().addLight(probe);
1 Like

It does look like the LightProbe worked, since the previously black surfaces of your PBR box are now lit (the other models aren’t using PBRLighting, so they will not be affected by a LightProbe, and they would need an AmbientLight to simulate indirect light with Lighting.j3md)

However I do notice how the PBR box’s texture does not look nearly as detailed as the preview from the site you downloaded it from.

Those sites nearly always use extra rendering techniques like filters or tesselation to render the best possible result to make their textures look more detailed and as good as possible. Some sites have way better PBR textures than others in my experience, it honestly mostly comes down to how much effort they put into making a good normal map. Some sites have PBR textures with crappy normal maps and then overcompensate with things like extreme parallax and tesselation when they take a screenshot of their final render, which unfortunately don’t end up translating well into JME

But it is possible to get better results regardless of the quality of your PBR textures.

A lot of it comes down to the minor details in how you setup your lighting. For example, normal maps will nearly always look better and more exagerated when light is coming in at the right angle.

And I see that your directional light (sun) is setup with a very unusual direction of 0,0,-1:

So I would try using different directions and see what happens. Especially for a box, if you have the sun coming in directly from the z axis like that, you are pretty much just hitting the front face directly at a perpendicular angle, and are essentially bypassing the the side faces of the box that are parallel to the light direction, which ironically will result in the least amount of detail possible for a lit box. So I would try a sun direction that is more irregular, something like (0.1, -0.4, 0.2).normalize()

add new material from jme-testdata

        Material mat23 = new Material(app.getAssetManager(), "Common/MatDefs/Light/PBRLighting.j3md");

        mat23.setTexture("RoughnessMap",app.getAssetManager().loadTexture("Textures/pbr/txt15/Gravel015_1K_Roughness.png"));
        //mat23.setTexture("NormalMap",app.getAssetManager().loadTexture("Textures/pbr/txt15/Gravel015_1K_Normal.png"));
        mat23.setTexture("NormalMap",app.getAssetManager().loadTexture("Textures/pbr/badlands-boulders-bl/normal-ogl.png"));
        mat23.setTexture("BaseColorMap",app.getAssetManager().loadTexture("Textures/pbr/txt15/Gravel015_1K_Color.png"));
        mat23.setTexture("LightMap",app.getAssetManager().loadTexture("Textures/pbr/txt15/Gravel015_1K_AmbientOcclusion.png"));
        mat23.setTexture("ParallaxMap",app.getAssetManager().loadTexture("Textures/pbr/txt15/Gravel015_1K_Displacement.png"));
        mat23.setBoolean("LightMapAsAOMap",true);
        mat23.setFloat("NormalType",1.0f);


add ambLight

AmbientLight al = new AmbientLight();
        al.setColor(ColorRGBA.White.mult(0.5f));
        sApp.getRootNode().addLight(al);

and new light direction

sun = new DirectionalLight();
            sun.setColor(ColorRGBA.White);
            ((DirectionalLight)sun).setDirection(new Vector3f(0.1f, -0.4f, 0.2f).normalizeLocal());
            sApp.getRootNode().addLight(sun);

sorry ,repaired a mistake

mat23.setTexture("NormalMap",app.getAssetManager().loadTexture("Textures/pbr/txt15/Gravel015_1K_Normal.png"));


I don’t see any normal and “bump” effect.

only roughtness


only normal

1 Like

You might also need to generate tangents, normal maps won’t show if tangents are missing.

Most models come with tangents pre-generated, but it looks like you are using basic boxes and quads in jme, which (if I recall correctly) don’t have tangents by default, so you can just call this method in jme’s MikktSpaceTangentGenerator class to generate them:
https://javadoc.jmonkeyengine.org/v3.4.0-beta1/com/jme3/util/mikktspace/MikktspaceTangentGenerator.html#generate-com.jme3.scene.Spatial-

Also, another thing that is important to know about using an ambient light with PBRLighting vs with Lighting.j3md:

The ambient light will be used to scale the final lighting contribution of the LightProbe. So your current AmbientLight value will cause your PBR models to be dimmed, and usually you should just leave AmbientLight as a full white value with PBR unless you are simulating nightime or caves or something like that. But a full white ambient light would look very bright and offputting in Lighting.j3md. So usually you shouldn’t try to mix PBRLighting with Lighting.j3md because it will be hard (if not impossible) to tweak the lighting to look good with both at the same time

Thanks!
It works!!!


just add one line:

Box b = new Box(1, 1, 1);
TangentBinormalGenerator.generate(b);
3 Likes

Glad you got it working!

You could get potentially even better results from using the newer MikktSpaceTangentGenerator to generate tangents, especially for more complex models, but a box is such a simple model to generate tangents for so it probably doesn’t make a difference here.

I’d also suggest, if you’re feeling up for it, to try out the new 3.8.0-alpha2 release and see if your PBR texture renders more similar to the source you downloaded it from. There was a small bug fix to PBRLighting.j3md in the new version that should make jme’s pbr render more accurate.

2 Likes