# 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 (intensity) of the light – normally, it is white (ColorRGBA(1,1,1,1)). 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,13, -1,13, 1,13).normalizeLocal());
    rootNode.addLight(sun);

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

    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, or cone of light. A SpotLight has a direction, a position, distance (range) and an angle. The inner angle is the central maximum of the light cone, the outer angle the edge of the light cone. Everything outside the light cone's angles is not affacted by the light.

    SpotLight sl = new SpotLight();
    sl.setDirection(new Vector3f(1,0,1));  // direction
    sl.setPosition(new Vector3f(0,0,0));
    sl.setSpotInnerAngle(15f); // inner light cone (maximum)
    sl.setSpotOuterAngle(35f); // outer light cone (edge of the light)
    sl.setSpotRange(10f);      // distance
    sl.setColor(ColorRGBA.White.mult(1.3f));
    rootNode.addLight(sl);

## LightControl

You can use a com.jme3.scene.control.LightControl to make a SpotLight or PointLight follow a Spatial.

            PointLight myLight = new PointLight();
            rootNode.addLight(myLight);
            LightControl lightControl = new LightControl(myLight);
            spatial.addControl(lightControl);

## 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, 3);
        pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
        viewPort.addProcessor(pssmRenderer);

The constructor expects the following values:

- Your assetManager object
- The size of the rendered shadowmaps (512, 1024, 2048, etc…)
- The number of shadow maps rendered (the more shadow maps, the more quality, the less FPS).

You can set the following properties on the `pssmRenderer` object:

- setDirection(Vector3f) – the direction of the light
- setLambda(0.65f) – Factor to use to reduce the split size
- setShadowIntensity(0.7f) – shadow darkness (1 black, 0 invisible)
- setShadowZextend() – distance how far away from camera shadows will still be computed

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

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

view online version