# Light and Shadow

Light and Shadow are two separate things in 3D engines, although we percieve them together in real life:

- Lighting means that an object is brighter on the side facing the light direction, and darker on the backside. Computationally, this is relatively easy.
- Lighting does not mean that objects cast a shadow on the floor or other objects: Activating shadow processing is an additional step described here. Since casting shadows has an impact on performance, drop shadows and ambient occlusion shading are not activated by default.

A light source with a direction or location is required for all Geometries with Lighting.j3md-based Materials. An ambient light is not sufficient. In a scene with no appropriate light sources, Geometries with Lighting.j3md-based Materials do not render. Only Geometries with Unshaded.j3md-based Materials are visible independent of any light sources.

## Light Sources and Colors

You can add several types of light sources to a scene using `rootNode.addLight(mylight)`.

The available light sources in `com.???jme3.???light` are:

- SpotLight
- PointLight
- AmbientLight
- DirectionalLight

You control the color and intensity of each light source. Typically you set the color to white (`new ColorRGBA(1.0f,1.0f,1.0f,1.0f)` or `ColorRGBA.White`), which makes all scene elements appear in their natural color.

You can choose to use lights in other colors than white, or darker colors. This influences the scene's atmosphere and will make the scene appear colder (e.g. `ColorRGBA.Cyan`) or warmer (`ColorRGBA.Yellow`), brighter (higher values) or darker (lower values).

You can get a list of all lights added to a Spatial by calling `getWorldLightList()` (includes inherited lights) or `getLocalLightList()` (only directly added lights), and iterating over the result.

### PointLight

A PointLight has a location and shines from there in all directions as far as its radius reaches. The light intensity decreases with increased distance from the light source. A PointLight can at the moment not be used for casting shadows (using the PssmShadowRenderer - read more about this below).

Typical example: Lamp, lightbulb, torch, candle.

    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);

### DirectionalLight

A DirectionalLight has no position, only a direction. It sends out parallel beams of light and is considered "infinitely" far away. You typically have one directional light per scene. A DirectionalLight can be used together with shadows.

Typically example: Sun light.

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

### SpotLight

A SpotLight sends out a distinct beam or cone of light. A SpotLight has a direction, a position, distance (range) and two angles. 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 affected by the light.

Typical Example: Flashlight

    SpotLight spot = new SpotLight();
    spot.setSpotRange(100f);                           // distance
    spot.setSpotInnerAngle(15f * FastMath.DEG_TO_RAD); // inner light cone (central beam)
    spot.setSpotOuterAngle(35f * FastMath.DEG_TO_RAD); // outer light cone (edge of the light)
    spot.setColor(ColorRGBA.White.mult(1.3f));         // light color
    spot.setPosition(cam.getLocation());               // shine from camera loc
    spot.setDirection(cam.getDirection());             // shine forward from camera loc
    rootNode.addLight(spot);

If you want the spotlight to follow the flycam, repeat the setDirection(???) and setPosition(???) calls in the update loop, and kee syncing them with the camera position and direction.

### AmbientLight

An AmbientLight simply influences the brightness and color of the scene globally. It has no direction and no location and shines equally everywhere. An AmbientLight does not cast any shadows, and it lights all sides of Geometries evenly, which makes 3D objects look unnaturally flat; this is why you typically do not use an AmbientLight alone without one of the other lights.

Typical example: Regulate overall brightness, tinge the whole scene in a warm or cold color.

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

You can increase the brightness of a light source gradually by multiplying the light color to values greater than 1.0f.
Example: `mylight.setColor(ColorRGBA.White.mult(1.3f));`

## Light Follows Spatial

You can use a `com.jme3.scene.control.LightControl` to make a SpotLight or PointLight follow a Spatial. This can be used for a flashlight being carried by a character, or for car headlights, or an aircraft's spotlight, etc.

    PointLight myLight = new PointLight();
    rootNode.addLight(myLight);
    LightControl lightControl = new LightControl(myLight);
    spatial.addControl(lightControl); // this spatial controls the position of this light.

Obviously, this does apply to AmbientLights which have no position.

## Simple Lighting

Full sample code:

For Simple Lighting we use Geometries with Materials based on Lighting.j3md (learn more about Materials here). Lighting.j3md-based materials dynamically support Shininess, and Ambient, Diffuse, and Specular light if there is a light source present. Note that this lighting method alone does not make the Geometry cast a shadow onto other Geometries automatically (see below for how to add drop shadows etc).

    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.Orange);
    mat.setColor("m_Diffuse",  ColorRGBA.Orange);
    mat.setColor("m_Specular", ColorRGBA.White);
    mat.setFloat("m_Shininess", 12);
    rootNode.attachChild(teapot);

The above example uses material colors and no textures. You can of course also use Lighting.j3md to create a lit Material that uses Texture Maps. The following example uses Shininess, Diffuse Map and Normal Map (a.k.a Bump Map).

    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",                 // surface color
        assetManager.loadTexture("Textures/Terrain/Pond/Pond.png"));
    mat_lit.setTexture("m_NormalMap",                  // surface bumps
        assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
    mat_lit.setFloat("m_Shininess", 5f);               // surface smoothness [1,128]
    shiny_rock.setMaterial(mat_lit);
    rootNode.attachChild(shiny_rock);

These light effects update live when the object or light source moves. If you shine a colored PointLight at this object, you will see a light reflection tinged in the color of the PointLight.

## BasicShadowRenderer

Full code sample

Use the Shadow Renderer to make Geometries with Lighting.j3md-based Materials cast and receive basic drop shadows. This fast and simple implementation of a shadow effect is good for scenes with flat floors, but looks less realistic on uneven terrains. To use it, you add a jME SceneProcessor named com.jme3.shadow.BasicShadowRenderer to the viewPort.

    BasicShadowRenderer bsr;
    ...
    public void simpleInitApp() {
        ...
        bsr = new BasicShadowRenderer(assetManager, 256);
        bsr.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal()); // light direction
        viewPort.addProcessor(bsr);
        ...

Shadow calculations (cast and receive) have a performance impact, therefor we recommend to use them smartly. Switch off the default shadow mode for the whole scene graph, and then specify the shadow behaviour individually for every scene node that needs shadows: You specifiy whether it casts shadows, receives shadows, both (slower), or neither (faster).

    rootNode.setShadowMode(ShadowMode.Off);        // reset all
    wall.setShadowMode(ShadowMode.CastAndReceive); // normal behaviour (slow)
    floor.setShadowMode(ShadowMode.Receive);       // can't see shadow cast below floor anyway...
    airplane.setShadowMode(ShadowMode.Cast);       // nothing casts shadows onto airplane anyway...
    ghost.setShadowMode(ShadowMode.Off);           // ghost is translucent anyway...

## Parallel-Split Shadow Map

Full sample code

The more advanced PSSM shadow renderer can cast real-time shadows, even on curved surfaces such as terrains. It is a bit more resource hungry than the BasicShadowRenderer. To activate PSSM drop shadows, add a jME SceneProcessor named `com.jme3.shadow.PssmShadowRenderer` to the viewPort. PSSM stands for the Parallel-Split Shadow Map technique.

    private PssmShadowRenderer pssmRenderer;
    ...
    public void simpleInitApp() {
        ....
        pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
        pssmRenderer.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal()); // light direction
        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 said above, it's more efficient to specify individual shadow behaviour for each Geometry.

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

## Screen Space Ambient Occlusion

Full sample code

- ,
  ??? Screen-Space Ambient Occlusion shadows
- ??? Screen-Space Ambient Occlusion shadows plus transparancy

Ambient Occlusion refers to the shadows that nearby objects cast on each other under an ambient lighting. It???s an approximation of how light radiates in a real life scene. To activate Ambient Occlusion shadows, add a jME SceneProcessor named `com.jme3.post.SSAOFilter` to the viewPort. SSAO stands for the Screen Space Ambient Occlusion technique.

    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
    SSAOFilter ssaoFilter = new SSAOFilter(12.94f, 43.92f, 0.33f, 0.61f);
    fpp.addFilter(ssaoFilter);
    viewPort.addProcessor(fpp);

view online version