/*
Simple examples of procedural bump textures. - Ken Perlin */ public class Sphere2 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; } }