/* Terrain ------- This algorithm generates a cloud texture as height map, computes a normal map, and then shades it. Author : Andreas Jonsson Created: April 17th, 2005 Updated: July 28th, 2005 Texture Generator version 1.1.0 */ void boxBlurAlpha(image @img, int bw, int bh) { int x, y; int w = img.width; int h = img.height; //-------------- // smooth x float[] line(w); y = 0; while( y < h ) { // Copy original pixels to the line buffer x = 0; while( x < w ) { line[x] = img.Alpha(x, y); ++x; } // Compute sum of first bw pixels x = 0; float sum = 0.0; while( x < bw ) { sum += line[x]; ++x; } // Set blurred pixels x = 0; while( x < w ) { img.Alpha((x+bw/2)%w, y) = sum/float(bw); sum -= line[x]; sum += line[(x+bw)%w]; ++x; } ++y; } //--------------- // smooth y line = float[](h); x = 0; while( x < w ) { // Copy original pixels to the line buffer y = 0; while( y < h ) { line[y] = img.Alpha(x, y); ++y; } // Compute sum of first bw pixels y = 0; float sum = 0.0; while( y < bh ) { sum += line[y]; ++y; } // Set blurred pixels y = 0; while( y < h ) { img.Alpha(x, (y+bh/2)%h) = sum/float(bh); sum -= line[y]; sum += line[(y+bh)%h]; ++y; } ++x; } } // A global randomizer randomizer rnd; void addRandom(image @img, float amp) { int x, y; int w = img.width; int h = img.height; x = 0; while( x < w ) { y = 0; while( y < h ) { float r = amp*(2.0*rnd.getNumber()-1.0); img.Alpha(x, y) = r + img.Alpha(x, y); ++y; } ++x; } } image @cloud(int start, int octaves, float ampMod) { image @img = @image(start, start); float amp = 1.0; addRandom(@img, amp); while( --octaves >= 0 ) { img.Resize(img.width*2, img.height*2); boxBlurAlpha(@img, 3, 3); amp *= ampMod; addRandom(@img, amp); } // blur the image one last time boxBlurAlpha(@img, 3, 3); return @img; } void normalize(image @img) { int x, y; int w = img.width; int h = img.height; // find the highest and lowest values used float hi = 0.0, lo = 1.0; y = 0; while( y < h ) { x = 0; while( x < w ) { float v = img.Alpha(x, y); if( v < lo ) lo = v; if( v > hi ) hi = v; ++x; } ++y; } // normalize the image y = 0; hi = 1.0/(hi-lo); while( y < h ) { x = 0; while( x < w ) { float v = img.Alpha(x, y); v = (v-lo)*hi; img.Alpha(x, y) = v; ++x; } ++y; } } image @normalMap(image @hmap) { int w = hmap.width; int h = hmap.height; image @nmap = @image(w, h); int x, y = 0; while( y < h ) { x = 0; while( x < w ) { // Get two vectors passing through pixel float hxp = hmap.Alpha((x + 1)%w, y); float hyp = hmap.Alpha(x, (y + 1)%h); float hxm = hmap.Alpha((x + w - 1)%w, y); float hym = hmap.Alpha(x, (y + h - 1)%h); // Compute the cross product of the two vectors float nx = -2.0*(hyp - hym); float ny = -(hxp - hxm)*2.0; float nz = 2.0*2.0; // Normalize float norm = 1.0/sqrt(nx*nx + ny*ny + nz*nz); nmap.Pixel(x,y) = pixel(nx*norm, ny*norm, nz*norm); x++; } y++; } return @nmap; } void scale(image @img, float s) { int x, y; int w = img.width; int h = img.height; y = 0; while( y < h ) { x = 0; while( x < w ) { float v = img.Alpha(x, y); img.Alpha(x, y) = v*s; ++x; } ++y; } } void shadeNormal(image @nmap, float lx, float ly, float lz) { int w = nmap.width; int h = nmap.height; // Normalize light vector float m = 1.0/sqrt(lx*lx + ly*ly + lz*lz); lx *= m; ly *= m; lz *= m; float nx = 0, ny = 0, nz = 0; int x, y; y = 0; while( y < w ) { x = 0; while( x < h ) { // Dot product pixel with light vector pixel p = nmap.Pixel(x,y); nx = p.r; ny = p.g; nz = p.b; float i = lx*nx + ly*ny + lz*nz; nmap.Pixel(x,y) = pixel(i,i,i); x++; } y++; } } void main() { image @img = @cloud(2, 7, 0.5); normalize(@img); img.Show(); scale(@img, float(img.width)); image @nmap = @normalMap(@img); nmap.Show(); shadeNormal(@nmap, 0.5, 0.5, 2.5); nmap.Show(); }