noise(x) { return Noise.noise(x); } noise(x,y) { return Noise.noise(x,y); } noise(x,y,z) { return Noise.noise(x,y,z); } I3D and GDC I3D GDC Oculus Rift Versu iFly Lighting and shading Directional light sources // each light has both a direction and a color: L i = { Ldiri , Lcolori } For example: double[][][] lights = { // Ldir Lcolor { { 1.0, 1.0,-0.5}, {0.0, 0.0, 1.0} }, { {-1.0,-0.3, 1.0}, {1.0, 0.0, 0.0} }, };
lerp(t,a,b) { return a + t * (b - a); } setup() { render.setBgColor(0,0,0); render.setFL(25); render.setFOV(0.5); material = new Material(); A = lights[0][1]; B = lights[1][1]; material.setAmbient(.1*(A[0]+B[0]),.1*(A[1]+B[1]),.1*(A[2]+B[2])); material.setDiffuse(0.8, 0.8, 0.8); material.setSpecular(1,1,1,10); ball = render.getWorld().add().sphere(20).superquadric(6,0); ball.getMatrix().rotateX(.2).rotateY(-.3); ball.setMaterial(material); for (n = 0 ; n < lights.length ; n++) { P = lights[n][0]; C = lights[n][1]; render.addLight(P[0],P[1],P[2],C[0]/2,C[1]/2,C[2]/2); s = 6 / Vec.norm(P); lamp = render.getWorld().add(); lamp.getMatrix().translate(s*P[0],s*P[1],s*P[2]) .scale(0.13); K = 10; for (int k = 0 ; k < K ; k++) { material = new Material(); material.setAmbient(C[0], C[1], C[2]); t = 1 - (double)k / K; material.setTransparency(1 - t * t); g = lamp.add().disk(16); g.setMaterial(material); g.getMatrix().scale(1 + .7*k); } } }
Phong reflectance model Argb: ambient color Drgb: diffuse color Srgb: specular color p: specular power N: surface normal direction E: direction toward eye R i: reflection of light = 2(LdiriN)N - Ldiri
path boolean mousePress(x,y) { setXY(x,y); return true; } mouseDrag(x,y) { setXY(x,y); } setXY(x,y) { mx = fx(Math.abs(x)); my = fy(y); } draw() { draw.setColor(Color.blue); draw.draw(-1,-.8,1,-.8,.02); if (path) { draw.setColor(Color.red); draw.fillArrow(.03,.8-.06,mx,my,.02); draw.drawText("L.N",-.16,.35); draw.fillArrow(0,-.8,0,0,.02); draw.fillArrow(0,0,0,.8,.02); draw.drawText("-Ldir",0.48,.45); } else { draw.setColor(Color.black); draw.fillArrow(0,-.8,0,.2,.02); draw.drawText("N", -.07, -.3); } draw.setColor(Color.black); draw.drawText("Ldir",-0.45,-.45); draw.drawText("R", 0.36,-.45); draw.fillArrow(0,-.8,-mx,my,.02); draw.fillArrow(0,-.8, mx,my,.02); } mx = .6; my = 0;
Loop through all light sources: Argb + i Lcolori ( Drgb max(0, LdiriN) + Srgbmax(0, R iE)p ) For example: double[][][] lights = { { { 1.0, 1.0, 1.0}, {1.0, 1.0, 1.0} }, { {-1.0,-1.0,-1.0}, {1.0, 1.0, 1.0} }, }; double[] A = {0.2,0.0,0.0}; double[] D = {0.8,0.0,0.0}; double[] S = {1.0,1.0,1.0}; double p = 10;
setup() { render.setFOV(.25); material = new Material(); material.setAmbient(A[0],A[1],A[2]); material.setDiffuse(D[0],D[1],D[2]); material.setSpecular(S[0],S[1],S[2],p); ball = render.getWorld().add().torus(32,32,.4); ball.setMaterial(material); ball.getMatrix().rotateX(-1); for (n = 0 ; n < lights.length ; n++) { P = lights[n][0]; C = lights[n][1]; render.addLight(P[0],P[1],P[2],C[0],C[1],C[2]); } }
Gamma correction The reflectance algorithm produces R,G,B values between 0.0 and 1.0. Then we need to correct for the gamma of the display screen: pixelrgb [ 255 R0.45 , 255 G0.45 , 255 B0.45 ] axesColor = new Color(0,0,255); curveColor = new Color(255,0,0); Gamma curve:
gamma draw() { s = 0.8; draw.setColor(axesColor); draw.draw(-s,-s,-s,s,0.01); draw.draw(-s,-s,s,-s,0.01); draw.drawText("0.0",-s,-s-.15); draw.drawText("1.0", s,-s-.15); draw.drawText("0",-s-.12,-s); draw.drawText("255", -s-.2,s); dx = 0.03; draw.setColor(curveColor); for (x0 = -1.0 ; x0 < 1.0 ; x0 += dx) { x1 = x0 + dx; y0 = 2 * Math.pow(0.5 + 0.5 * x0, gamma) - 1; y1 = 2 * Math.pow(0.5 + 0.5 * x1, gamma) - 1; draw.draw(s * x0, s * y0, s * x1, s * y1, 0.02); } draw.setColor(Color.black); draw.drawText("gamma = " + gamma, 0,0); }
How different materials respond to light plastic Plastic: dye particles embedded in clear acrylic
bounce yellow ray blue ray lightOrange = new Color(255,192,128); draw() { for (int n = 0 ; n < 200 ; n++) { x0 = 3.0 * (n / 200. - .5); x1 = 3.0 * ((n+1) / 200. - .5); y0 = .07 * noise(10*x0); y1 = .07 * noise(10*x1); draw.draw(x0,y0,x1,y1,.01); } draw.setColor(Color.orange); for (int n = 0 ; n < 200 ; n++) { x = 0.9 * (n / 200. - .5); y = noise(.1 + 20*n, .5, 20.5); if (y > 0) y -= 0.6; draw.fillDisk(3 * x, y, .02); } if (bounce) { draw.setColor(Color.black); draw.fillArrow(-.7,.9,0,0,.02); draw.setColor(Color.gray); draw.fillArrow(0,0,.7,.9,.02); draw.fillArrow(0,0,.59,.97,.02); draw.fillArrow(0,0,.8,.8,.02); } if (orange) { draw.setColor(Color.orange); draw.draw(-.7,.9,.21,-.37,.02); draw.draw(.21,-.37,-.1,-.3,.02); draw.draw(-.1,-.3,.25,-.10,.02); draw.setColor(lightOrange); draw.fillArrow(.25,-.10,-.4,.2,.02); draw.fillArrow(.25,-.10,1.0,.3,.02); draw.fillArrow(.25,-.10, .6,.6,.02); draw.fillArrow(.25,-.10,-.1,.6,.02); } if (blue) { draw.setColor(Color.blue); draw.fillArrow(-.7,.9,.21,-.37,.02); } }
Specular reflections are white.
metal Metal: dialectric surface interaction with photons
bounce lightBlue = new Color(128,128,255); draw() { for (int n = 0 ; n < 200 ; n++) { x0 = 3.0 * (n / 200. - .5); x1 = 3.0 * ((n+1) / 200. - .5); y0 = .07 * noise(10*x0); y1 = .07 * noise(10*x1); draw.draw(x0,y0,x1,y1,.01); } if (bounce) { draw.setColor(Color.blue); draw.fillArrow(-.7,.9,0,0,.02); draw.setColor(lightBlue); draw.fillArrow(0,0,.7,.9,.02); draw.fillArrow(0,0,.59,.97,.02); draw.fillArrow(0,0,.8,.8,.02); } }
Specular reflections are not white.
plastic versus metal
setup() { render.setBgColor(.2,.5,.9); render.setFOV(.45); render.addLight( 1, 1, 1.0,1,1, 1); render.addLight(-1,-1,-1.5,.5,.5,.5); material1 = new Material(); material2 = new Material(); ball1 = render.getWorld().add().sphere(40); ball2 = render.getWorld().add().sphere(40); ball1.setMaterial(material1); ball2.setMaterial(material2); ball1.getMatrix().translate(-1.3,0,0).rotateX(.3).rotateY(.50); ball2.getMatrix().translate( 1.3,0,0).rotateX(.3).rotateY(.30); r = 1; g = m == 0 ? .35 : .65; b = m == 0 ? .20 : .10; material1.setAmbient(.10*r,.10*g,.10*b); material1.setDiffuse(.05*r,.05*g,.05*b); material1.setSpecular(1,1,1,10); material2.setAmbient(.10*r,.10*g,.10*b); material2.setDiffuse(.05*r,.05*g,.05*b); material2.setSpecular(r,g,b,10); /* material2.setAmbient(.10,.10,.10); material2.setDiffuse(.05,.05,.05); material2.setAmbient(.65,.65,.15); material2.setDiffuse(.00,.00,.00); */ ball1.superquadric(8, 0); ball2.superquadric(8, 0); }
Graphics processing units: Vertex shaders and fragment shaders vertex shader: (1) compute r,g,b at each vertex (2) interpolate rgb down to pixel hardware fragment shaders. Inspiring animations The Works Fiat Lux Jurassic Park HOMEWORK Your homework, due by class time on Wednesday April 10, will be to implement the Phong reflectance model, and to apply it at every vertex. So instead of interpolating transformed normal values, the scan conversion algorithm you have already implemented will be interpolating the result of converting transformed normals to (r,g,b) values. Remember to apply gamma correction, so that your r,g,b values between 0...255 will look correct on computer monitors. For the vector toward the eye E in the Phong specular calculation, you can use the approximation (0,0,1) -- the vector in the positive z direction. You will want to create a class Material, which contains the 10 numerical values of the Phong model: ambientColor[3], diffuseColor[3], specularColor[3] and specularPower. Each of your Geometry objects should refer to a Material object.