Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

perlin.C

Go to the documentation of this file.
00001 /*****************************************************************
00002 * perlin.C
00003 *****************************************************************/
00004 #include "gtex/gl_extensions.H"
00005 #include "gtex/util.H"
00006 
00007 #include "perlin.H"
00008 
00009 
00010 Perlin* Perlin:: _instance=0;
00011 
00012 
00013 //**************** Perlin Noise Generation ************
00014 
00015 
00016 Perlin::Perlin()
00017 {
00018 
00019 //the object should be created only once and shared between objects in jot
00020   if (_instance)
00021      cerr << " Warning: Perlin Texture Generator already exists !! " << endl;
00022 
00023   _previous_instance=_instance;
00024   _instance = this;
00025 
00026 
00027    perlin2d_tex=0;
00028    perlin3d_tex=0;
00029 }
00030 
00031 Perlin::~Perlin()
00032 {
00033    //will restore the static variable to previous instance
00034    //or just zero
00035    _instance = _previous_instance;
00036 
00037    //not deleteing the texture pointers on purpose
00038    //this is not a bug, things are allowed to hold 
00039    //on to teir textures after the texture generator has been 
00040    //deleted
00041 }
00042 
00043 /*
00044 The following code was inspired by this paper:
00045 http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
00046 by Hugo Elias
00047 
00048 I've changed the actual noise sampling code 
00049 in order to use cubic interpolation in higher dimensions, have good tiling
00050 and to have the code build on the lower dimension sampling functions
00051 
00052 My way can be quickly extended to 4D,5D etc..
00053 
00054 */
00055 
00056 //this is a more controlable random number generator
00057 //it will always produce the same random value for 
00058 //the same imput value, the built in rand() function
00059 //requires setting the seed value in a separate step
00060 
00061 double
00062 Perlin::noise(unsigned int input)
00063 {
00064    input = (input << 13) ^ input;
00065    return (1.0 - ((input * (input * input *15731 + 789221) + 1376312589) & 0x7fffffff) /1073741824.0);    
00066 }
00067 
00068 /*
00069 
00070 
00071 cubic interpolation between value v1 and v2
00072 t=[0..1] , requires v0 and v2 extending past 
00073 the two inner points, 
00074 
00075 example of a noise function
00076 
00077     v0**
00078    *    **v1*           *
00079   *          *        v3
00080               *     **
00081                v2*** 
00082 
00083 
00084 therefore for f=0 you will get v1
00085 and for f=1 you will get v2
00086 
00087 */
00088 
00089 double 
00090 Perlin::cubic(double v0, double v1, double v2, double v3, double t)
00091 {
00092    double P = (v3 - v2) - (v0 - v1);
00093    double Q = (v0 - v1) - P;
00094    double R = v2 - v0;
00095    double S = v1;
00096 
00097    return P*(t*t*t) + Q*(t*t) + R*t + S;
00098 }
00099 
00100 double
00101 Perlin::frac(double x)
00102 {
00103    return (x-int(x));
00104 }
00105 
00106 /* 
00107 
00108 The following functions return an interpolated noise value
00109 at a specific point for a given frequency.
00110 For a given octave the seed value must remain constant
00111 otherwise the function will return garbage.
00112 It is permissible (but not needed) to change the seed value
00113 between octaves.
00114 
00115 
00116 the interval is always [0..1]
00117 frequency is the number of random samples per interval
00118 frequency should nvever be smaller than 2 !!!
00119 
00120 */
00121 
00122 
00123 //1 dimensional perlin octave 
00124 //[0..1]
00125 double
00126 Perlin::getval_1D(int freq, double x, unsigned int seed)
00127 {
00128    x=x*freq; //rescale the interval in terms of the frequency
00129 
00130    //my trick here is to predictably change the 
00131    //the seed value from point to point but have this
00132    //value wrap around the [0..1] so it tiles nicely
00133 
00134    int c0 = (int(x-1)%freq) + seed;
00135    int c1 = (int(x)%freq) + seed;
00136    int c2 = (int(x+1)%freq) + seed;
00137    int c3 = (int(x+2)%freq) + seed;
00138 
00139    return cubic(noise(c0),noise(c1),noise(c2),noise(c3),frac(x));
00140 }
00141 
00142 //2 dimensional octave 
00143 //[0..1]x[0..1]
00144 
00145 double
00146 Perlin::getval_2D(int freq, double x, double y, unsigned int seed)
00147 {
00148    x=x*freq;
00149 
00150    unsigned int c0 = (int(x-1)%freq)*freq + seed;
00151    unsigned int c1 = (int(x)%freq)*freq + seed;
00152    unsigned int c2 = (int(x+1)%freq)*freq + seed;
00153    unsigned int c3 = (int(x+2)%freq)*freq + seed;
00154 
00155    double v0 = getval_1D(freq, y, c0);
00156    double v1 = getval_1D(freq, y, c1);
00157    double v2 = getval_1D(freq, y, c2);
00158    double v3 = getval_1D(freq, y, c3);
00159 
00160    return cubic(v0,v1,v2,v3,frac(x));
00161 }
00162 
00163 //3 dimensional octave
00164 //[0..1]x[0..1]x[0..1]
00165 
00166 double
00167 Perlin::getval_3D(int freq, double x, double y, double z, unsigned int seed)
00168 {
00169    x=x*freq;
00170    int fc = freq*freq;
00171 
00172    unsigned int c0 = (int(x-1)%freq)*fc + seed;
00173    unsigned int c1 = (int(x)%freq)*fc + seed;
00174    unsigned int c2 = (int(x+1)%freq)*fc + seed;
00175    unsigned int c3 = (int(x+2)%freq)*fc + seed;
00176 
00177    double v0 = getval_2D(freq, y, z, c0);
00178    double v1 = getval_2D(freq, y, z, c1);
00179    double v2 = getval_2D(freq, y, z, c2);
00180    double v3 = getval_2D(freq, y, z, c3);
00181 
00182    return cubic(v0,v1,v2,v3,frac(x));
00183 } 
00184 
00185 //4 dimensional octave
00186 //[0..1]x[0..1]x[0..1]x[0..1]
00187 
00188 double
00189 Perlin::getval_4D(int freq, double x, double y, double z, double t, unsigned int seed)
00190 {
00191    x=x*freq;
00192    int fc = freq*freq*freq;
00193 
00194    unsigned int c0 = (int(x-1)%freq)*fc + seed;
00195    unsigned int c1 = (int(x)%freq)*fc + seed;
00196    unsigned int c2 = (int(x+1)%freq)*fc + seed;
00197    unsigned int c3 = (int(x+2)%freq)*fc + seed;
00198 
00199    double v0 = getval_3D(freq, y, z, t, c0);
00200    double v1 = getval_3D(freq, y, z, t, c1);
00201    double v2 = getval_3D(freq, y, z, t, c2);
00202    double v3 = getval_3D(freq, y, z, t, c3);
00203 
00204    return cubic(v0,v1,v2,v3,frac(x));
00205 }
00206 
00207 
00208 
00209 /*
00210 Following functions create the texture and copy it to the
00211 OPEN GL texture. The system is allowed to choose 
00212 between video and main memory on its own
00213 
00214 These functions (especially 3D version)
00215 can be quite slow and should only be called once.
00216 I've inserted a safety mechanism that will create 
00217 the texture only once for any GTexture, all further calls will 
00218 do nothing. 
00219 
00220 I think that the idea of all the GTextures that use 
00221 perlin noise sharing one set of textures is a good one.
00222 So as soon something starts using either one it will be created and 
00223 shared with whatever else attempts to create them at a later time.
00224 Changing the seed values produces very similar looking results. 
00225 There is no need to realy tweak the values here. 
00226 I was totally convinced otherwise on monday before reading the Perlin paper.
00227 
00228 Also maybe it should be allowed to have 2d and 3d textures
00229 at the same time on different texture units.
00230 
00231 
00232 */
00233 
00234 
00235 //returns a procedurally generated 3d noise texture
00236 
00237 TEXTUREglptr
00238 Perlin::create_perlin_texture3(int tex_stage)
00239 {
00240 
00241    if (perlin3d_tex)
00242       return perlin3d_tex;
00243 
00244 
00245    //setup texture gl
00246    perlin3d_tex = new TEXTUREgl(GL_TEXTURE_3D,GL_TEXTURE0 + tex_stage); //bogus filename
00247    perlin3d_tex->set_format(GL_RGBA);
00248    perlin3d_tex->set_wrap_r(GL_REPEAT);
00249    perlin3d_tex->set_wrap_s(GL_REPEAT);
00250    perlin3d_tex->set_wrap_t(GL_REPEAT);
00251    perlin3d_tex->set_min_filter(GL_LINEAR);
00252    perlin3d_tex->set_max_filter(GL_LINEAR);
00253 
00254 
00255 
00256    cerr << "Creating 3D Perlin Noise Texture, stage : " << tex_stage << endl;
00257 
00258 
00259    //this value should not exceed 128
00260    //because of the memory and speed constraints
00261    int noise3DTexSize = 64;
00262    GLubyte *noise3DTexPtr; 
00263 
00264    int startFrequency = 4;
00265    int numOctaves = 4;
00266    int frequency = startFrequency;
00267    GLubyte *ptr;
00268    double amp = 64.0;
00269 
00270    int f, i, j, k, inc;
00271 
00272    if ((noise3DTexPtr = new GLubyte[noise3DTexSize*noise3DTexSize*
00273       noise3DTexSize* 4]) == NULL)
00274    {
00275       cerr << "Could not allocate Perlin Noise Texture" << endl;
00276    } 
00277    else
00278       cerr << "Texture memory allocated " << endl;
00279 
00280    //fill with "middle" value
00281    memset(noise3DTexPtr,128,noise3DTexSize*noise3DTexSize*
00282       noise3DTexSize* 4);
00283    ptr=noise3DTexPtr;
00284 
00285 
00286    cerr << "Generating the 3d noise, it's not crashed just taking its sweet time... " << endl;
00287 
00288    for (f = 0; f < numOctaves; f++) {
00289       inc=0;
00290       for(i = 0; i < noise3DTexSize; ++i) {
00291          for(j = 0; j < noise3DTexSize; ++j) {
00292             for(k = 0; k < noise3DTexSize; ++k) {
00293 
00294                //blue
00295                *(ptr+inc) += static_cast<uchar>(
00296                   amp*getval_3D(frequency, double(i)/double(noise3DTexSize),
00297                   double(j)/double(noise3DTexSize),
00298                   double(k)/double(noise3DTexSize), SEED_1));
00299 
00300                //green
00301                *(ptr+inc+1) += static_cast<uchar>(
00302                   amp*getval_3D(frequency, double(i)/double(noise3DTexSize),
00303                   double(j)/double(noise3DTexSize),
00304                   double(k)/double(noise3DTexSize), SEED_2));
00305 
00306 
00307                //red 
00308                *(ptr+inc+2) += static_cast<uchar>(
00309                   amp*getval_3D(frequency, double(i)/double(noise3DTexSize),
00310                   double(j)/double(noise3DTexSize),
00311                   double(k)/double(noise3DTexSize), SEED_3));
00312 
00313 
00314                //alpga
00315                *(ptr+inc+3) += static_cast<uchar>(
00316                   amp*getval_3D(frequency, double(i)/double(noise3DTexSize),
00317                   double(j)/double(noise3DTexSize),
00318                   double(k)/double(noise3DTexSize), SEED_4));
00319 
00320 
00321                inc+=4;
00322             }
00323          }
00324       }
00325       frequency=frequency*2;
00326       amp=amp/2;
00327    }
00328 
00329 
00330   
00331    perlin3d_tex->declare_texture();
00332    perlin3d_tex->apply_texture();
00333  
00334  
00335  glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA,
00336       noise3DTexSize, noise3DTexSize, noise3DTexSize, 
00337       0, GL_RGBA, GL_UNSIGNED_BYTE, noise3DTexPtr);
00338 
00339    cerr << "Texture copied to hardware " << endl;
00340 
00341    GL_VIEW::print_gl_errors("3D perlin noise setup D: ");
00342 
00343    delete[] noise3DTexPtr;
00344 
00345    return perlin3d_tex;
00346 
00347 }
00348 
00349 
00350 
00351 //returns a procedurally generated 2d noise texture
00352 TEXTUREglptr 
00353 Perlin::create_perlin_texture2(int tex_stage)
00354 {
00355 
00356    if(perlin2d_tex)
00357       return perlin2d_tex;
00358 
00359    //setup texture gl
00360    perlin2d_tex = new TEXTUREgl(GL_TEXTURE_2D,GL_TEXTURE0 + tex_stage);
00361    perlin2d_tex->set_format(GL_RGBA);
00362    perlin2d_tex->set_wrap_r(GL_REPEAT);
00363    perlin2d_tex->set_wrap_s(GL_REPEAT);
00364    perlin2d_tex->set_min_filter(GL_LINEAR);
00365    perlin2d_tex->set_max_filter(GL_LINEAR);
00366 
00367 
00368 
00369    cerr << "Creating 2D Perlin Noise Texture,  stage : " << tex_stage << endl;
00370 
00371   
00372    int noise2DTexSize = 256;
00373    GLubyte *noise2DTexPtr; 
00374 
00375    //noise control values
00376    //this is setup to create generic Perlin noise
00377    int startFrequency = 4;
00378    int numOctaves = 8;
00379    int frequency = startFrequency;
00380    GLubyte *ptr;
00381    int amp = 64;
00382 
00383 
00384    int f, i, j, inc;
00385 
00386 
00387    if ((noise2DTexPtr = new GLubyte[noise2DTexSize*noise2DTexSize*4]) == NULL) {
00388       cerr << "Could not allocate Perlin Noise Texture" << endl;
00389    } else
00390       cerr << "Texture memory allocated " << endl;
00391 
00392    //fill with middle value
00393    memset(noise2DTexPtr,128,noise2DTexSize*noise2DTexSize*4);
00394    ptr=noise2DTexPtr;
00395 
00396    cerr << "Generating 2D noise... " << endl;
00397 
00398    for (f = 0; f < numOctaves; f++) {
00399       inc=0;
00400       for(i = 0; i < noise2DTexSize; ++i) {
00401          for(j = 0; j < noise2DTexSize; ++j) {
00402 
00403             // Karol: the compiler warns about converting double to unsigned char.
00404             //        are you sure the values really are in the range [0,255]?
00405             //        --Lee
00406 
00407             // Prof : Yeah, the getval_2D returns numbers in the range -1..1 
00408             //         than it is multiplied by amplification factor and 
00409             //         added to the existing texture value.
00410             //         The texture value starts off as 128.
00411             //         In order to use the noise 0.5 must be subtracted 
00412             //         by the GLSL program.
00413             //         --Karol
00414             //         P.S. Sorry about the compiler warning :)
00415 
00416             //blue
00417             *(ptr+inc) += static_cast<uchar>(
00418                amp*getval_2D(frequency, double(i)/double(noise2DTexSize),
00419                double(j)/double(noise2DTexSize), SEED_1));
00420 
00421             //green
00422             *(ptr+inc+1) += static_cast<uchar>(
00423                amp*getval_2D(frequency, double(i)/double(noise2DTexSize),
00424                double(j)/double(noise2DTexSize), SEED_2));
00425 
00426             //red
00427             *(ptr+inc+2) += static_cast<uchar>(
00428                amp*getval_2D(frequency, double(i)/double(noise2DTexSize),
00429                double(j)/double(noise2DTexSize), SEED_3));
00430 
00431             //alpha
00432             *(ptr+inc+3) += static_cast<uchar>(
00433                amp*getval_2D(frequency, double(i)/double(noise2DTexSize),
00434                double(j)/double(noise2DTexSize), SEED_4));
00435 
00436             inc+=4;
00437          }
00438       }
00439       frequency=frequency*2;
00440       amp=amp/2;
00441    }
00442 
00443    perlin2d_tex->declare_texture();
00444    perlin2d_tex->apply_texture();
00445 
00446    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, noise2DTexSize, noise2DTexSize, 
00447       0, GL_RGBA, GL_UNSIGNED_BYTE, noise2DTexPtr);
00448 
00449    cerr << "Texture copied to hardware " << endl;
00450 
00451    return perlin2d_tex;
00452 }
00453 
00454 
00455 
00456 //these functions have been tested using visual inspection
00457 
00458 
00459 
00460 Vec4
00461 Perlin:: noise1(double x)
00462 {
00463    x = frac(x);
00464 
00465    Vec4 output(0.0,0.0,0.0,0.0);
00466    int freq= START_FREQ;
00467    double amp= 0.5;
00468 
00469    for(int octave=0; octave<NUM_OCTAVES; octave++)
00470    {
00471       output[0] += amp*getval_1D(freq,x,SEED_1);
00472       output[1] += amp*getval_1D(freq,x,SEED_2);
00473       output[2] += amp*getval_1D(freq,x,SEED_3);
00474       output[3] += amp*getval_1D(freq,x,SEED_4);
00475 
00476       amp = amp / double(PRESISTANCE);
00477       freq = freq * PRESISTANCE;
00478    }
00479 
00480    return output;
00481 }
00482 
00483 Vec4
00484 Perlin:: noise2(double x, double y)
00485 {
00486    x = frac(x);
00487    y = frac(y);
00488 
00489    Vec4 output(0.0,0.0,0.0,0.0);
00490    int freq= START_FREQ;
00491    double amp= 0.5;
00492 
00493    for(int octave=0; octave<NUM_OCTAVES; octave++)
00494    {
00495       output[0] += amp*getval_2D(freq,x,y,SEED_1);
00496       output[1] += amp*getval_2D(freq,x,y,SEED_2);
00497       output[2] += amp*getval_2D(freq,x,y,SEED_3);
00498       output[3] += amp*getval_2D(freq,x,y,SEED_4);
00499 
00500       amp = amp / double(PRESISTANCE);
00501       freq = freq * PRESISTANCE;
00502    }
00503 
00504    return output;
00505 }
00506 
00507 Vec4
00508 Perlin:: noise3(double x, double y, double z)
00509 {
00510    x = frac(x);
00511    y = frac(y);
00512    z = frac(z);
00513 
00514    Vec4 output(0.0,0.0,0.0,0.0);
00515    int freq= START_FREQ;
00516    double amp= 0.5;
00517 
00518    for(int octave=0; octave<NUM_OCTAVES; octave++)
00519    {
00520       output[0] += amp*getval_3D(freq,x,y,z,SEED_1);
00521       output[1] += amp*getval_3D(freq,x,y,z,SEED_2);
00522       output[2] += amp*getval_3D(freq,x,y,z,SEED_3);
00523       output[3] += amp*getval_3D(freq,x,y,z,SEED_4);
00524 
00525       amp = amp / double(PRESISTANCE);
00526       freq = freq * PRESISTANCE;
00527    }
00528 
00529    return output;
00530 }
00531 
00532 
00533 Vec4
00534 Perlin:: noise4(double x, double y, double z, double t)
00535 {
00536    x = frac(x);
00537    y = frac(y);
00538    z = frac(z);
00539    t = frac(t); 
00540 
00541    Vec4 output(0.0,0.0,0.0,0.0);
00542    int freq= START_FREQ;
00543    double amp= 0.5;
00544 
00545    for(int octave=0; octave<NUM_OCTAVES; octave++)
00546    {
00547       output[0] += amp*getval_4D(freq,x,y,z,t,SEED_1);
00548       output[1] += amp*getval_4D(freq,x,y,z,t,SEED_2);
00549       output[2] += amp*getval_4D(freq,x,y,z,t,SEED_3);
00550       output[3] += amp*getval_4D(freq,x,y,z,t,SEED_4);
00551 
00552       amp = amp / double(PRESISTANCE);
00553       freq = freq * PRESISTANCE;
00554    }
00555 
00556    return output;
00557 }

Generated on Mon Sep 18 11:39:32 2006 for jot by  doxygen 1.4.4