Light and Shadow

Lighting means that an object is brighter on the side facing the light direction, and darker on the backside. A light source with a direction or location is required for lit Materials to be visible. Lighting does not automatically mean that objects cast a shadow on the floor or other objects: Activating shadow processing is an extra step described below.

Light Sources

You can add several light sources to a scene using rootNode.addLight(). All Lighting.j3md- based Materials require a light source to be visible. The available light sources in com.​jme3.​light are SpotLight, PointLight, AmbientLight, and DirectionalLight. You can set the color of the light – normally, it is white. You can choose to set other colors to influence the scene's atmosphere. A PointLight has a location and shines from there in all directions as far as its radius reaches, like a lamp. The light intensity decreases with increased distance from the light source.

PointLight lamp_light = new PointLight();
lamp_light.setColor(ColorRGBA.Yellow);
lamp_light.setRadius(4f);
lamp_light.setPosition(new Vector3f(lamp_geo.getLocalTranslation()));
rootNode.addLight(lamp_light);

A DirectionalLight has no position, only a direction. It is considered "infinitely" far away and sends out parallel beams of light. It can cast shadows. You typically use it to simulate sun light:

DirectionalLight sun = new DirectionalLight();
sun.setColor(ColorRGBA.White);
sun.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
rootNode.addLight(sun);

An AmbientLight influences the brightness of the whole scene globally. It has no direction and no location, and does not cast any shadow.

AmbientLight al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(1.3f));
rootNode.addLight(al);

A SpotLight is like a flashlight that sends a distinct beam of light. (Still work in progress, as of alpha-3.)

Simple Lighting

Here we use a material based on Lighting.j3md (More info about Materials). Lighting.j3md-based materials dynamically support Shininess, and Ambient, Diffuse, and Specular Colors.

Geometry teapot = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
TangentBinormalGenerator.generate(teapot.getMesh(), true);
Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
mat.setBoolean("m_UseMaterialColors", true);
mat.setColor("m_Ambient",  ColorRGBA.Black);
mat.setColor("m_Diffuse",  ColorRGBA.Blue);
mat.setColor("m_Specular", ColorRGBA.White);
mat.setFloat("m_Shininess", 12);
rootNode.attachChild(teapot);

In this example, we use material colors instead of textures. But you can equally well use Lighting.j3md to create a Material that uses texture maps, such as the Diffuse and Normal map used here, but also Specular and Paralax Maps:

    Sphere rock = new Sphere(32,32, 2f);
    Geometry shiny_rock = new Geometry("Shiny rock", rock);
    rock.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
    TangentBinormalGenerator.generate(rock);           // for lighting effect
    Material mat_lit = new Material(
        assetManager, "Common/MatDefs/Light/Lighting.j3md");
    mat_lit.setTexture("m_DiffuseMap",
        assetManager.loadTexture("Textures/Terrain/Pond/Pond.png"));
    mat_lit.setTexture("m_NormalMap",
        assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
    mat_lit.setFloat("m_Shininess", 5f); // [0,128]
    shiny_rock.setMaterial(mat_lit);
    rootNode.attachChild(shiny_rock);

This lighting updates live when the object or light source moves. If you shine a colored PointLight at this object, you will see a light reflection in the color of the PointLight. This lighting method doesn't make the node cast a shadow onto other nodes.

BasicShadowRenderer

Use the Shadow Renderer to make textured scene nodes cast and receive shadows. Switch off the default shadow mode, and add a jME SceneProcessor named com.jme3.shadow.BasicShadowRenderer to the viewPort.

BasicShadowRenderer bsr;
...
public void simpleInitApp() {
    ...
    rootNode.setShadowMode(ShadowMode.Off);
    bsr = new BasicShadowRenderer(assetManager, 256);
    bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
    viewPort.addProcessor(bsr);
    ...

For every scene node that needs shadows, individually specify the shadow behaviour: Whether it cast shadows, receive shadows, both, or neither.

wall.setShadowMode(ShadowMode.CastAndReceive);
...
floor.setShadowMode(ShadowMode.Receive);
...
airplane.setShadowMode(ShadowMode.Cast);
...
ghost.setShadowMode(ShadowMode.Off);
...

Parallel-Split Shadow Map

The PSSM shadow renderer can cast real-time shadows on curved surfaces. To activate it, add a jME SceneProcessor named com.jme3.shadow.PssmShadowRenderer to the viewPort.

private PssmShadowRenderer pssmRenderer;
...
public void simpleInitApp() {
    ....
    pssmRenderer = new PssmShadowRenderer(
        assetManager,1024,4,PssmShadowRenderer.EDGE_FILTERING_PCF);
    pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
    viewPort.addProcessor(pssmRenderer);

The constructor expects the following values:

You can set the following properties on the pssmRenderer object:

As usual, specify the shadow behaviour for every scene node.

...
teapot.setShadowMode(ShadowMode.CastAndReceive);
...
soil.setShadowMode(ShadowMode.Receive);
...

view online version