Efficient 2D tile lighting system

What is the most efficient way to make lighting for a tile based device in Java?
Will it put a black background behind the tiles and change the alpha tiles?
Or put a black plan and change alpha? Or something else?

This is an example of what kind of lighting I want:
http://i.stack.imgur.com/F5Lzo.png

+7
java 2d tile lighting
source share
3 answers

There are many ways to achieve this. Take some time before making a final decision. I will briefly summarize some of the techniques you could use, and ultimately provide some code.


Hard lighting

If you want to create a strong lighting effect (for example, the image of your example) several approaches come to my mind:

hard light example

Fast and dirty (as you said)

  • Use black background
  • Set the alpha values ​​of the tile according to their darkness value

The problem is that you cannot make the tile brighter than before (emphasizes) and do not change the background color. Both of these aspects that usually make lighting in games look good.

The second set of tiles

  • Use a second set of (black / color) tiles
  • Lay them on top of the main plates.
  • Set a new alpha tile depending on how strong the new color should be.

This approach has the same effect as the first, with the advantage that now you can paint overlay tiles of a different color than black, which allows you to use both colored lights and make highlights.

Example: hard light with black tiles example

Although this is easy, the problem is that it is really very inefficient. (Two processed tiles per tile, constant repainting, many rendering operations, etc.)


More effective approaches (hard and / or soft lighting)

When you look at your example, I believe that light always comes from a specific source tile (symbol, torch, etc.).

  • For each type of light (large torch, small flashlight, character lighting), you create an image representing the specific behavior of the lighting relative to the original tile (light mask). Maybe something similar for a torch (white alpha):

centered light mask

  • For each tile that is a light source, you represent this image at the source position as an overlay.
  • To add a little light color, you can use for example. 10% opaque orange instead of full alpha.

results

image mask hard light result

Adding Soft Light

Soft light doesn't matter much now, just use more details in a light mask than plates. Using only 15% alpha in the usually black area, you can add a low vision effect when the tile is off:

soft light

You can easily achieve more complex forms of lighting (cones, etc.) by simply changing the image of the mask.

Multiple light sources

When combining several light sources, this approach leads to a problem: Drawing two masks that intersect with each other can cancel:

mask cancellation

We want them to add their lights instead of subtracting them. Avoid the problem:

  • Invert all light masks (alpha - dark areas, opaque - light)
  • Mark all these light masks in a temporary image that has the same dimensions as the viewing area.
  • Invert and render a new image (as if it were the only light mask) across the landscape.

This will lead to something like this: result image

Code for mask inversion method

Assuming that you first transfer all the tiles to BufferedImage I will give a few instructions that resemble the last method shown (only grayscale support).

Several light masks, for example. torch and player can be combined as follows:

 public BufferedImage combineMasks(BufferedImage[] images) { // create the new image, canvas size is the max. of all image sizes int w, h; for (BufferedImage img : images) { w = img.getWidth() > w ? img.getWidth() : w; h = img.getHeight() > h ? img.getHeight() : h; } BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // paint all images, preserving the alpha channels Graphics g = combined.getGraphics(); for (BufferedImage img : images) g.drawImage(img, 0, 0, null); return combined; } 

The final mask is created and applied using this method:

 public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask) { int width = image.getWidth(); int height = image.getHeight(); int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width); int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width); for (int i = 0; i < imagePixels.length; i++) { int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha // get alpha from color int // be careful, an alpha mask works the other way round, so we have to subtract this from 255 int alpha = (maskPixels[i] >> 24) & 0xff; imagePixels[i] = color | alpha; } image.setRGB(0, 0, width, height, imagePixels, 0, width); } 

As already noted, this is a primitive example. Implementing color mixing can be a little more efficient.

+19
source share

Raytracing may be the easiest approach.

  • you can save which tiles have been noticed (used for autopilot, used to "remember your card when dazzling," perhaps for a mini-map, etc.).
  • you only show what you see - maybe a monster from a wall or a hill blocks your view, and then raytracing at that moment.
  • distant "luminous objects" or other light sources (lava torches) can be seen even if your own light source does not reach a very large distance.
  • the length of your rays will be used to check the amount of light (fading light).
  • Perhaps you have a special sensor (ESP, gold / food detection) that will be used to search for objects that are not in your opinion? raytrace can also help ^^

How to do it easily?

  • draw a line from your player to every point on the border of your map (using the Breshamham algorithm http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm, follow this line (from your character to the end) until your view is blocked; on At this point, stop your search (or perhaps do the last last iteration to find out what you really liked).
  • for each point on your line set lighning (maybe 100% for distance 1, 70% for distance 2, etc.) and mark the map as shown on the page

Maybe you won’t go all over the map, maybe it’s enough if you set raytrace to view 20x20? NOTE: you really need to go around the borders of the viewport, it is NOT required to track each point.

I am adding a linear algorithm to simplify your work:

 public static ArrayList<Point> getLine(Point start, Point target) { ArrayList<Point> ret = new ArrayList<Point>(); int x0 = start.x; int y0 = start.y; int x1 = target.x; int y1 = target.y; int sx = 0; int sy = 0; int dx = Math.abs(x1-x0); sx = x0<x1 ? 1 : -1; int dy = -1*Math.abs(y1-y0); sy = y0<y1 ? 1 : -1; int err = dx+dy, e2; /* error value e_xy */ for(;;){ /* loop */ ret.add( new Point(x0,y0) ); if (x0==x1 && y0==y1) break; e2 = 2*err; if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */ if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */ } return ret; } 

I did all this zipper stuff a while ago, * pathfindin feel free to ask additional questions

Appendum: maybe I can just add small algorithms for raytracing ^^

to get the North and South border points, just use this snippet:

 for (int x = 0; x <map.WIDTH; x++){ Point northBorderPoint = new Point(x,0); Point southBorderPoint = new Point(x,map.HEIGHT); rayTrace( getLine(player.getPos(), northBorderPoint), player.getLightRadius()) ); rayTrace( getLine(player.getPos(), southBorderPoint, player.getLightRadius()) ); } 

and raytrace works as follows:

 private static void rayTrace(ArrayList<Point> line, WorldMap map, int radius) { //int radius = radius from light source for (Point p: line){ boolean doContinue = true; float d = distance(line.get(0), p); //caclulate light linear 100%...0% float amountLight = (radius - d) / radius; if (amountLight < 0 ){ amountLight = 0; } map.setLight( p, amountLight ); if ( ! map.isViewBlocked(p) ){ //can be blockeb dy wall, or monster doContinue = false; break; } } } 
+9
source share

I have been developing an indie game for about three years now. The way I do it is, first of all, using OpenGL so that you can get all the benefits of the GPU's graphics processing power (I hope you already do this). Suppose we start with all the fragments in a VBO that are fully lit. Now there are several options for achieving the desired. Depending on how complex your lighting system is, you may choose a different approach.

  • If your light is circular around the player, regardless of whether obstacles block the light in real life, you can choose the lighting algorithm implemented in the vertex shader. In the vertex shader, you can calculate the distance to the top of the player and apply some function that determines how bright things should be depending on the calculated distance. Do not use alpha, but simply increase the color of the texture / tile by the lighting value.

  • If you want to use a custom lightmap (which is more likely), I would suggest adding an additional vertex attribute that indicates the brightness of the tile. Update VBO if necessary. The same approach goes here: multiply the texture pixel by the light value. If you return the light recursively from the player’s position as a starting point, you will update the VBO every time the player moves.

  • If your lightmap depends on where the sunlight reaches your level, you can combine two types of lighting methods. Create one vertex attribute for the brightness of the sun and another vertex attribute for the light emitted by the bright points (for example, a torch held by a player). Now you can combine these two values ​​in the vertex shader. Suppose your sun rises and falls, like a day and night pattern. Let's say the brightness of the sun is sun , which is a value from 0 to 1. This value can be passed to the vertex shader as uniform. The vertex attribute representing the brightness of the Sun is s , and the light emitted by light points is l . Then you can calculate the total light for this tile as follows:

     tileBrightness = max(s * sun, l + flicker); 

    Where the flicker (also the shape of the vertex shader) is some kind of wavy function that presents small brightness options for your light points.
    This approach makes the scene dynamic without having to constantly recreate VBO. I implemented this approach in a draft evidence-based concept. It works great. You can check how it looks here: http://www.youtube.com/watch?v=jTcNitp_IIo . Notice how the torch flickers at 0:40 in the video. This is done by what I have explained here.

+1
source share

All Articles