/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