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 - Ldiripath
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
bounceyellow rayblue 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.