Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (109)
games submitted by our members
Games in WIP (536)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Realtime Lighting Example in java2D  (Read 2798 times)
0 Members and 1 Guest are viewing this topic.
Offline StumpyStrust
« Posted 2013-08-08 04:06:03 »

So I have posted things about lighting in java2D here before but now I am going to give you a nice example and how my library for it works so you could make your own java2D lighting system.

Waning: If you wanted to do any REAL dynamic lighting, for the love of God do not use java2D. I just really like smashing myself in the face. I mean I really like it.

Here is a the download.
http://www.mediafire.com/?764jz882juodw98

Controls:
A and S to change ambient lighting.
Z and X to change color blend.
Use arrow keys to change size of lights and blocks.
1 and 2 to change between light placing mode and block placing mode respectively.
Click to selected and move a light/block
Control + Right Click to add light.
Control + Left Click to add block.

And here is a screen to show.


Now the lights will not look like openGL type lights mainly because java2D cannot do hardware additive blending.

This is the way the current api works.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
//create the lightmap with the screen width and height. The third arg is the lightmap scale reduction. 1 is no reduction
     lightmap = new LightMap(window.getWidth(),window.getHeight(),1);
      lightmap.setAmbientColor(Themes.getColor(Themes.GREEN));
      lightmap.setAmbientLuminosity(luma);
      lightmap.setColorBlend(colorBlend);
      lightmap.setAmbientLight(ambLight);


//add a light with location and size. Fifth arg is a color. By not passing in a BufferedImage as a fifth arg to use as a light, it will use a default one.
        Light l = new Light(0,0,size,size, Themes.getColor(Themes.CYAN));
//there are some vars and methods that will make the light do stuff.
        l.jump(window.mouseX, window.mouseY);
         l.decay = 0;
         l.intensity = 1;
// and add the light to the lightmap
        lightmap.addLight(l);

//Then update the lightmap
     lightmap.update();
//and finally render everything that will be effected by the lightmap first and then render the lightmap
     Graphics2D g2d = window.begin();
      //draw stuff
     lightmap.render(g2d);
//render stuff not lit here
     g2d.dispose();
      window.end();


The only thing that is left is to add blocks to the lightmap via

1  
lightmap.cullers.add(new Block(x,y,width,hieght)


These could be walls or a set of blocks for a player or other things.

Circle cullers are actually cheaper and the code has been done but they don't play well with blocks but for a player or sprite they would work great. If people want I can post the source in the shared code section and explain a little more on how it is done.



Offline davedes
« Reply #1 - Posted 2013-08-08 04:42:09 »

No source? Tongue

Like you say, generally Java2D is not a good way to go for this, but it's a nice learning experience. And it could give you some insight later down the road if you need to use HTML5 canvas or another platform that doesn't support shaders.

I'll piggyback on your thread by posting another approach in Java2D that I did for a school project. It's very basic, but fast enough.


Full source:
https://gist.github.com/mattdesl/5287214

The "falloff" is created with RadialGradientPaint. The trick is to just treat each entity as a circle. Alternatively, you could use classic "shadow boundary" technique described by Orangy Tang to get a more accurate geometry; in brief it looks like this:
https://gist.github.com/mattdesl/5287255

Offline StumpyStrust
« Reply #2 - Posted 2013-08-08 05:04:51 »

Yup what you are doing is similar to what I do. Down side is that your block is off a bit because of using circles. What I do is basically the same as you.

Here is the source.

http://pastebin.java-gaming.org/3d4277f6960

I also support culling when you have an offset to the camera.

Basic idea is finding the corner points of the squares and creating a polygon from that based on the points angle to the light. Cool thing is that this supports baking lights. Basically, find the shadows, bake to BufferedImage, and render said image. This is a huge performance boost as you lose the fully dynamic lights but gain static fake dynamic lights.


Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline StumpyStrust
« Reply #3 - Posted 2013-08-17 18:50:08 »

Because I can.



Fully dynamic lighting with arbitrary shapes. Support rotation and scaling of said shapes.  Grin

Offline The-Next

Senior Member


Medals: 5
Projects: 1



« Reply #4 - Posted 2013-09-06 21:42:26 »

StumpyStrust!

I have to say that one of your previous posts was really helpful for me in order to make a (fake) dynamic lighting system. Thanks a lot!! I owe you a beer.

Using an idea similar to yours I managed to do dynamic shadows. Sorry for the bad video framerate. The performance is quite acceptable.

<a href="http://www.youtube.com/v/crrH_WHF4WQ?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/crrH_WHF4WQ?version=3&amp;hl=en_US&amp;start=</a>



The Idea I chose was to, from the light source and the picture contour, create a polygon in real time and in an intermediate volatileimage, remove the polygon to the light sprite with the AlphaComposite.SRC_OUT. If you are interested in some details just ask me

Offline StumpyStrust
« Reply #5 - Posted 2013-09-07 00:03:48 »

Absolutely! That is so close to what I am doing. How are you creating the polygon from the image contours? How fast is it? How many lights can you have? Are you using an image as the light or drawing a gradient? Right now I can do 200 lights that are 256x256 in size where after I hit a fillrate limit. So basically, no limit on the number of lights.

Offline The-Next

Senior Member


Medals: 5
Projects: 1



« Reply #6 - Posted 2013-09-07 08:57:47 »

Ok,  first what I have is an "Image_container class", which stores both images and polygons:

1  
2  
3  
4  
public class Image_container extends Identificable_element {

 private BufferedImage[] Image;
 private Polygon[] perimeter;


So, each image has its own associated perimeter polygon. I get these polygons with a (quite) improved version of this idea: http://stackoverflow.com/questions/7052422/image-graphic-into-a-shape

Once you have the light source location and the location of the polygon, you can create these fake shadows for each facet of the polygon. I optimized this by just creating a complex polygon which is composed by all the exposed object facets plus the shdow projection. If I have time I will try to clean up some code and post it here Tongue

After some thinking and optimizations you can reach nice performance. Not 300 lights of course, but maybe 5-6 lights with a few tens of objects in screen is feasible (on a mid-end card).  The biggest limitation here is the shadow rendering, (not the calculation, just the "drawpolygon"). If you have to render 10 shadows per light, you will become limited by fillrate, specially on lowend cards like intedl HD3000 (I works sooo bad with java2D).

For the light I use an existing image, as you did in your older posts about this. It works fine.

Also, you can get smoother shadows by doing the following, it is like a "cheap" antialias. It draws a traslucent shadow contour several time before drawing the shadow.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
private void paintShadow(Graphics2D gbsombras){

//"remove" contains the shadow polygon
//"gbsombras" is the target graphics2D    

  if (antiAlias) {
   
     gbsombras.setColor(new Color(0, 0, 0, 0.5f));
     
                       for (int a=-1;a<2;a++){
                       for (int b=-1;b<2;b++){  
                            remove.translate( a, b);
                            gbsombras.drawPolygon(remove.xpoints,remove.ypoints,remove.npoints);
                            remove.translate(-a,-b);
                            }}}
                             

      gbsombras.setColor(Color.BLACK);                
      gbsombras.fillPolygon(remove.xpoints,remove.ypoints,remove.npoints);
}


Offline StumpyStrust
« Reply #7 - Posted 2013-09-07 11:52:27 »

So how to you cast the shadow? Right now I am using the java Area class which is very slow for constructing the shadow. Please do post code. I also would love to see how you create these polygons. Are they done in real time? or pre-computed.


That anti-aliasing is not cheap. Java can draw quite a few Images fast. Things like polygon fills, and gradients, are much slower. I have an idea for softish shadows that will be truly free. Need to test it. I can confirm that normally finding shadows is fast but drawing them is slow. I fixed this with volatile images. Now, very little to no penalty for size or number of objects.

On my laptop which I think is HD 3000 I have the most Fing weird bugs because the drivers are such shit.  I have solved most and know what is going on but it pisses me off. On that one, I can get 100+ lights.

Do you combine all the shadows as you find them? This could allow you to cull things out if they are already blocked by a shadow caster but in my tests it is faster to just draw each shadow as you find it. Also, how did you solve the shadows of one gear not clipping on to another?

Offline The-Next

Senior Member


Medals: 5
Projects: 1



« Reply #8 - Posted 2013-10-05 06:51:56 »

OMG I'm sorry for the delay!!!

Finally, this saturday morning I have some spare time Tongue

I cast the shadow using some trigonometry. If you know the location of a polygon point and the location of the light source, you can get the expression of the line that connects them. This line is one of the rays casted by the shadow.


I use polygons, not areas. I just add points to that Polygon and then I send the polygon to render. Pretty fast and simple.


I calculate all the shadow polygons in real time, it is not very resource consuming. This is some graphical sample of what I do. lines p3-p4 and p2-p4 are ignored as they are in the back of the object.




I agree with you that

This is part of the code (removed some non important stuff) . It is a mess and it is full of spanish variable names, so it may be quite confusing for you. I will try to fix this some day. Anyways I hope that you will get the general idea.


For any doubt, please ask me!! ;(

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
//I call this method n times for each light, where n is the number of objects inside the scene

//ii: object number ii of the scene
//luz_baseX,int luz_base, screen location of the light
//distancia_fuera: distance of shadow trail
//lightScale: scale of the light
//gsombras: volatileimage where I store all the shadows
//dimMaxMedia: size/ of the light


private void AddSombras_to_luz(int ii,int luz_baseX,int luz_baseY,float distancia_fuera,float lightScale,Graphics2D gbsombras,int dimMaxMedia){

   
          //LIGHT POSITION ON SCREEN (X,Y)
         float centroX=(luz_baseX*light_resolution);          
          float centroY=(luz_baseY*light_resolution);      
         
          int quita_baseX,quita_baseY;

           
          //POLYGON THAT STORES THE IMAGE CONTOUR
         Polygon quita;
           
          //GET POLYGON    
         
   
                if (!A_e.get_non_bg_objects(ii).get_cast_shadow()) return;              
                quita      =A_e.get_non_bg_objects(ii).getPolygon(tiempoms);

               
                //LOCATION OF THE UP-LEFT CORNER OF THIS POLYGON ON SCREEN
               quita_baseX=A_e.get_non_bg_objects_x(ii);
                quita_baseY=A_e.get_non_bg_objects_y(ii);
               
               
                       
          //NO POLYGON OR LESS THAN 3 VERTEX
          if (quita== null || quita.npoints<3) return;
           
         
           

           
          boolean anterior_shadow=false;
          int cont=0;
          int pos=0;
         
           //two points of the object polygon
           float pto1_x,pto1_y,pto2_x,pto2_y;
           
            //distance from these points to light source
           float dist1,dist2;
         
            pto2_x=((quita_baseX+quita.xpoints[0]));
            pto2_y=((quita_baseY+quita.ypoints[0]));
            dist2=(float)Math.sqrt( (pto2_y-centroY)*(pto2_y-centroY)+(pto2_x-centroX)*(pto2_x-centroX));
           
           

           
          //for each point of the polygon
         while(cont<quita.npoints){
          boolean actual_shadow=true;    
             
         
          //move point 2 to point 1
         pto1_x=pto2_x;
          pto1_y=pto2_y;
                   
           //read a new point from the polygon
         if (pos<quita.npoints-1) {
            pto2_x=((quita_baseX+quita.xpoints[pos+1]));
            pto2_y=((quita_baseY+quita.ypoints[pos+1]));}
          else {
            pto2_x=((quita_baseX+quita.xpoints[0]));
            pto2_y=((quita_baseY+quita.ypoints[0]));}  
         
         
         //distances to light source
        dist1=dist2;
         dist2=(float)Math.sqrt( (pto2_y-centroY)*(pto2_y-centroY)+(pto2_x-centroX)*(pto2_x-centroX));
       
         
        //optimizations
       
         //this vertex is outside light range, it should not project shadow
       if (dist1>(dimMaxMedia)*lightScale   && dist2>(dimMaxMedia)*lightScale  ) {actual_shadow=false;}
       
       
        else{
       
        //the line between these two points creales a line. is this line in the front side of the object or in the back side
       //if it is in the back side it should not project a light (useless)
       
        //we calculate the bisector and we move us 4 pixels from the middle of the line to the line in the direction of the bisector.
       //is we are inside the polygon we don't project a shadow, othwerwise we project it
           
        float bisectriz_X=(pto1_x+pto2_x)*0.5f;
        float bisectriz_Y=(pto1_y+pto2_y)*0.5f;
        float dist_bis=(float)Math.sqrt( (bisectriz_Y-centroY)*(bisectriz_Y-centroY)+(bisectriz_X-centroX)*(bisectriz_X-centroX));
       
        float bcos_1=(bisectriz_X-centroX)/dist_bis;
        float bsin_1=(bisectriz_Y-centroY)/dist_bis;
        float binIn_X=(bisectriz_X -bcos_1*4);
        float binIn_Y=(bisectriz_Y-bsin_1*4);
 
       
        if (quita.contains((binIn_X*light_resolution_inv-(quita_baseX)),
                           (binIn_Y*light_resolution_inv-(quita_baseY)))) {actual_shadow=false;}}
       
       
       
       
        if (cont==0 && actual_shadow) {if (pos==quita.npoints-1) break; pos++;continue;}
       
       
        //even more optimization!!
       
        //cases    anterior_shadow, actual shadow
       //               false        true                               add pto 3 (shadow ray) y 1
       //               false        false                              no shadow is projected, we ignore these points.
       //               true         true                               add pto 1.
       //               true         false   (||actual=last)            add pto 1, pto 2, pto 4 (shadow ray) and render

       
                      float cos_1 = (pto1_x - centroX) / dist1;
                      float sin_1 = (pto1_y - centroY) / dist1;
                      float cos_2 = (pto2_x - centroX) / dist2;
                      float sin_2 = (pto2_y - centroY) / dist2;
       
              if (actual_shadow == true) {
                  if (anterior_shadow == true) {
                      //add point to the polygons
                     quita2.addPoint(Math.round(pto2_x+ cos_2*2), Math.round(pto2_y+ sin_2*2));
                     

                  } else {
                      float pto3_X = (centroX + cos_1 * distancia_fuera);
                      float pto3_Y = (centroY + sin_1 * distancia_fuera);
                       //add point to the polygons
                     quita2.addPoint(Math.round(pto3_X), Math.round(pto3_Y));
                      quita2.addPoint(Math.round(pto1_x+ cos_1*2), Math.round(pto1_y+ sin_1*2));
                      quita2.addPoint(Math.round(pto2_x+ cos_2*2), Math.round(pto2_y+ sin_2*2));}

                  if (cont == quita.npoints - 1) {

                      float pto4_X = (centroX + cos_2 * distancia_fuera);
                      float pto4_Y = (centroY + sin_2 * distancia_fuera);
                        //add point to the polygons
                       quita2.addPoint(Math.round(pto4_X+ cos_2*2), Math.round(pto4_Y+ sin_2*2));
                       
                     //render the polygon
                    { paintShadow(gbsombras);}
                      quita2.reset();}


              } else {
                  if (anterior_shadow == true) {
                      float pto3_X = (pto1_x + cos_1 * distancia_fuera);
                      float pto3_Y = (pto1_y + sin_1 * distancia_fuera);
                       //add point to the polygons      
                     quita2.addPoint(Math.round(pto3_X+ cos_1*2), Math.round(pto3_Y+ sin_1*2));
                     
                      //render the polygon
                          paintShadow(gbsombras);
                      quita2.reset();
                     
                  }

              }
       
                   anterior_shadow=actual_shadow;
                   cont++;
                   pos++;if (pos==quita.npoints) pos=0;
          }

}
Offline StumpyStrust
« Reply #9 - Posted 2013-10-05 10:13:02 »

Sweet. I am not sure if you do this already but give the lights a max radius and check if a shadow caster is inside before you add its shadow. You cull out points but you could drop the whole shape much faster.

I do the exact same thing with circles and squares. I only used areas with general polys as I could not solve the issue where a object casts a light on its self.  I can get the front or back facing polys very very fast. The issues is when the object has an edge that could occlude an inner part of itself. Normally you project the points furthest (perpendicular from the center point of the shape and light point) out; p2 and p3 in your picture. But if you get some edge that protrudes out. Ray casting deals with these but requires checking if a ray intersects the shape and the point of said intersection to form the polygon shadow.

I looks like you do that towards the end but I am not quite sure how.

Hehe it is Friday night here.

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

CogWheelz (18 views)
2014-07-30 21:08:39

Riven (26 views)
2014-07-29 18:09:19

Riven (15 views)
2014-07-29 18:08:52

Dwinin (13 views)
2014-07-29 10:59:34

E.R. Fleming (33 views)
2014-07-29 03:07:13

E.R. Fleming (12 views)
2014-07-29 03:06:25

pw (43 views)
2014-07-24 01:59:36

Riven (44 views)
2014-07-23 21:16:32

Riven (30 views)
2014-07-23 21:07:15

Riven (31 views)
2014-07-23 20:56:16
List of Learning Resources
by SilverTiger
2014-07-31 18:29:50

List of Learning Resources
by SilverTiger
2014-07-31 18:26:06

List of Learning Resources
by SilverTiger
2014-07-31 13:54:12

HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!