LibGDX Lighting – Day / Night cycle

Recently researched the use of a Shader in libGDX to create a simple day/night cycle. Found some basic tutorials to create a fire at night affect including: Lightmap Shader This basically adds a tint to the background and overlays an image to create a spotlight.

Initiating vars etc:

Vector3 bright = new Vector3(6.3f, 6.3f, 6.7f);
// Load shaders from text files
vertexShader = Gdx.files.internal("data/shaders/vertexShader.glsl").readString();
defaultPixelShader = Gdx.files.internal("data/shaders/defaultPixelShader.glsl").readString();
finalPixelShader =  Gdx.files.internal("data/shaders/pixelShader.glsl").readString();

ShaderProgram.pedantic = false;
defaultShader = new ShaderProgram(Art.vertexShader, Art.defaultPixelShader);
finalShader = new ShaderProgram(Art.vertexShader, Art.finalPixelShader);currentShader = finalShader;
ambientColor = bright;
 
finalShader.begin();
finalShader.setUniformi("u_lightmap", 1);
finalShader.setUniformf("ambientColor", ambientColor.x, ambientColor.y, ambientColor.z, ambientIntensity);
finalShader.end();
// Image for spot light 
light = new Texture(Gdx.files.internal("data/shaders/light.png"));
fbo = new FrameBuffer(Format.RGBA8888, 1024, 788, false);

finalShader.begin();
finalShader.setUniformf("resolution", 1024, 788);
finalShader.end();

Render Loop
After setting up the variables and shader files in the render loop I adjusted the ambientColor over time to give affect of night day.

// Adjust ambientColor to give appearance of night/day  
finalShader.begin();
finalShader.setUniformi("u_lightmap", 1);
finalShader.setUniformf("ambientColor", ambientColor.x, ambientColor.y, ambientColor.z, ambientIntensity);
finalShader.end();

// I added logic to only show spotlight during night
if(show_light){
  fbo.begin();
  fbo.getColorBufferTexture().bind(1);
  light_batch.setShader(defaultShader);
  light_batch.begin();
  light.bind(0);
  light_batch.draw(light, hero.x-l_off+.4f,hero.y-l_off+.2f, light_size,light_size);
  light_batch.end();
  fbo.end();
}
       
// Draw your map here
light_batch.setShader(currentShader);
light_batch.begin();
MapGenerator.draw(gameController.camera, light_batch);
light_batch.end(); 

Night

 Evening
This solution limited me to using one spot light, also my knowledge of OpenGL Shader language is zero. At this point I went back to the LibGDX documentation and came across Box2dLights, a library that uses collision data from Box2D to create lights/shadows.
// BOX2DLIGHTS
private PointLight spriteLight;
private RayHandler rayHandler;

// Create a new Box2D World, this is required. 
World world = new World(new Vector2(), true); 
RayHandler.useDiffuseLight(true);
  
// Setup the new RayHandler, it will use the same camera as the main game
rayHandler = new RayHandler(world);
rayHandler.setCulling(true);
rayHandler.setCombinedMatrix(gameController.camera.combined);
rayHandler.setAmbientLight(1);
 
// Light to follow the hero
spriteLight = new PointLight(rayHandler, 50);
spriteLight.setPosition(hero.x,hero.y);
spriteLight.setDistance(5);
spriteLight.setColor(3, 12, 33, 0.3f);
// Keep the unique ID of the light  
hero_light = spriteLight.hashCode();

// this lights the map (Sun)  
spriteLight = new PointLight(rayHandler, 50);
spriteLight.setPosition(hero.x-20,hero.y-20);
spriteLight.setDistance(250);
spriteLight.setColor(3, 12, 33, 0.5f);
 
// Test shadow being cast by solid object 
PolygonShape tileShape = new PolygonShape();
tileShape.setAsBox(.5f, .5f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = tileShape;
fixtureDef.filter.groupIndex = 0;
BodyDef tileBodyDef = new BodyDef();
float bodyX = hero.x-3;
float bodyY = hero.y-4f;
tileBodyDef.position.set(bodyX, bodyY); 
tileBodyDef.type = BodyType.StaticBody;
Body tileBody = world.createBody(tileBodyDef);
tileBody.createFixture(fixtureDef);

// RENDER LOOP CODE
rayHandler.setCombinedMatrix(gameController.camera.combined, gameController.camera.position.x, gameController.camera.position.y,gameController.camera.viewportWidth, gameController.camera.viewportHeight);
rayHandler.updateAndRender();

Here I set up some basic lights, a small one for the hero and a large light to act as the sun, I added one solid object to show a shadow being cast. Would need to add bodies for each object in the game and update there positions. Here the alpha is set to low on the sun light to give impression of night:

Zoomed out, earlier in the day:
Multiple Lights

Accessing and looping through all lights is simple, we stored the hashcode of the hero light so on each render loop update its position, else change the alpha value; increase/decrease this value to simulate night to day and vice versa.

for (Light light : rayHandler.lightList) {
 if (light.hashCode() == hero_light){
  light.setPosition(hero.x,hero.y); 
 }else{
  light.setColor(1, 1, 1, ambientIntensity);
 }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s