/*
Simple examples of procedural bump textures. - Ken Perlin
*/
public class Sphere extends MISApplet {
// PARAMETERS
int surfaceType = 0; // SURFACE TYPE (0,1 OR 2)
double surfaces[][][] = { // SURFACES:
// AMBIENT DIFFUSE SPECULAR
{{.2,.2,.2},{.6,.2,.1},{.3,.3,.3, 4}}, // BROWN
{{.2,.2,.2},{.2,.4,.8},{.3,.3,.3,20}}, // BLUISH
{{.2,.2,.2},{.6,.4,.3},{.3,.3,.3, 5}}, // PAPERY
};
double lights[][][] = { // LIGHTS
// DIRECTION // COLOR // SCATTER
{{.577, .577, .577},{1,1,1} ,{0,0,0}},
{{-.707, 0,-.707},{0,0,0} ,{.5,.4,.5}},
};
// WORKING GLOBAL VARIABLES
int type; // CURRENT SURFACE TYPE
double normal[] = {0,0,0}; // SURFACE NORMAL VECTOR
double color[] = {0,0,0}; // SURFACE COLOR VECTOR
// INITIALIZE ONE RENDERED FRAME
public void initFrame(double time) {
type = surfaceType; // SET THE SURFACE TYPE
}
// SET THE COLOR OF A SINGLE PIXEL
public void setPixel(int x, int y, int rgb[]) {
// SEE WHETHER PIXEL IS WITHIN THE ANTIALIASED DISK
double X = x-W/2; // X RELATIVE TO DISK CENTER
double Y = H/2-y; // Y RELATIVE TO DISK CENTER
double R = W/2-W/10; // RADIUS OF DISK
// IF PIXEL IS IN THE DISK, THEN
double t = disk(X*X+Y*Y, R);
if (t > 0) {
// COMPUTE POINT ON UNIT SPHERE (WHICH IS ALSO THE SURFACE NORMAL)
normal[0] = X / R;
normal[1] = Y / R;
normal[2] = Math.sqrt(1 - normal[0]*normal[0] - normal[1]*normal[1]);
// SAMPLE THE FUNCTION FOUR TIMES TO GET GRADIENT INFO
double f0 = f(normal[0] ,normal[1] ,normal[2] ),
fx = f(normal[0]+.0001,normal[1] ,normal[2] ),
fy = f(normal[0] ,normal[1]+.0001,normal[2] ),
fz = f(normal[0] ,normal[1] ,normal[2]+.0001);
// SUBTRACT THE FUNCTION'S GRADIENT FROM THE SURFACE NORMAL
normal[0] -= (fx - f0) / .0001;
normal[1] -= (fy - f0) / .0001;
normal[2] -= (fz - f0) / .0001;
normalize(normal);
double s[][] = surfaces[type];
for (int i = 0 ; i < 3 ; i++) // START WITH JUST AMBIENT COLOR
color[i] = s[0][i];
for (int L = 0 ; L < lights.length ; L++) { // ITERATE OVER ALL LIGHTS
// COMPUTE DIFFUSE AND SPECULAR REFLECTANCE FACTORS
double spec = specular(normal, lights[L][0], s[2][3]);
double diff = diffuse (normal, lights[L][0]);
// SHADE THE PIXEL WITH PHONG ALGORITHM
for (int i = 0 ; i < 3 ; i++)
color[i] += lights[L][1][i] * (s[1][i]*diff + s[2][i]*spec) + lights[L][2][i]*diff;
}
// CHANGE COLOR TO FIXED POINT AND SEND IT TO THE FRAME BUFFER
for (int i = 0 ; i < 3 ; i++)
rgb[i] = (int)(255 * t * color[i]);
}
// IF BACKGROUND, THEN PIXEL IS BLACK
else
rgb[0] = rgb[1] = rgb[2] = 0;
}
// CHOOSE A TYPE OF SPACE FILLING TEXTURE
double f(double x,double y,double z) {
switch (type) {
case 0: return .03 * noise(x,y,z, 8);
case 1: return .01 * stripes(x + 2*turbulence(x,y,z,1), 1.6);
default: return -.10 * turbulence(x,y,z, 1);
}
}
// STRIPES TEXTURE (GOOD FOR MAKING MARBLE)
double stripes(double x, double f) {
double t = .5 + .5 * Math.sin(f * 2*Math.PI * x);
return t * t - .5;
}
// TURBULENCE TEXTURE
double turbulence(double x, double y, double z, double freq) {
double t = -.5;
for ( ; freq <= W/12 ; freq *= 2)
t += Math.abs(noise(x,y,z,freq) / freq);
return t;
}
// NOISE TEXTURE
double noise(double x, double y, double z, double freq) {
double x1, y1, z1;
x1 = .707*x-.707*z;
z1 = .707*x+.707*z;
y1 = .707*x1+.707*y;
x1 = .707*x1-.707*y;
return ImprovedNoise.noise(freq*x1 + 100, freq*y1, freq*z1);
}
// DIFFUSE REFLECTION
double diffuse(double normal[], double light[]) {
return Math.max(0, normal[0]*light[0] + normal[1]*light[1] + normal[2]*light[2]);
}
// SPECULAR REFLECTION (SPECIAL CASE, WHERE CAMERA IS ALWAYS IN (0,0,1) DIRECTION)
double r[] = {0,0,0};
double specular(double normal[], double light[], double power) {
r[0] = 2*normal[2]*normal[0];
r[1] = 2*normal[2]*normal[1];
r[2] = 2*normal[2]*normal[2]-normal[2];
return Math.pow(diffuse(r,light),power);
}
// COVERAGE OF ONE PIXEL BY A SMOOTH-EDGED DISK
double disk(double rr, double radius) {
double dd = rr - radius*radius;
return dd >= 2*radius ? 0 : dd <= -2*radius ? 1 : (2*radius - dd) / (4*radius);
}
// NORMALIZE THE LENGTH OF A VECTOR
void normalize(double v[]) {
double norm = Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
v[0] /= norm;
v[1] /= norm;
v[2] /= norm;
}
// ALLOW AN EXTERNAL CALLER TO SET THE SURFACE TYPE
public void setType(int t) { surfaceType = t; }
}