/Users/lyon/j4p/src/graphics/raytracers/tracer/Tracer.java

1    package graphics.raytracers.tracer; 
2     
3    import graphics.raytracers.tracer.geometry.Ray3d; 
4    import graphics.raytracers.tracer.geometry.Vector3d; 
5    import graphics.raytracers.tracer.primatives.Isect; 
6    import graphics.raytracers.tracer.primatives.Light; 
7    import graphics.raytracers.tracer.primatives.Primitive; 
8    import graphics.raytracers.tracer.primatives.Surface; 
9     
10   import java.awt.*; 
11   import java.awt.image.ColorModel; 
12   import java.awt.image.ImageConsumer; 
13   import java.awt.image.ImageProducer; 
14   import java.util.Hashtable; 
15    
16   public class Tracer implements ImageProducer { 
17       Light lights[] = new Light[8]; 
18       int nlights; 
19       Primitive prims[] = new Primitive[16]; 
20       int nprims; 
21    
22       int row[]; 
23       view theView; 
24    
25       int width, height; 
26       ColorModel model; 
27       Hashtable props; 
28       private ImageConsumer theConsumer; 
29    
30       public Tracer(int w, int h, view v) { 
31           width = w; 
32           height = h; 
33           props = new Hashtable(); 
34           theView = v; 
35           model = ColorModel.getRGBdefault(); 
36       } 
37    
38       /* 
39        * This ImageProducer boilerplate should be in 
40        * another class we can extend. 
41        */ 
42       public synchronized void addConsumer(ImageConsumer ic) { 
43           theConsumer = ic; 
44           try { 
45               produce(); 
46           } catch (Exception e) { 
47               if (theConsumer != null) { 
48                   theConsumer.imageComplete( 
49                           ImageConsumer.IMAGEERROR); 
50               } 
51           } 
52           theConsumer = null; 
53       } 
54    
55       public synchronized boolean isConsumer(ImageConsumer ic) { 
56           return ic == theConsumer; 
57       } 
58    
59       public synchronized void removeConsumer(ImageConsumer ic) { 
60           if (ic == theConsumer) { 
61               theConsumer = null; 
62           } 
63       } 
64    
65       public void startProduction(ImageConsumer ic) { 
66           addConsumer(ic); 
67       } 
68    
69       public void requestTopDownLeftRightResend(ImageConsumer ic) { 
70       } 
71    
72       /* 
73        * Find closest ray, return initialized isect 
74        * with intersection information. 
75        */ 
76       Isect intersect(Ray3d r, double maxt) { 
77           Isect tp; 
78           Isect inter; 
79           int i, nhits; 
80    
81           nhits = 0; 
82           inter = new Isect(); 
83           inter.t = 1e9; 
84           for (i = 0; i < nprims; i++) { 
85               tp = prims[i].intersect(r); 
86               if (tp != null && tp.t < inter.t) { 
87                   inter.t = tp.t; 
88                   inter.prim = tp.prim; 
89                   inter.surf = tp.surf; 
90                   inter.enter = tp.enter; 
91                   nhits++; 
92               } 
93           } 
94           return nhits > 0 ? inter : null; 
95       } 
96    
97       int Shadow(Ray3d r, double tmax) { 
98           if (intersect(r, tmax) != null) 
99               return 0; 
100          return 1; 
101      } 
102   
103      /* 
104       * Return the vector's cutils.reflection direction. 
105       */ 
106      Vector3d SpecularDirection(Vector3d I, Vector3d N) { 
107          Vector3d r; 
108          r = Vector3d.comb(1.0 / Math.abs(Vector3d.dot(I, N)), I, 2.0, N); 
109          r.normalize(); 
110          return r; 
111      } 
112   
113      /* 
114       * Likewise for transmission direction... 
115       */ 
116      Vector3d TransDir(Surface m1, Surface m2, Vector3d I, Vector3d N) { 
117          double n1, n2, eta, c1, cs2; 
118          Vector3d r; 
119          n1 = m1 == null ? 1.0 : m1.ior; 
120          n2 = m2 == null ? 1.0 : m2.ior; 
121          eta = n1 / n2; 
122          c1 = -Vector3d.dot(I, N); 
123          cs2 = 1.0 - eta * eta * (1.0 - c1 * c1); 
124          if (cs2 < 0.0) 
125              return null; 
126          r = Vector3d.comb(eta, I, eta * c1 - Math.sqrt(cs2), N); 
127          r.normalize(); 
128          return r; 
129      } 
130   
131      /* 
132       * Straight out of Glassner via MTV ... 
133       */ 
134      Vector3d shade(int level, double weight, Vector3d P, Vector3d N, Vector3d I, Isect hit) { 
135          Ray3d tray; 
136          Vector3d tcol; 
137          Vector3d L, H, R; 
138          double t, diff, spec; 
139          Surface surf; 
140          Vector3d col; 
141          int l; 
142   
143          col = new Vector3d(0, 0, 0); 
144          surf = hit.surf; 
145          R = new Vector3d(0, 0, 0); 
146          if (surf.shine > 1e-6) { 
147              R = SpecularDirection(I, N); 
148          } 
149          for (l = 0; l < nlights; l++) { 
150              L = Vector3d.sub(lights[l].pos, P); 
151              if (Vector3d.dot(N, L) >= 0.0) { 
152                  t = L.normalize(); 
153                  tray = new Ray3d(P, L); 
154                  if (Shadow(tray, t) > 0) { 
155                      diff = Vector3d.dot(N, L) * surf.kd * 
156                              lights[l].brightness; 
157                      col = Vector3d.adds(diff, surf.color, col); 
158                      if (surf.shine > 1e-6) { 
159                          spec = Vector3d.dot(R, L); 
160                          if (spec > 1e-6) { 
161                              spec = Math.pow(spec, 
162                                      surf.shine); 
163                              col.setX(col.getX() + spec); 
164                              col.setY(col.getY() + spec); 
165                              col.setZ(col.getZ() + spec); 
166                          } 
167                      } 
168                  } 
169              } 
170          } 
171   
172          tray = new Ray3d(P, new Vector3d(0, 0, 0)); 
173          if (surf.ks * weight > 1e-3) { 
174              tray.setDirection(SpecularDirection(I, N)); 
175              tcol = trace(level + 1, surf.ks * weight, tray); 
176              col = Vector3d.adds(surf.ks, tcol, col); 
177          } 
178          if (surf.kt * weight > 1e-3) { 
179              if (hit.enter > 0) 
180                  tray.setDirection(TransDir(null, surf, I, N)); 
181              else 
182                  tray.setDirection(TransDir(surf, null, I, N)); 
183              tcol = trace(level + 1, surf.kt * weight, tray); 
184              col = Vector3d.adds(surf.kt, tcol, col); 
185          } 
186          return col; 
187      } 
188   
189      Vector3d trace(int level, double weight, Ray3d r) { 
190          Primitive prim; 
191          Vector3d P, N; 
192          Isect hit; 
193   
194          if (level > 6) { 
195              return new Vector3d(0, 0, 0); 
196          } 
197   
198          hit = intersect(r, 1e6); 
199          if (hit != null) { 
200              prim = hit.prim; 
201              P = r.point(hit.t); 
202              N = prim.normal(P); 
203              if (Vector3d.dot(r.getDirection(), N) >= 0.0) { 
204                  N.negate(); 
205              } 
206              return shade(level, weight, P, N, r.getDirection(), hit); 
207          } 
208          return new Vector3d(0, 0, 0); 
209      } 
210   
211      void scan(Vector3d eye, Vector3d viewvec, Vector3d upvec, Vector3d leftvec, 
212                int xres, int yres, int xmin, int xmax) { 
213          Ray3d r; 
214          int x, y, red, green, blue; 
215          double xlen, ylen; 
216          Image imgLine; 
217          Vector3d col; 
218   
219          r = new Ray3d(eye, new Vector3d(0, 0, 0)); 
220          System.out.println("scan: " + xres + "," + yres); 
221          System.out.println("scan: viewvec " + viewvec.toString()); 
222          System.out.println("scan: upvec " + upvec.toString()); 
223          System.out.println("scan: leftvec " + leftvec.toString()); 
224          col = new Vector3d(0, 0, 0); 
225          for (y = 0; y < yres; y++) { 
226              ylen = (double) (2.0 * y) / (double) yres - 1.0; 
227              int subImage[] = 
228                      getColumn(xmin, xmax, xres, leftvec, ylen, upvec, r, viewvec); 
229   
230              theConsumer.setPixels(0, y, xmax - xmin, 1, model, 
231                      subImage, 0, xmax - xmin); 
232          } 
233      } 
234   
235      private int[] getColumn( 
236              int xmin, int xmax, int xres, 
237              Vector3d leftvec, double ylen, Vector3d upvec, Ray3d r, Vector3d viewvec) { 
238          int x; 
239          double xlen; 
240          Vector3d col; 
241          int red; 
242          int green; 
243          int blue; 
244          int subImage[] = new int[xmax - xmin]; 
245          for (x = xmin; x < xmax; x++) { 
246              xlen = (double) (2.0 * x) / (double) xres - 1.0; 
247              r.setDirection(Vector3d.comb(xlen, leftvec, ylen, upvec)); 
248              r.setDirection(Vector3d.add(r.getDirection(), viewvec)); 
249              r.getDirection().normalize(); 
250              col = trace(0, 1.0, r); 
251   
252              red = (int) (col.getX() * 255.0); 
253              if (red > 255) 
254                  red = 255; 
255              green = (int) (col.getY() * 255.0); 
256              if (green > 255) 
257                  green = 255; 
258              blue = (int) (col.getZ() * 255.0); 
259              if (blue > 255) 
260                  blue = 255; 
261              subImage[x] = (255 << 24) | 
262                      (red << 16) | 
263                      (green << 8) | 
264                      (blue); 
265          } 
266          return subImage; 
267      } 
268   
269      private void produce() { 
270          Vector3d viewvec, leftvec, upvec, tmpvec; 
271          double frustrumwidth; 
272   
273          if (theConsumer != null) { 
274              theConsumer.setDimensions(width, height); 
275              theConsumer.setProperties(props); 
276              theConsumer.setColorModel(model); 
277              theConsumer.setHints(ImageConsumer.TOPDOWNLEFTRIGHT | 
278                      ImageConsumer.COMPLETESCANLINES | 
279                      ImageConsumer.SINGLEPASS | 
280                      ImageConsumer.SINGLEFRAME); 
281          } 
282   
283          viewvec = Vector3d.sub(theView.getAt(), theView.getFrom()); 
284          viewvec.normalize(); 
285   
286          tmpvec = new Vector3d(viewvec); 
287          tmpvec.scale(Vector3d.dot(theView.getUp(), viewvec)); 
288          upvec = Vector3d.sub(theView.getUp(), tmpvec); 
289          upvec.normalize(); 
290   
291          leftvec = Vector3d.cross(theView.getUp(), viewvec); 
292          leftvec.normalize(); 
293   
294          frustrumwidth = theView.getDist() * Math.tan(theView.getAngle()); 
295          upvec.scale(-frustrumwidth); 
296          leftvec.scale(theView.getAspect() * frustrumwidth); 
297   
298          scan(theView.getFrom(), viewvec, upvec, leftvec, width, height, 0, width); 
299          //scan(theView.from, viewvec, upvec, leftvec, width, height,0,100); 
300   
301          if (theConsumer != null) { 
302              theConsumer.imageComplete( 
303                      ImageConsumer.STATICIMAGEDONE); 
304          } 
305      } 
306   
307      void newLight(Vector3d p, double b) { 
308          if (nlights < 8) { 
309              lights[nlights] = new Light(); 
310              lights[nlights].pos = p; 
311              lights[nlights].brightness = b; 
312              nlights++; 
313          } 
314      } 
315   
316      void newPrim(Primitive p) { 
317          if (nprims < 16) { 
318              prims[nprims] = p; 
319              nprims++; 
320          } 
321      } 
322  } 
323