Pregunta

I am trying write some lighting code for a Java2D isometric game I am writing - I have found a few algorithms I want to try implementing - one of which I found here:

here

The problem is that this sort of algorithm would require some optimal pixel-shading effect that I haven't found a way of achieving via Java2D. Preferably some method via the graphics hardware but if that isn't possible - at least a method of achieving the same effect quickly in software.

If that isn't possible, could someone direct me to a more optimal algorithm with Java2D in mind? I have considered per-tile lighting - however I find the drawPolygon method isn't hardware accelerated and thus performs very slowly.

I want to try and avoid native dependencies or the requirement for elevated permissions in an applet.

Thanks

¿Fue útil?

Solución

I did a lot of research since I posted this question - there are tons of alternatives and JavaFX does intend (on a later release) to include its own shader language for those interested. There is also a ofcourse LWJGL that will allow you to load your own shaders onto the GPU.

However, if you're stuck in Java2D (as I am) it is still possible to implement lighting in an isometric game it is just 'awkward' because you cannot perform the light shading on a per-pixel level.

How it Looks:

I have achieved a (highly unpolished - after some polishing I can assure you it will look great) effect for casting shadows, depth sorting the light map, and applying the lighting without experiencing a drop in frame-rate. Here is how it looks:

casting shadows and depth sorting the light map

You'll see in this screen-shot a diffuse light (not shaded in but that step I'd say is relatively easy in contrast to the steps to get there) casting shadows - the areas behind the entities that obstructs the light's passage BUT also in the bounds of the light's maximum fall-out is shaded in as the ambient lighting but in reality this area is passed to the lights rendering routine to factor in the amount of obstruction that has occurred so that the light can apply a prettier gradient (or fading effect of some sort.)

The current implementation of the diffuse lighting is to just simply render obstructed regions the ambient colour and render non-obstructed regions the light's colour - obviously though you'd apply a fading effect as you got further from the light (that part of the implementation I haven't done yet - but as I said it is relatively easy.)

How I did it: I don't guarantee this is the most optimal method, but for those interested:

Essentially, this effect is achieved by using a lot of Java shape operations - the rendering of the light map is accelerated by using a VolatileImage.

When the light map is being generated, the render routine does the following:

  1. Creates an Area object that contains a Rectangle that covers the entirety of the screen. This area will contain your ambient lighting.

  2. It then iterates through the lights asking them what their light-casting Area would be if there were no obstructions in the way.

  3. It takes this area object and searches the world for Actors\Tiles that are contained within that area that the light would be cast in.

  4. For every tile that it finds that obstructs view in the light's casting area, it will calculate the difference in the light source's position and the obstruction's position (essentially creating a vector that points AT the obstruction from the light source - this is the direction you want to cast your shadow) This pointing vector (in world space) needs to be translated to screen space.

  5. Once that has been done, a perpendicular to that vector is taken and normalized. This essentially gives you a line you can travel up or down on by multiplying it by any given length to travel the given direction in. This vector is perpendicular to the direction you want to cast your shadow over.

  6. Almost done, you consturct a polygon that consists of four points. The first two points are at the the base of the screen coordinate of your obstruction's center point. To get the first point, you want to travel up your perpendicular vector (calculated in 5) a quantity of half your tile's height [ this is a relatively accurate approximation though I think this part of the algorithm is slightly incorrect - but it has no noticable decay on the visual effect] - then ofcourse add to that the obstructions origin. To get the second, you do the same but instead travel down.

  7. The remainder of the two points are calculated exactly the same way - only these points need to be projected outward in the direction of your shadow's projection vector calculated in 4. - You can choose any large amount to project it outwards by - just as long as it reaches at least outside of you light's casting area (so if you just want to do it stupidly multiply your shadow projection vector by a factor of 10 and you should be safe)

  8. From this polygon you just constructed, construct an area, and then invoke the "intersect" method with your light's area as the first argument - this will assure that your shadows area doesn't reach outside of the bounds of the area that your light casts over.

  9. Subtract from your light's casting the shadow area you constructed above. At this point you now have two areas - the area where the light casts unobstructed, and the area the light casts over obstructed - if your Actors have a visibility obstruction factor that you used to determine that a particular actor was obstructing view - you also have the grade at which it obstructs the view that you can apply later when you are drawing in the light effect (this will allow you to chose between a darker\brighter shade depending on how much light is being obstructed

  10. Subtract from your ambient light area you constructed in (1) both the light area, and the obstructed light area so you don't apply the ambient light to areas where the lighting effect will take over and render into

Now you need to merge your light map with your depth-buffered world's render routine

Now that you've rendered you're light map and it is contained inside of a volatile image, you need to throw it into your world's render routine and depth-sorting algorithm. Since the back-buffer and the light map are both volatileimages, rendering the light map over the world is relatively optimal.

You need to construct a polygon that is essentially a strip that contains what a vertical strip of your world tiles would be rendered into (look at my screen shot, you'll see an array of thin diagonal lines seperating these strips. These strips are what I am referring). You can than render parts of this light map strip by strip (render it over the strip after you've rendered the last tile in that strip since - obviously - the light map has to be applied over the map). You can use the same image-map just use that strip as a clip for Graphics - you will need to translate that strip polygon down per render of a strip.

Anyway, like I said I don't guarantee this is the most optimal way - but so far it is working fine for me. The light map is applied p

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top