diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/light/pbr/TestPBRLighting.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/light/pbr/TestPBRLighting.java new file mode 100644 index 0000000000..0cbd19da24 --- /dev/null +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/light/pbr/TestPBRLighting.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.jmonkeyengine.screenshottests.light.pbr; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; +import com.jme3.app.state.BaseAppState; +import com.jme3.asset.AssetManager; +import com.jme3.environment.EnvironmentCamera; +import com.jme3.environment.FastLightProbeFactory; +import com.jme3.light.DirectionalLight; +import com.jme3.light.LightProbe; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.ToneMapFilter; +import com.jme3.renderer.Camera; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.texture.plugins.ktx.KTXLoader; +import com.jme3.util.SkyFactory; +import com.jme3.util.mikktspace.MikktspaceTangentGenerator; +import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +/** + * Screenshot tests for PBR lighting. + * + * @author nehon - original test + * @author Richard Tingle (aka richtea) - screenshot test adaptation + * + */ +public class TestPBRLighting extends ScreenshotTestBase { + + private static Stream testParameters() { + return Stream.of( + Arguments.of("LowRoughness", 0.1f, false), + Arguments.of("HighRoughness", 1.0f, false), + Arguments.of("DefaultDirectionalLight", 0.5f, false), + Arguments.of("UpdatedDirectionalLight", 0.5f, true) + ); + } + + /** + * Test PBR lighting with different parameters + * + * @param testName The name of the test (used for screenshot filename) + * @param roughness The roughness value to use + * @param updateLight Whether to update the directional light to match camera direction + */ + @ParameterizedTest(name = "{0}") + @MethodSource("testParameters") + public void testPBRLighting(String testName, float roughness, boolean updateLight, TestInfo testInfo) { + + if(!testInfo.getTestClass().isPresent() || !testInfo.getTestMethod().isPresent()) { + throw new RuntimeException("Test preconditions not met"); + } + + String imageName = testInfo.getTestClass().get().getName() + "." + testInfo.getTestMethod().get().getName() + "_" + testName; + + screenshotTest(new BaseAppState() { + private static final int RESOLUTION = 256; + + private Node modelNode; + private int frame = 0; + + @Override + protected void initialize(Application app) { + Camera cam = app.getCamera(); + cam.setLocation(new Vector3f(18, 10, 0)); + cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y); + + AssetManager assetManager = app.getAssetManager(); + assetManager.registerLoader(KTXLoader.class, "ktx"); + + app.getViewPort().setBackgroundColor(ColorRGBA.White); + + modelNode = new Node("modelNode"); + Geometry model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o"); + MikktspaceTangentGenerator.generate(model); + modelNode.attachChild(model); + + DirectionalLight dl = new DirectionalLight(); + dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + SimpleApplication simpleApp = (SimpleApplication) app; + simpleApp.getRootNode().addLight(dl); + dl.setColor(ColorRGBA.White); + + // If we need to update the light direction to match camera + if (updateLight) { + dl.setDirection(app.getCamera().getDirection().normalize()); + } + + simpleApp.getRootNode().attachChild(modelNode); + + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); + int numSamples = app.getContext().getSettings().getSamples(); + if (numSamples > 0) { + fpp.setNumSamples(numSamples); + } + + fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f))); + app.getViewPort().addProcessor(fpp); + + Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); + simpleApp.getRootNode().attachChild(sky); + + Material pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m"); + pbrMat.setFloat("Roughness", roughness); + model.setMaterial(pbrMat); + + // Set up environment camera + EnvironmentCamera envCam = new EnvironmentCamera(RESOLUTION, new Vector3f(0, 3f, 0)); + app.getStateManager().attach(envCam); + } + + @Override + protected void cleanup(Application app) {} + + @Override + protected void onEnable() {} + + @Override + protected void onDisable() {} + + @Override + public void update(float tpf) { + frame++; + + if (frame == 2) { + modelNode.removeFromParent(); + LightProbe probe; + + SimpleApplication simpleApp = (SimpleApplication) getApplication(); + probe = FastLightProbeFactory.makeProbe(simpleApp.getRenderManager(), + simpleApp.getAssetManager(), + RESOLUTION, + Vector3f.ZERO, + 1f, + 1000f, + simpleApp.getRootNode()); + + probe.getArea().setRadius(100); + simpleApp.getRootNode().addLight(probe); + } + + if (frame > 10 && modelNode.getParent() == null) { + SimpleApplication simpleApp = (SimpleApplication) getApplication(); + simpleApp.getRootNode().attachChild(modelNode); + } + } + }).setBaseImageFileName(imageName) + .setFramesToTakeScreenshotsOn(12) + .run(); + } +} diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/light/pbr/TestPBRSimple.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/light/pbr/TestPBRSimple.java new file mode 100644 index 0000000000..70220f7eef --- /dev/null +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/light/pbr/TestPBRSimple.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2024 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.jmonkeyengine.screenshottests.light.pbr; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; +import com.jme3.app.state.BaseAppState; +import com.jme3.asset.AssetManager; +import com.jme3.environment.EnvironmentProbeControl; +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import com.jme3.util.SkyFactory; +import com.jme3.util.mikktspace.MikktspaceTangentGenerator; +import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +/** + * A simpler PBR example that uses EnvironmentProbeControl to bake the environment + * + * @author Richard Tingle (aka richtea) - screenshot test adaptation + */ +public class TestPBRSimple extends ScreenshotTestBase { + + private static Stream testParameters() { + return Stream.of( + Arguments.of("WithRealtimeBaking", true), + Arguments.of("WithoutRealtimeBaking", false) + ); + } + + /** + * Test PBR simple with different parameters + * + * @param testName The name of the test (used for screenshot filename) + * @param realtimeBaking Whether to use realtime baking + */ + @ParameterizedTest(name = "{0}") + @MethodSource("testParameters") + public void testPBRSimple(String testName, boolean realtimeBaking, TestInfo testInfo) { + if(!testInfo.getTestClass().isPresent() || !testInfo.getTestMethod().isPresent()) { + throw new RuntimeException("Test preconditions not met"); + } + + String imageName = testInfo.getTestClass().get().getName() + "." + testInfo.getTestMethod().get().getName() + "_" + testName; + + screenshotTest(new BaseAppState() { + private int frame = 0; + + @Override + protected void initialize(Application app) { + Camera cam = app.getCamera(); + cam.setLocation(new Vector3f(18, 10, 0)); + cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y); + + AssetManager assetManager = app.getAssetManager(); + SimpleApplication simpleApp = (SimpleApplication) app; + + // Create the tank model + Geometry model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o"); + MikktspaceTangentGenerator.generate(model); + + Material pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m"); + model.setMaterial(pbrMat); + simpleApp.getRootNode().attachChild(model); + + // Create sky + Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); + simpleApp.getRootNode().attachChild(sky); + + // Create baker control + EnvironmentProbeControl envProbe = new EnvironmentProbeControl(assetManager, 256); + simpleApp.getRootNode().addControl(envProbe); + + // Tag the sky, only the tagged spatials will be rendered in the env map + envProbe.tag(sky); + } + + @Override + protected void cleanup(Application app) {} + + @Override + protected void onEnable() {} + + @Override + protected void onDisable() {} + + @Override + public void update(float tpf) { + if (realtimeBaking) { + frame++; + if (frame == 2) { + SimpleApplication simpleApp = (SimpleApplication) getApplication(); + simpleApp.getRootNode().getControl(EnvironmentProbeControl.class).rebake(); + } + } + } + }).setBaseImageFileName(imageName) + .setFramesToTakeScreenshotsOn(10) + .run(); + } +} \ No newline at end of file diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrain.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrain.java new file mode 100644 index 0000000000..37f8063300 --- /dev/null +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrain.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2024 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.jmonkeyengine.screenshottests.terrain; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; +import com.jme3.app.state.BaseAppState; +import com.jme3.asset.AssetManager; +import com.jme3.asset.TextureKey; +import com.jme3.light.AmbientLight; +import com.jme3.light.DirectionalLight; +import com.jme3.light.LightProbe; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.terrain.geomipmap.TerrainLodControl; +import com.jme3.terrain.geomipmap.TerrainQuad; +import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; +import com.jme3.terrain.heightmap.AbstractHeightMap; +import com.jme3.terrain.heightmap.ImageBasedHeightMap; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture.WrapMode; +import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +/** + * This test uses 'PBRTerrain.j3md' to create a terrain Material for PBR. + * + * Upon running the app, the user should see a mountainous, terrain-based + * landscape with some grassy areas, some snowy areas, and some tiled roads and + * gravel paths weaving between the valleys. Snow should be slightly + * shiny/reflective, and marble texture should be even shinier. If you would + * like to know what each texture is supposed to look like, you can find the + * textures used for this test case located in jme3-testdata. + * + * Uses assets from CC0Textures.com, licensed under CC0 1.0 Universal. For more + * information on the textures this test case uses, view the license.txt file + * located in the jme3-testdata directory where these textures are located: + * jme3-testdata/src/main/resources/Textures/Terrain/PBR + * + * @author yaRnMcDonuts (Original manual test) + * @author Richard Tingle (aka richtea) - screenshot test adaptation + */ +@SuppressWarnings("FieldCanBeLocal") +public class TestPBRTerrain extends ScreenshotTestBase { + + private static Stream testParameters() { + return Stream.of( + Arguments.of("FinalRender", 0), + Arguments.of("NormalMap", 1), + Arguments.of("RoughnessMap", 2), + Arguments.of("MetallicMap", 3), + Arguments.of("GeometryNormals", 8) + ); + } + + /** + * Test PBR terrain with different debug modes + * + * @param testName The name of the test (used for screenshot filename) + * @param debugMode The debug mode to use + */ + @ParameterizedTest(name = "{0}") + @MethodSource("testParameters") + public void testPBRTerrain(String testName, int debugMode, TestInfo testInfo) { + + if(!testInfo.getTestClass().isPresent() || !testInfo.getTestMethod().isPresent()) { + throw new RuntimeException("Test preconditions not met"); + } + + String imageName = testInfo.getTestClass().get().getName() + "." + testInfo.getTestMethod().get().getName() + "_" + testName; + + screenshotTest(new BaseAppState() { + private TerrainQuad terrain; + private Material matTerrain; + + private final int terrainSize = 512; + private final int patchSize = 256; + private final float dirtScale = 24; + private final float darkRockScale = 24; + private final float snowScale = 64; + private final float tileRoadScale = 64; + private final float grassScale = 24; + private final float marbleScale = 64; + private final float gravelScale = 64; + + @Override + protected void initialize(Application app) { + SimpleApplication simpleApp = (SimpleApplication) app; + AssetManager assetManager = app.getAssetManager(); + + setUpTerrain(simpleApp, assetManager); + setUpTerrainMaterial(assetManager); + setUpLights(simpleApp, assetManager); + setUpCamera(app); + + // Set debug mode + matTerrain.setInt("DebugValuesMode", debugMode); + } + + private void setUpTerrainMaterial(AssetManager assetManager) { + // PBR terrain matdef + matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/PBRTerrain.j3md"); + + matTerrain.setBoolean("useTriPlanarMapping", false); + + // ALPHA map (for splat textures) + matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png")); + matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png")); + + // DIRT texture + Texture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + dirt.setWrap(WrapMode.Repeat); + matTerrain.setTexture("AlbedoMap_0", dirt); + matTerrain.setFloat("AlbedoMap_0_scale", dirtScale); + matTerrain.setFloat("Roughness_0", 1); + matTerrain.setFloat("Metallic_0", 0); + + // DARK ROCK texture + Texture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); + darkRock.setWrap(WrapMode.Repeat); + matTerrain.setTexture("AlbedoMap_1", darkRock); + matTerrain.setFloat("AlbedoMap_1_scale", darkRockScale); + matTerrain.setFloat("Roughness_1", 0.92f); + matTerrain.setFloat("Metallic_1", 0.02f); + + // SNOW texture + Texture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); + snow.setWrap(WrapMode.Repeat); + matTerrain.setTexture("AlbedoMap_2", snow); + matTerrain.setFloat("AlbedoMap_2_scale", snowScale); + matTerrain.setFloat("Roughness_2", 0.55f); + matTerrain.setFloat("Metallic_2", 0.12f); + + // TILES texture + Texture tiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); + tiles.setWrap(WrapMode.Repeat); + matTerrain.setTexture("AlbedoMap_3", tiles); + matTerrain.setFloat("AlbedoMap_3_scale", tileRoadScale); + matTerrain.setFloat("Roughness_3", 0.87f); + matTerrain.setFloat("Metallic_3", 0.08f); + + // GRASS texture + Texture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + grass.setWrap(WrapMode.Repeat); + matTerrain.setTexture("AlbedoMap_4", grass); + matTerrain.setFloat("AlbedoMap_4_scale", grassScale); + matTerrain.setFloat("Roughness_4", 1); + matTerrain.setFloat("Metallic_4", 0); + + // MARBLE texture + Texture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); + marble.setWrap(WrapMode.Repeat); + matTerrain.setTexture("AlbedoMap_5", marble); + matTerrain.setFloat("AlbedoMap_5_scale", marbleScale); + matTerrain.setFloat("Roughness_5", 0.06f); + matTerrain.setFloat("Metallic_5", 0.8f); + + // Gravel texture + Texture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); + gravel.setWrap(WrapMode.Repeat); + matTerrain.setTexture("AlbedoMap_6", gravel); + matTerrain.setFloat("AlbedoMap_6_scale", gravelScale); + matTerrain.setFloat("Roughness_6", 0.9f); + matTerrain.setFloat("Metallic_6", 0.07f); + + // NORMAL MAPS + Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); + normalMapDirt.setWrap(WrapMode.Repeat); + + Texture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); + normalMapDarkRock.setWrap(WrapMode.Repeat); + + Texture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); + normalMapSnow.setWrap(WrapMode.Repeat); + + Texture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); + normalMapGravel.setWrap(WrapMode.Repeat); + + Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); + normalMapGrass.setWrap(WrapMode.Repeat); + + Texture normalMapTiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); + normalMapTiles.setWrap(WrapMode.Repeat); + + matTerrain.setTexture("NormalMap_0", normalMapDirt); + matTerrain.setTexture("NormalMap_1", normalMapDarkRock); + matTerrain.setTexture("NormalMap_2", normalMapSnow); + matTerrain.setTexture("NormalMap_3", normalMapTiles); + matTerrain.setTexture("NormalMap_4", normalMapGrass); + matTerrain.setTexture("NormalMap_6", normalMapGravel); + + terrain.setMaterial(matTerrain); + } + + private void setUpTerrain(SimpleApplication simpleApp, AssetManager assetManager) { + // HEIGHTMAP image (for the terrain heightmap) + TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); + Texture heightMapImage = assetManager.loadTexture(hmKey); + + // CREATE HEIGHTMAP + AbstractHeightMap heightmap; + try { + heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.3f); + heightmap.load(); + heightmap.smooth(0.9f, 1); + } catch (Exception e) { + throw new RuntimeException(e); + } + + terrain = new TerrainQuad("terrain", patchSize + 1, terrainSize + 1, heightmap.getHeightMap()); + TerrainLodControl control = new TerrainLodControl(terrain, getApplication().getCamera()); + control.setLodCalculator(new DistanceLodCalculator(patchSize + 1, 2.7f)); // patch size, and a multiplier + terrain.addControl(control); + terrain.setMaterial(matTerrain); + terrain.setLocalTranslation(0, -100, 0); + terrain.setLocalScale(1f, 1f, 1f); + simpleApp.getRootNode().attachChild(terrain); + } + + private void setUpLights(SimpleApplication simpleApp, AssetManager assetManager) { + LightProbe probe = (LightProbe) assetManager.loadAsset("Scenes/LightProbes/quarry_Probe.j3o"); + + probe.setAreaType(LightProbe.AreaType.Spherical); + probe.getArea().setRadius(2000); + probe.getArea().setCenter(new Vector3f(0, 0, 0)); + simpleApp.getRootNode().addLight(probe); + + DirectionalLight directionalLight = new DirectionalLight(); + directionalLight.setDirection((new Vector3f(-0.3f, -0.5f, -0.3f)).normalize()); + directionalLight.setColor(ColorRGBA.White); + simpleApp.getRootNode().addLight(directionalLight); + + AmbientLight ambientLight = new AmbientLight(); + ambientLight.setColor(ColorRGBA.White); + simpleApp.getRootNode().addLight(ambientLight); + } + + private void setUpCamera(Application app) { + app.getCamera().setLocation(new Vector3f(0, 10, -10)); + app.getCamera().lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y); + } + + @Override + protected void cleanup(Application app) {} + + @Override + protected void onEnable() {} + + @Override + protected void onDisable() {} + + }).setBaseImageFileName(imageName) + .setFramesToTakeScreenshotsOn(5) + .run(); + } +} diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrainAdvanced.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrainAdvanced.java new file mode 100644 index 0000000000..a1f5830896 --- /dev/null +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrainAdvanced.java @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2024 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.jmonkeyengine.screenshottests.terrain; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; +import com.jme3.app.state.BaseAppState; +import com.jme3.asset.AssetManager; +import com.jme3.asset.TextureKey; +import com.jme3.light.AmbientLight; +import com.jme3.light.DirectionalLight; +import com.jme3.light.LightProbe; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.shader.VarType; +import com.jme3.terrain.geomipmap.TerrainLodControl; +import com.jme3.terrain.geomipmap.TerrainQuad; +import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; +import com.jme3.terrain.heightmap.AbstractHeightMap; +import com.jme3.terrain.heightmap.ImageBasedHeightMap; +import com.jme3.texture.Image; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.Texture.MagFilter; +import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.TextureArray; +import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + + +/** + * This test uses 'AdvancedPBRTerrain.j3md' to create a terrain Material with + * more textures than 'PBRTerrain.j3md' can handle. + * + * Upon running the app, the user should see a mountainous, terrain-based + * landscape with some grassy areas, some snowy areas, and some tiled roads and + * gravel paths weaving between the valleys. Snow should be slightly + * shiny/reflective, and marble texture should be even shinier. If you would + * like to know what each texture is supposed to look like, you can find the + * textures used for this test case located in jme3-testdata. + + * The MetallicRoughness map stores: + *
    + *
  • AmbientOcclusion in the Red channel
  • + *
  • Roughness in the Green channel
  • + *
  • Metallic in the Blue channel
  • + *
  • EmissiveIntensity in the Alpha channel
  • + *
+ * + * The shaders are still subject to the GLSL max limit of 16 textures, however + * each TextureArray counts as a single texture, and each TextureArray can store + * multiple images. For more information on texture arrays see: + * https://www.khronos.org/opengl/wiki/Array_Texture + * + * Uses assets from CC0Textures.com, licensed under CC0 1.0 Universal. For more + * information on the textures this test case uses, view the license.txt file + * located in the jme3-testdata directory where these textures are located: + * jme3-testdata/src/main/resources/Textures/Terrain/PBR + * + * @author yaRnMcDonuts - original test + * @author Richard Tingle (aka richtea) - screenshot test adaptation + */ +@SuppressWarnings("FieldCanBeLocal") +public class TestPBRTerrainAdvanced extends ScreenshotTestBase { + + private static Stream testParameters() { + return Stream.of( + Arguments.of("FinalRender", 0), + Arguments.of("AmbientOcclusion", 4), + Arguments.of("Emissive", 5) + ); + } + + /** + * Test advanced PBR terrain with different debug modes + * + * @param testName The name of the test (used for screenshot filename) + * @param debugMode The debug mode to use + */ + @ParameterizedTest(name = "{0}") + @MethodSource("testParameters") + public void testPBRTerrainAdvanced(String testName, int debugMode, TestInfo testInfo) { + if(!testInfo.getTestClass().isPresent() || !testInfo.getTestMethod().isPresent()) { + throw new RuntimeException("Test preconditions not met"); + } + + String imageName = testInfo.getTestClass().get().getName() + "." + testInfo.getTestMethod().get().getName() + "_" + testName; + + screenshotTest(new BaseAppState() { + private TerrainQuad terrain; + private Material matTerrain; + + private final int terrainSize = 512; + private final int patchSize = 256; + private final float dirtScale = 24; + private final float darkRockScale = 24; + private final float snowScale = 64; + private final float tileRoadScale = 64; + private final float grassScale = 24; + private final float marbleScale = 64; + private final float gravelScale = 64; + + private final ColorRGBA tilesEmissiveColor = new ColorRGBA(0.12f, 0.02f, 0.23f, 0.85f); //dim magenta emission + private final ColorRGBA marbleEmissiveColor = new ColorRGBA(0.0f, 0.0f, 1.0f, 1.0f); //fully saturated blue emission + + @Override + protected void initialize(Application app) { + SimpleApplication simpleApp = (SimpleApplication) app; + AssetManager assetManager = app.getAssetManager(); + + setUpTerrain(simpleApp, assetManager); + setUpTerrainMaterial(assetManager); + setUpLights(simpleApp, assetManager); + setUpCamera(app); + + // Set debug mode + matTerrain.setInt("DebugValuesMode", debugMode); + } + + private void setUpTerrainMaterial(AssetManager assetManager) { + // advanced PBR terrain matdef + matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/AdvancedPBRTerrain.j3md"); + + matTerrain.setBoolean("useTriPlanarMapping", false); + + // ALPHA map (for splat textures) + matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png")); + matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png")); + + // load textures for texture arrays + // These MUST all have the same dimensions and format in order to be put into a texture array. + //ALBEDO MAPS + Texture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + Texture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); + Texture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); + Texture tileRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); + Texture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + Texture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); + Texture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); + + // NORMAL MAPS + Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); + Texture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); + Texture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); + Texture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); + Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); + Texture normalMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Normal.png"); + Texture normalMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); + + //PACKED METALLIC/ROUGHNESS / AMBIENT OCCLUSION / EMISSIVE INTENSITY MAPS + Texture metallicRoughnessAoEiMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_PackedMetallicRoughnessMap.png"); + Texture metallicRoughnessAoEiMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_PackedMetallicRoughnessMap.png"); + Texture metallicRoughnessAoEiMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_PackedMetallicRoughnessMap.png"); + Texture metallicRoughnessAoEiMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel_015_PackedMetallicRoughnessMap.png"); + Texture metallicRoughnessAoEiMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_PackedMetallicRoughnessMap.png"); + Texture metallicRoughnessAoEiMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_PackedMetallicRoughnessMap.png"); + Texture metallicRoughnessAoEiMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_PackedMetallicRoughnessMap.png"); + + // put all images into lists to create texture arrays. + List albedoImages = new ArrayList<>(); + List normalMapImages = new ArrayList<>(); + List metallicRoughnessAoEiMapImages = new ArrayList<>(); + + albedoImages.add(dirt.getImage()); //0 + albedoImages.add(darkRock.getImage()); //1 + albedoImages.add(snow.getImage()); //2 + albedoImages.add(tileRoad.getImage()); //3 + albedoImages.add(grass.getImage()); //4 + albedoImages.add(marble.getImage()); //5 + albedoImages.add(gravel.getImage()); //6 + + normalMapImages.add(normalMapDirt.getImage()); //0 + normalMapImages.add(normalMapDarkRock.getImage()); //1 + normalMapImages.add(normalMapSnow.getImage()); //2 + normalMapImages.add(normalMapRoad.getImage()); //3 + normalMapImages.add(normalMapGrass.getImage()); //4 + normalMapImages.add(normalMapMarble.getImage()); //5 + normalMapImages.add(normalMapGravel.getImage()); //6 + + metallicRoughnessAoEiMapImages.add(metallicRoughnessAoEiMapDirt.getImage()); //0 + metallicRoughnessAoEiMapImages.add(metallicRoughnessAoEiMapDarkRock.getImage()); //1 + metallicRoughnessAoEiMapImages.add(metallicRoughnessAoEiMapSnow.getImage()); //2 + metallicRoughnessAoEiMapImages.add(metallicRoughnessAoEiMapRoad.getImage()); //3 + metallicRoughnessAoEiMapImages.add(metallicRoughnessAoEiMapGrass.getImage()); //4 + metallicRoughnessAoEiMapImages.add(metallicRoughnessAoEiMapMarble.getImage()); //5 + metallicRoughnessAoEiMapImages.add(metallicRoughnessAoEiMapGravel.getImage()); //6 + + //initiate texture arrays + TextureArray albedoTextureArray = new TextureArray(albedoImages); + TextureArray normalParallaxTextureArray = new TextureArray(normalMapImages); // parallax is not used currently + TextureArray metallicRoughnessAoEiTextureArray = new TextureArray(metallicRoughnessAoEiMapImages); + + //apply wrapMode to the whole texture array, rather than each individual texture in the array + setWrapAndMipMaps(albedoTextureArray); + setWrapAndMipMaps(normalParallaxTextureArray); + setWrapAndMipMaps(metallicRoughnessAoEiTextureArray); + + //assign texture array to materials + matTerrain.setParam("AlbedoTextureArray", VarType.TextureArray, albedoTextureArray); + matTerrain.setParam("NormalParallaxTextureArray", VarType.TextureArray, normalParallaxTextureArray); + matTerrain.setParam("MetallicRoughnessAoEiTextureArray", VarType.TextureArray, metallicRoughnessAoEiTextureArray); + + //set up texture slots: + matTerrain.setInt("AlbedoMap_0", 0); // dirt is index 0 in the albedo image list + matTerrain.setFloat("AlbedoMap_0_scale", dirtScale); + matTerrain.setFloat("Roughness_0", 1); + matTerrain.setFloat("Metallic_0", 0.02f); + + matTerrain.setInt("AlbedoMap_1", 1); // darkRock is index 1 in the albedo image list + matTerrain.setFloat("AlbedoMap_1_scale", darkRockScale); + matTerrain.setFloat("Roughness_1", 1); + matTerrain.setFloat("Metallic_1", 0.04f); + + matTerrain.setInt("AlbedoMap_2", 2); + matTerrain.setFloat("AlbedoMap_2_scale", snowScale); + matTerrain.setFloat("Roughness_2", 0.72f); + matTerrain.setFloat("Metallic_2", 0.12f); + + matTerrain.setInt("AlbedoMap_3", 3); + matTerrain.setFloat("AlbedoMap_3_scale", tileRoadScale); + matTerrain.setFloat("Roughness_3", 1); + matTerrain.setFloat("Metallic_3", 0.04f); + + matTerrain.setInt("AlbedoMap_4", 4); + matTerrain.setFloat("AlbedoMap_4_scale", grassScale); + matTerrain.setFloat("Roughness_4", 1); + matTerrain.setFloat("Metallic_4", 0); + + matTerrain.setInt("AlbedoMap_5", 5); + matTerrain.setFloat("AlbedoMap_5_scale", marbleScale); + matTerrain.setFloat("Roughness_5", 1); + matTerrain.setFloat("Metallic_5", 0.2f); + + matTerrain.setInt("AlbedoMap_6", 6); + matTerrain.setFloat("AlbedoMap_6_scale", gravelScale); + matTerrain.setFloat("Roughness_6", 1); + matTerrain.setFloat("Metallic_6", 0.01f); + + // NORMAL MAPS + matTerrain.setInt("NormalMap_0", 0); + matTerrain.setInt("NormalMap_1", 1); + matTerrain.setInt("NormalMap_2", 2); + matTerrain.setInt("NormalMap_3", 3); + matTerrain.setInt("NormalMap_4", 4); + matTerrain.setInt("NormalMap_5", 5); + matTerrain.setInt("NormalMap_6", 6); + + //METALLIC/ROUGHNESS/AO/EI MAPS + matTerrain.setInt("MetallicRoughnessMap_0", 0); + matTerrain.setInt("MetallicRoughnessMap_1", 1); + matTerrain.setInt("MetallicRoughnessMap_2", 2); + matTerrain.setInt("MetallicRoughnessMap_3", 3); + matTerrain.setInt("MetallicRoughnessMap_4", 4); + matTerrain.setInt("MetallicRoughnessMap_5", 5); + matTerrain.setInt("MetallicRoughnessMap_6", 6); + + //EMISSIVE + matTerrain.setColor("EmissiveColor_5", marbleEmissiveColor); + matTerrain.setColor("EmissiveColor_3", tilesEmissiveColor); + + terrain.setMaterial(matTerrain); + } + + private void setWrapAndMipMaps(Texture texture) { + texture.setWrap(WrapMode.Repeat); + texture.setMinFilter(MinFilter.Trilinear); + texture.setMagFilter(MagFilter.Bilinear); + } + + private void setUpTerrain(SimpleApplication simpleApp, AssetManager assetManager) { + // HEIGHTMAP image (for the terrain heightmap) + TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); + Texture heightMapImage = assetManager.loadTexture(hmKey); + + // CREATE HEIGHTMAP + AbstractHeightMap heightmap; + try { + heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.3f); + heightmap.load(); + heightmap.smooth(0.9f, 1); + } catch (Exception e) { + throw new RuntimeException(e); + } + + terrain = new TerrainQuad("terrain", patchSize + 1, terrainSize + 1, heightmap.getHeightMap()); + TerrainLodControl control = new TerrainLodControl(terrain, getApplication().getCamera()); + control.setLodCalculator(new DistanceLodCalculator(patchSize + 1, 2.7f)); // patch size, and a multiplier + terrain.addControl(control); + terrain.setMaterial(matTerrain); + terrain.setLocalTranslation(0, -100, 0); + terrain.setLocalScale(1f, 1f, 1f); + simpleApp.getRootNode().attachChild(terrain); + } + + private void setUpLights(SimpleApplication simpleApp, AssetManager assetManager) { + LightProbe probe = (LightProbe) assetManager.loadAsset("Scenes/LightProbes/quarry_Probe.j3o"); + + probe.setAreaType(LightProbe.AreaType.Spherical); + probe.getArea().setRadius(2000); + probe.getArea().setCenter(new Vector3f(0, 0, 0)); + simpleApp.getRootNode().addLight(probe); + + DirectionalLight directionalLight = new DirectionalLight(); + directionalLight.setDirection((new Vector3f(-0.3f, -0.5f, -0.3f)).normalize()); + directionalLight.setColor(ColorRGBA.White); + simpleApp.getRootNode().addLight(directionalLight); + + AmbientLight ambientLight = new AmbientLight(); + ambientLight.setColor(ColorRGBA.White); + simpleApp.getRootNode().addLight(ambientLight); + } + + private void setUpCamera(Application app) { + app.getCamera().setLocation(new Vector3f(0, 10, -10)); + app.getCamera().lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y); + } + + @Override + protected void cleanup(Application app) {} + + @Override + protected void onEnable() {} + + @Override + protected void onDisable() {} + + }).setBaseImageFileName(imageName) + .setFramesToTakeScreenshotsOn(5) + .run(); + } +} \ No newline at end of file diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_DefaultDirectionalLight_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_DefaultDirectionalLight_f12.png new file mode 100644 index 0000000000..44e4e09453 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_DefaultDirectionalLight_f12.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_HighRoughness_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_HighRoughness_f12.png new file mode 100644 index 0000000000..2e53fc1bce Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_HighRoughness_f12.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_LowRoughness_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_LowRoughness_f12.png new file mode 100644 index 0000000000..da5d37d643 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_LowRoughness_f12.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_UpdatedDirectionalLight_f12.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_UpdatedDirectionalLight_f12.png new file mode 100644 index 0000000000..f3fcc3495d Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRLighting.testPBRLighting_UpdatedDirectionalLight_f12.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithRealtimeBaking_f10.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithRealtimeBaking_f10.png new file mode 100644 index 0000000000..1a3a738bc9 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithRealtimeBaking_f10.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithoutRealtimeBaking_f10.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithoutRealtimeBaking_f10.png new file mode 100644 index 0000000000..1a3a738bc9 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.light.pbr.TestPBRSimple.testPBRSimple_WithoutRealtimeBaking_f10.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_FinalRender_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_FinalRender_f5.png new file mode 100644 index 0000000000..aa8c62dcd4 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_FinalRender_f5.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_GeometryNormals_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_GeometryNormals_f5.png new file mode 100644 index 0000000000..215274f23d Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_GeometryNormals_f5.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_MetallicMap_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_MetallicMap_f5.png new file mode 100644 index 0000000000..dbbe6cf3a5 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_MetallicMap_f5.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_NormalMap_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_NormalMap_f5.png new file mode 100644 index 0000000000..791339d4c3 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_NormalMap_f5.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_RoughnessMap_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_RoughnessMap_f5.png new file mode 100644 index 0000000000..167ec2eeb7 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrain.testPBRTerrain_RoughnessMap_f5.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_AmbientOcclusion_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_AmbientOcclusion_f5.png new file mode 100644 index 0000000000..5c515e1e26 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_AmbientOcclusion_f5.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_Emissive_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_Emissive_f5.png new file mode 100644 index 0000000000..f1304956c3 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_Emissive_f5.png differ diff --git a/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_FinalRender_f5.png b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_FinalRender_f5.png new file mode 100644 index 0000000000..aa8c62dcd4 Binary files /dev/null and b/jme3-screenshot-tests/src/test/resources/org.jmonkeyengine.screenshottests.terrain.TestPBRTerrainAdvanced.testPBRTerrainAdvanced_FinalRender_f5.png differ