CMPS-3480 Computer Graphics
Lab-7
--------------------------- jump to transparent section ---------------------------Overview:
Show your images on an Odin web page at: public_html/3480/lab7.htmlLab-8 code from Wednesday for dent in sphere------------------------------- structs... ------------------------------- struct Clip { Vec center; Vec normal; Flt radius; int inside; Clip() { radius = 0.0; inside = 1; } }; struct Object { int type; int inside; Vec center; Vec norm; Vec tri[3]; Flt radius, radius2; Vec color; int surface; bool specular; Vec spec; Clip clip; int nclips; Object() { nclips = 0; inside = 0; } }; ------------------------------- function... ------------------------------- void vecNegate(Vec v) { v[0] = -v[0]; v[1] = -v[1]; v[2] = -v[2]; } ------------------------------- from init()... ------------------------------- //big ball o = &g.object[g.nobjects]; o->type = TYPE_SPHERE; vecMake(0.0, 200.0, 0.0, o->center); vecMake(1.0, 0.0, 0.0, o->color); o->radius = 200.0; o->specular = true; o->specular = 0; vecMake(0.3, 0.3, 0.3, o->spec); o->surface = SURF_NONE; //add a clip vecMake(50.0, 380.0, 90.0, o->clip.center); o->clip.radius = 60.0; ++o->nclips; g.nobjects++; // //small ball for dent o = &g.object[g.nobjects]; o->type = TYPE_SPHERE; vecMake(50.0, 380.0, 90.0, o->center); vecMake(1.0, 0.0, 0.0, o->color); o->radius = 60.0; o->specular = true; o->specular = 0; vecMake(0.3, 0.3, 0.3, o->spec); o->surface = SURF_NONE; o->inside = 1; //add a clip vecMake(0.0, 200.0, 0.0, o->clip.center); o->clip.radius = 200.0; o->clip.inside = 0; ++o->nclips; g.nobjects++; ------------------------------- from raySphereIntersect()... ------------------------------- if (t0 > 0.0) { hit->p[0] = ray->o[0] + ray->d[0] * t0; hit->p[1] = ray->o[1] + ray->d[1] * t0; hit->p[2] = ray->o[2] + ray->d[2] * t0; sphereNormal(hit->p, o->center, hit->norm); //if (o->inside) // vecNegate(hit->norm); hit->t = t0; if (o->nclips > 0) { if (o->clip.radius != 0.0) { //clipping sphere Vec v; vecSub(o->clip.center, hit->p, v); Flt dist = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); if (o->clip.inside) { if (dist <= o->clip.radius) goto t1; } else { if (dist >= o->clip.radius) goto t1; } } } return 1; } t1:; if (t1 > 0.0) { hit->p[0] = ray->o[0] + ray->d[0] * t1; hit->p[1] = ray->o[1] + ray->d[1] * t1; hit->p[2] = ray->o[2] + ray->d[2] * t1; sphereNormal(hit->p, o->center, hit->norm); //if (o->inside) // vecNegate(hit->norm); hit->t = t1; if (o->nclips > 0) { if (o->clip.radius != 0.0) { //clipping sphere Vec v; vecSub(o->clip.center, hit->p, v); Flt dist = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); if (o->clip.inside) { if (dist <= o->clip.radius) return 0; } else { if (dist >= o->clip.radius) return 0; } } } return 1; } ------------------------------- from trace()... ------------------------------- case TYPE_SPHERE: if (raySphereIntersect(o, ray, &hit)) { if (hit.t < closehit.t) { closehit.t = hit.t; vecCopy(hit.p, closehit.p); vecCopy(o->color, closehit.color); sphereNormal(closehit.p, o->center, closehit.norm); if (o->inside) vecNegate(closehit.norm); h = i; } } break; ------------------------------- spherical texture... -------------------------------
Name your texture image file: lab8a.png //This goes inside the trace() function if (o->surface == SURF_SPHERICAL_TEXTURE) { //Get the spherical texture color based on the hit-point normal. //Must have Image class from lab-1. // const Flt PI = 3.14159265358979; const Flt PIdiv2 = PI / 2.0; const Flt PItimes2 = PI * 2.0; Vec vn; vecCopy(closehit.norm, vn); //Use normal.y to get an angle Flt pct = asin(vn[1]) / PIdiv2; //pct might be positive or negative Flt h2 = (Flt)img.height * 0.5; //Get the row in the image stream Flt row = h2 + (pct * h2); //Get the angle of hitpoint going around the sphere Flt col_angle = atan2(vn[2], vn[0]); //Use angle to get the column in the image stream Flt col = ((col_angle + PI) / PItimes2) * (Flt)img.width; //flip y and x coordinates row = img.height - row - 1; col = img.width - col - 1; //Get row and column to use in pointer arithmetic int y = (int)row; int x = (int)col; //Get the spot within the image stream int spot = y * img.width * 3 + x * 3; //Get pointer to the pixel stream. unsigned char *p = img.data; //Get the red, green, blue colors. closehit.color[0] = (Flt)(*(p + (spot + 0))) / 255.0; closehit.color[1] = (Flt)(*(p + (spot + 1))) / 255.0; closehit.color[2] = (Flt)(*(p + (spot + 2))) / 255.0; }
Step 1:Log into Odin, using the -YC option. example: ssh -YC myname@odin Do this: $ cd 3480 $ ./lab-start.sh $ cd 7 $ cp /home/fac/gordon/p/3480/code/7/* . $ make $ ./lab7 Press M Step 1: render a scene similar to the one belowThis scene is also possible
Step 2: create a short animation Follow along with Gordon to produce an animation Step 3: make your own ray-traced animation 1. Animation must loop forever. 2. Animation must be seamless. Viewer does not know where it starts or ends. It can go up and down, left and right, or around. You may make the checkerboard floor texture move or change to give the appearance of the camera following an object.. Use the objects given. 3. Create gif animation from program. Create your gif animation at the command-line something like this: convert -delay
-loop 0 *.png mylab7.gif Your program will capture frames and create the gif animation on a key press. Add the key to your menu. 4. Make a web page at public_html/3480/lab7.html Display your animations.
//======================================================================= Screen shot function that produces .png files PNG is about 95% smaller than a PPM file. void takeScreenshot(const char *filename, int reset) { //This function will capture your current X11 window, //and save it to a PPM P6 image file. //File names are generated sequentially. static int picnum = 0; int x,y; int width, height; x11.getWindowAttributes(&width, &height); if (reset) picnum = 0; XImage *image = x11.getImage(width, height); // //If filename argument is empty, generate a sequential filename... char ts[200] = ""; char png[200] = ""; strcpy(ts, filename); strcpy(png, filename); if (ts[0] == '\0') { sprintf(ts,"./lab7%02i.ppm", picnum); sprintf(png,"./lab7%02i.png", picnum); picnum++; } FILE *fpo = fopen(ts, "w"); if (fpo) { fprintf(fpo, "P6\n%i %i\n255\n", width, height); for (y=0; y<height; y++) { for (x=0; x<width; x++) { unsigned long pixel = XGetPixel(image, x, y); fputc(((pixel & 0x00ff0000)>>16), fpo); fputc(((pixel & 0x0000ff00)>> 8), fpo); fputc(((pixel & 0x000000ff) ), fpo); } } fclose(fpo); } XFree(image); char str[450]; sprintf(str, "convert %s %s", ts, png); system(str); unlink(ts); } //=======================================================================
Ray tracer on a business card1. vi card.cpp 2. Copy and paste the code below. 3. g++ card.cpp -lm -o card 4. ./card > image.ppm 5. display image.ppm --------------------------------------- #include <stdlib.h> // card > aek.ppm #include <stdio.h> #include <math.h> typedef int i;typedef float f;struct v{ f x,y,z;v operator+(v r){return v(x+r.x ,y+r.y,z+r.z);}v operator*(f r){return v(x*r,y*r,z*r);}f operator%(v r){return x*r.x+y*r.y+z*r.z;}v(){}v operator^(v r ){return v(y*r.z-z*r.y,z*r.x-x*r.z,x*r. y-y*r.x);}v(f a,f b,f c){x=a;y=b;z=c;}v operator!(){return*this*(1/sqrt(*this%* this));}};i G[]={247570,280596,280600, 249748,18578,18577,231184,16,16};f R(){ return(f)rand()/RAND_MAX;}i T(v o,v d,f &t,v&n){t=1e9;i m=0;f p=-o.z/d.z;if(.01 <p)t=p,n=v(0,0,1),m=1;for(i k=19;k--;) for(i j=9;j--;)if(G[j]&1<<k){v p=o+v(-k ,0,-j-4);f b=p%d,c=p%p-1,q=b*b-c;if(q>0 ){f s=-b-sqrt(q);if(s<t&&s>.01)t=s,n=!( p+d*t),m=2;}}return m;}v S(v o,v d){f t ;v n;i m=T(o,d,t,n);if(!m)return v(.7, .6,1)*pow(1-d.z,4);v h=o+d*t,l=!(v(9+R( ),9+R(),16)+h*-1),r=d+n*(n%d*-2);f b=l% n;if(b<0||T(h,l,t,n))b=0;f p=pow(l%r*(b >0),99);if(m&1){h=h*.2;return((i)(ceil( h.x)+ceil(h.y))&1?v(3,1,1):v(3,3,3))*(b *.2+.1);}return v(p,p,p)+S(h,r)*.5;}i main(){printf("P6 512 512 255 ");v g=!v (-6,-16,0),a=!(v(0,0,1)^g)*.002,b=!(g^a )*.002,c=(a+b)*-256+g;for(i y=512;y--;) for(i x=512;x--;){v p(13,13,13);for(i r =64;r--;){v t=a*(R()-.5)*99+b*(R()-.5)* 99;p=S(v(17,16,8)+t,!(t*-1+(a*(R()+x)+b *(y+R())+c)*16))*3.5+p;}printf("%c%c%c" ,(i)p.x,(i)p.y,(i)p.z);}} --------------------------------------- Reference https://fabiensanglard.net/revisiting_the_businesscard_raytracer/------------------------------ For transparent objects... ------------------------------ Image from code below...Try to achieve this image... Code for the sky is shown below.
Log on to Odin and do this... $ cd 3400 $ ./lab-start.sh $ cd 8 $ cp lab8.cpp ../b/lab11.cpp $ cd ../b $ g++ lab11.cpp -Wall -lX11 Now add the following code to your lab11.cpp program. add to Object struct -------------------- Flt sf_transmit; Flt sf_iorOutside, sf_iorInside; Flt spectrans; add to Object constructor ------------------------- Object() { sf_transmit = 0.0; sf_iorOutside = 1.0; sf_iorInside = 1.0; spectrans = 0.1; } add to your scene in init() --------------------------- //clear glass ball o = &g.object[g.nobjects]; o->type = TYPE_SPHERE; vecMake(0.0, 200.0, 0.0, o->center); vecMake(0.0, 0.0, 0.0, o->color); o->radius = 200.0; o->specular = true; o->spectrans = 0.0; vecMake(0.3, 0.3, 0.3, o->spec); o->surface = SURF_NONE; o->sf_transmit = 1.0; // transparent material o->sf_iorOutside = 1.00029; // air o->sf_iorInside = 1.3; // glass o->nclips = 0; g.nobjects++; add to the trace() function --------------------------- if (o->sf_transmit > 0.0) { Ray rx; Vec nh; vecCopy(closehit.norm, nh); Flt kr, kt; fresnel(ray->d, nh, o->sf_iorOutside, o->sf_iorInside, kr); kt = 1.0 - kr; //reflect Vec rrgb = {0.0}; if (o->specular > 0.0) { kr *= o->spectrans; kt = 1.0 - kr; vecCopy(closehit.p, rx.o); reflect(ray->d, nh, rx.d); nudge_ray_forward(&rx); trace(&rx, rrgb, weight*kr, level+1); } //transmit Flt eta = o->sf_iorOutside / o->sf_iorInside; if ((vecDotProduct(ray->d, nh)) > 0.0) { //Normal is pointing the wrong way, so flip. vecNegate(nh); eta = 1.0 / eta; } refract(eta, ray->d, nh, rx.d); vecCopy(closehit.p, rx.o); Vec trgb = {0.0}; nudge_ray_forward(&rx); trace(&rx, trgb, weight*kt, level+1); rgb[0] += (rrgb[0] + trgb[0]); rgb[1] += (rrgb[1] + trgb[1]); rgb[2] += (rrgb[2] + trgb[2]); } else if (o->specular) { ... ... add just above trace() function ------------------------------- int refract(Flt eta, Vec I, Vec N, Vec T) { //Calculates direction of refracted angle, T. //Returns 0 if total internal reflection occurs. //eta = ratio of old/new iors // relative index-of-refraction //I = incident vector //N = surface normal //T = transmitted vector (calculated) Flt dot, c1, cs2; if (eta == 1.0) { //same material, no refraction occurs. vecCopy(I, T); return 1; } dot = -vecDotProduct(I, N); c1 = dot; cs2 = 1.0 - eta*eta * (1.0 - c1*c1); if (cs2 < 0.0) { //Total internal reflection //Reflect off surface instead of refract reflect(I, N, T); return 0; } cs2 = eta*c1 - sqrt(cs2); //combine the vectors... T[0] = eta * I[0] + cs2 * N[0]; T[1] = eta * I[1] + cs2 * N[1]; T[2] = eta * I[2] + cs2 * N[2]; return 1; } Flt clamp(Flt lo, Flt hi, Flt val) { if (val < lo) val = lo; if (val > hi) val = hi; return val; } Flt max(Flt v1, Flt v2) { if (v1 >= v2) return v1; return v2; } void swap(Flt &v1, Flt &v2) { Flt tmp = v1; v1 = v2; v2 = tmp; } void fresnel(Vec I, Vec N, Flt iorOut, Flt iorIn, Flt &kr) { //from scratch-a-pixel Flt cosi = clamp(-1, 1, vecDotProduct(I, N)); Flt etai = iorOut, etat = iorIn; if (cosi > 0.0) { swap(etai, etat); } //Compute sini using Snell's law Flt sint = etai / etat * sqrtf(max(0.0f, 1.0 - cosi * cosi)); //Total internal reflection if (sint >= 1.0) { kr = 1.0; } else { Flt cost = sqrtf(max(0.0f, 1.0 - sint * sint)); cosi = fabsf(cosi); Flt Rs = ((etat*cosi)-(etai*cost)) / ((etat * cosi) + (etai * cost)); Flt Rp = ((etai*cosi)-(etat*cost)) / ((etai * cosi) + (etat * cost)); kr = (Rs * Rs + Rp * Rp) / 2.0; } // As a consequence of the conservation of energy, // transmittance is given by: // kt = 1 - kr; } add to your menu ---------------- y += inc; x11.drawText(10, y, "G - Glass ball"); add to your checkKeys() function -------------------------------- if (key == XK_g) { g.mode = 'g'; return 0; }
add the sky from image above add to the Global class ----------------------- Vec sky[2]; add to the setup of your scene ------------------------------ //Setup a light vecMake(0.5, 0.5, 0.9, g.sky[0]); //sky color at the horizon vecMake(0.1, 0.1, 0.9, g.sky[1]); //sky color looking straight up add to the trace() function --------------------------- if (h < 0) { //ray did not hit an object. //sky color based on ray direction Flt c = ray->d[1]; rgb[0] += (g.sky[0][0] + (g.sky[1][0] - g.sky[0][0]) * c) * weight; rgb[1] += (g.sky[0][1] + (g.sky[1][1] - g.sky[0][1]) * c) * weight; rgb[2] += (g.sky[0][2] + (g.sky[1][2] - g.sky[0][2]) * c) * weight; return; }
multiple lights in a scene -------------------------- We will program this together on Friday. Get the source at: /home/fac/gordon/p/3480/code/b/lights.cpp $ cd 3480 $ ./lab-start.sh $ cd b $ cp /home/fac/gordon/p/3480/code/b/lights.cpp . This scene has 3-point lighting![]()