// **************************************************************************** // // Ray-trace rendering // // **************************************************************************** #include "render.h" #include "main.h" #include "vector.h" #include "sphere.h" // list of spheres Sphere Spheres[] = { // position, radius, color, reflection, transparency (transparency not realized yet) Sphere(V3(-1.5,-0.4,1), 1, V3(1,1,0), 0.5, 0), // yellow sphere Sphere(V3(0,0.4,0), 1, V3(1,0,0), 0.5, 0), // red sphere Sphere(V3(2,-0.1,-6), 2, V3(0,0,(real)0.4), (real)0.7, 0), // blue sphere Sphere(V3(3,0.2,1), 1.5, V3(0,(real)0.5,0), (real)0.6, 0), // green sphere }; #define OBJNUM count_of(Spheres) V3 Camera(0, 0, 10); // camera position V3 Light(-6, 4, 10); // light position real Ambient = (real)0.3; // intensity of ambient light V3 BackCol((real)0.4, (real)0.6, 1); // background color in horizon #define TOOFAR 1e10 #define FLOORY -2 // floor Y position #define DEPTHMAX 6 // max. depth of trace // trace ray void Trace(V3* rgb, V3 &orig, V3 &dir, int depth, Sphere* disable) { // find nearest intersection real t1best = TOOFAR; Sphere* sbest = NULL; real t1, t2; uint i; for (i = 0; i < OBJNUM; i++) { if (&Spheres[i] == disable) continue; t1 = TOOFAR; t2 = TOOFAR; if (Spheres[i].Intersect(orig, dir, &t1, &t2)) { if (t1 < 0) t1 = t2; if (t1 < t1best) { t1best = t1; sbest = &Spheres[i]; } } } // if object not found, return sky color or continue with floor plane V3 col, pos, norm; real refl; if (sbest == NULL) { // sky (black in top direction) real k = dir.y; if (dir.y >= 0) { k *= 2.5; if (k > 1) k = 1; *rgb = BackCol*(1-k); return; } // floor - substitute with plane parameters t1best = (orig.y - FLOORY) / dir.y; pos = orig - (dir*t1best); norm.Set(0,1,0); col = ((int)(ceil(pos.x) + ceil(pos.z)) & 1) ? V3(1,1,1) : V3(1,0.25,0.25); refl = (real)0.4; } else { // coordinate and normal in intersection point pos = orig + (dir*t1best); norm = pos - sbest->pos; norm.Norm(); col = sbest->col; refl = sbest->ref; } // if normal and ray direction are not opposited, we are inside sphere, then reverse normal if (dir.Dot(norm) > 0) { norm.Inv(); } // vector to light V3 light = Light - pos; light.Norm(); // check if point is in shadow real intens = 1; for (i = 0; i < OBJNUM; i++) { if (sbest == &Spheres[i]) continue; if (Spheres[i].Intersect(pos, light, &t1, &t2)) { intens = 0; break; } } // get diffusion color intens = intens*norm.Dot(light)*(1-Ambient); if (intens <= 0) intens = 0; *rgb = col * (intens + Ambient); // add reflection if ((refl > 0) && (depth < DEPTHMAX)) { // reflection vector V3 rdir = dir - norm*2*dir.Dot(norm); rdir.Norm(); // add reflection V3 rgb2; Trace(&rgb2, pos, rdir, depth + 1, sbest); *rgb *= 1 - refl; *rgb += rgb2 * refl; } } // render image fast void Render3DFast() { // local variables int tmp; int x, y; // current X and Y coordinates in bitmap real xx, yy; // current X and Y coordinates in viewing plane V3 rgbV; // result pixel color as vactor 0..1 V3 orig(0, 0, 10); // camera position V3 dir; // ray direction real fov = 45*PI/180; // field of view in degrees real tfov = (real)tan(fov/2); // height/2 of viewing plane real ar = WIDTH/(real)HEIGHT; // aspect ratio u8* dst = &Box[0]; int red, green, blue; // render picture for (y = HEIGHT-2; y >= 0; y -= 2) { for (x = 0; x < WIDTH; x += 2) { // ray direction vector xx = (real)(2*(x + 0.5)/WIDTH - 1) * tfov * ar; yy = (real)(2*(y + 0.5)/HEIGHT - 1) * tfov; dir.Set(xx, yy, -1); dir.Norm(); // trace this ray Trace(&rgbV, Camera, dir, 0, NULL); // convert vector to RGB pixel tmp = (int)(rgbV.x*256 + 0.5); if (tmp < 0) tmp = 0; if (tmp > 255) tmp = 255; red = tmp; // red tmp = (int)(rgbV.y*256 + 0.5); if (tmp < 0) tmp = 0; if (tmp > 255) tmp = 255; green = tmp; // green tmp = (int)(rgbV.z*256 + 0.5); if (tmp < 0) tmp = 0; if (tmp > 255) tmp = 255; blue = tmp; // blue // dithering correction int k = ((x ^ y) & 2) << 2; // write RGB pixel red += k + RandS8MinMax(-2, 3); if (red > 255) red = 255; if (red < 0) red = 0; red &= 0xe0; green += k + RandS8MinMax(-2, 3); if (green > 255) green = 255; if (green < 0) green = 0; green &= 0xe0; blue += (k<<1) + RandS8MinMax(-3, 4); if (blue > 255) blue = 255; if (blue < 0) blue = 0; u8 b = (u8)(red | (green >> 3) | (blue >> 6)); dst[WIDTH] = b; dst[WIDTH+1] = b; *dst++ = b; *dst++ = b; } dst += WIDTH; } } // render image (useaa = use antialiasing) void Render3D(Bool useaa) { // local variables int tmp; int x, y; // current X and Y coordinates in bitmap real xx, yy; // current X and Y coordinates in viewing plane V3 rgbV; // result pixel color as vactor 0..1 V3 orig(0, 0, 10); // camera position V3 dir; // ray direction real fov = 45*PI/180; // field of view in degrees real tfov = (real)tan(fov/2); // height/2 of viewing plane real ar = WIDTH/(real)HEIGHT; // aspect ratio u8* dst = &Box[0]; int red, green, blue; // render picture for (y = HEIGHT-1; y >= 0; y--) { for (x = 0; x < WIDTH; x++) { // use antialiasing if (useaa) { int ix, iy; V3 rgbVtmp; rgbV.Zero(); for (iy = 0; iy < 3; iy++) { for (ix = 0; ix < 3; ix++) { // ray direction vector xx = (real)(2*(x + ix/(real)3 + 1/(real)6)/WIDTH - 1) * tfov * ar; yy = (real)(2*(y + iy/(real)3 + 1/(real)6)/HEIGHT - 1) * tfov; dir.Set(xx, yy, -1); dir.Norm(); // trace this ray Trace(&rgbVtmp, Camera, dir, 0, NULL); rgbV += rgbVtmp; } } rgbV *= (real)1/9; } // no antialiasing else { // ray direction vector xx = (real)(2*(x + 0.5)/WIDTH - 1) * tfov * ar; yy = (real)(2*(y + 0.5)/HEIGHT - 1) * tfov; dir.Set(xx, yy, -1); dir.Norm(); // trace this ray Trace(&rgbV, Camera, dir, 0, NULL); } // convert vector to RGB pixel tmp = (int)(rgbV.x*256 + 0.5); if (tmp < 0) tmp = 0; if (tmp > 255) tmp = 255; red = tmp; // red tmp = (int)(rgbV.y*256 + 0.5); if (tmp < 0) tmp = 0; if (tmp > 255) tmp = 255; green = tmp; // green tmp = (int)(rgbV.z*256 + 0.5); if (tmp < 0) tmp = 0; if (tmp > 255) tmp = 255; blue = tmp; // blue // dithering correction int k = ((x ^ y) & 3) << 3; // write RGB pixel red += k + RandS8MinMax(-3, 5); if (red > 255) red = 255; if (red < 0) red = 0; red &= 0xe0; green += k + RandS8MinMax(-3, 5); if (green > 255) green = 255; if (green < 0) green = 0; green &= 0xe0; blue += (k<<1) + RandS8MinMax(-6, 10); if (blue > 255) blue = 255; if (blue < 0) blue = 0; *dst++ = (u8)(red | (green >> 3) | (blue >> 6)); } } }