Skip to content

Commit

Permalink
Added separated image class and generator class
Browse files Browse the repository at this point in the history
can now generate a Perlin noise image that can re-blend r, g, and b
values upon request
  • Loading branch information
rydrman committed Jan 25, 2014
1 parent 36e290e commit d7d9ba4
Show file tree
Hide file tree
Showing 5 changed files with 430 additions and 0 deletions.
75 changes: 75 additions & 0 deletions Generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
function Generator()
{
this.perlin = new ClassicalNoise();
}

//generates and returns a seperated image using perlin noise
//w -> width
//h -> height
//blockSize -> number of screen pixels per color block
//noise detail (optional) -> smaller is smoother noise
Generator.prototype.generateBackground = function (w, h, blockSize, noiseDetail)
{
if( typeof(noiseDetail) == 'undefined' ) noiseDetail = 0.05;
img = new SeparatedImage(w, h);

//get drawing contexts for each image
var ctxBW = img.getCTX('bw'),
ctxR = img.getCTX('r'),
ctxG = img.getCTX('g'),
ctxB = img.getCTX('b');


//create empty image data for images
var dataBW = ctxBW.createImageData( w, h ),
dataR = ctxR.createImageData( w, h ),
dataG = ctxG.createImageData( w, h ),
dataB = ctxB.createImageData( w, h );

//fill image datas with noise
var pixel, byte, r, g, b;
for(var pY = 0; pY < h; pY += blockSize)
{
for(var pX = 0; pX < w; pX += blockSize)
{
r = 75 + 150 * this.perlin.noise( pX * 0.01, pY * 0.01 );
g = 75 + 150 * this.perlin.noise( pX * 0.01 + 1000, pY * 0.01 + 1000 );
b = 75 + 150 * this.perlin.noise( pX * 0.01 + 2000, pY * 0.01 + 2000 );

//do pixelsize x pixelsize
for(var oY = 0; oY < blockSize; oY++)
{
for(var oX = 0; oX < blockSize; oX++)
{
var nX = Math.min( pX + oX, w - 1 );
var nY = Math.min( pY + oY, h -1 );

pixel = nY * w + nX;
byte = pixel * 4;

dataR.data[byte] = r;
dataG.data[byte+1] = g;
dataB.data[byte+2] = b;

//greyscale
dataBW.data[byte] = dataBW.data[byte + 1] = dataBW.data[byte + 2] = (r+g+b)/3;

//alpha
dataBW.data[byte+3] = dataR.data[byte+3] = dataG.data[byte+3] = dataB.data[byte+3] = 255;

}
}
}
}

//set images
ctxBW.putImageData(dataBW, 0, 0);
ctxR.putImageData(dataR, 0, 0);
ctxG.putImageData(dataG, 0, 0);
ctxB.putImageData(dataB, 0, 0);

//return separated image
img.blend(1, 1, 1);
return img;
}

72 changes: 72 additions & 0 deletions SeparatedImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
function SeparatedImage(w, h)
{
//final composite image
this.imgComp = document.createElement('canvas');
this.ctxComp = this.imgComp.getContext('2d');

//black and white image
this.imgBW = document.createElement('canvas');
//this.ctxBW = imgMain.getContext('2d');

//canvas to blend on
this.imgBlend = document.createElement('canvas');
this.ctxBlend = this.imgBlend.getContext('2d');

//separate components
this.imgR = document.createElement('canvas');
this.imgG = document.createElement('canvas');
this.imgB = document.createElement('canvas');

//set sizes
this.w = this.imgComp.width = this.imgBlend.width = this.imgR.width = this.imgG.width = this.imgB.width = this.imgBW.width = w;
this.h = this.imgComp.height = this.imgBlend.height = this.imgR.height = this.imgG.height = this.imgB.height = this.imgBW.height = h;
}

//passes back the drawing context for the requested canvas
//possible values are r, g, b, and bw
SeparatedImage.prototype.getCTX = function(color)
{
switch(color)
{
case 'r':
return this.imgR.getContext('2d');
case 'g':
return this.imgG.getContext('2d');
case 'b':
return this.imgB.getContext('2d');
case 'bw':
return this.imgBW.getContext('2d');
}
}

//blends its r, g, and b components to the given percentages
//pass in values from 0 to 1
SeparatedImage.prototype.blend = function(r, g, b)
{
//draw black and white to composite
this.ctxComp.save();
this.ctxComp.drawImage(this.imgBW, 0, 0, this.w, this.h);

//blend r, g, b values
this.ctxBlend.save();
this.ctxBlend.clearRect(0, 0, this.imgBlend.width, this.imgBlend.height);

this.ctxBlend.globalCompositeOperation = 'screen';
this.ctxBlend.globalAlpha = r;
this.ctxBlend.drawImage(this.imgR, 0, 0, this.w, this.h);
this.ctxBlend.globalAlpha = g;
this.ctxBlend.drawImage(this.imgG, 0, 0, this.w, this.h);
this.ctxBlend.globalAlpha = b;
this.ctxBlend.drawImage(this.imgB, 0, 0, this.w, this.h);
this.ctxBlend.restore();

//blend onto composite image
this.ctxComp.globalCompositeOperation = 'color';
this.ctxComp.drawImage(this.imgBlend, 0, 0, this.w, this.h);
this.ctxComp.restore();
}

SeparatedImage.prototype.getImage = function()
{
return this.imgComp;
}
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

<html>
<head>
<script type="text/javascript" src="libs/perlin-noise-classical.js"></script>
<script type="text/javascript" src="libs/perlin-noise-simplex.js"></script>
<script type="text/javascript" src="SeparatedImage.js"></script>
<script type="text/javascript" src="Generator.js"></script>
<script type="text/javascript" src="character.js"></script>
<script type="text/javascript" src="particles.js"></script>
<script type="text/javascript" src="engine.js"></script>
Expand Down
100 changes: 100 additions & 0 deletions libs/perlin-noise-classical.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Ported from Stefan Gustavson's java implementation
// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
// Read Stefan's excellent paper for details on how this code works.
//
// Sean McCullough banksean@gmail.com

/**
* You can pass in a random number generator object if you like.
* It is assumed to have a random() method.
*/
var ClassicalNoise = function(r) { // Classic Perlin noise in 3D, for comparison
if (r == undefined) r = Math;
this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
this.p = [];
for (var i=0; i<256; i++) {
this.p[i] = Math.floor(r.random()*256);
}
// To remove the need for index wrapping, double the permutation table length
this.perm = [];
for(var i=0; i<512; i++) {
this.perm[i]=this.p[i & 255];
}
};

ClassicalNoise.prototype.dot = function(g, x, y, z) {
return g[0]*x + g[1]*y + g[2]*z;
};

ClassicalNoise.prototype.mix = function(a, b, t) {
return (1.0-t)*a + t*b;
};

ClassicalNoise.prototype.fade = function(t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
};

// Classic Perlin noise, 3D version
ClassicalNoise.prototype.noise = function(x, y, z) {
// Find unit grid cell containing point
var X = Math.floor(x);
var Y = Math.floor(y);
var Z = Math.floor(z);

// Get relative xyz coordinates of point within that cell
x = x - X;
y = y - Y;
z = z - Z;

// Wrap the integer cells at 255 (smaller integer period can be introduced here)
X = X & 255;
Y = Y & 255;
Z = Z & 255;

// Calculate a set of eight hashed gradient indices
var gi000 = this.perm[X+this.perm[Y+this.perm[Z]]] % 12;
var gi001 = this.perm[X+this.perm[Y+this.perm[Z+1]]] % 12;
var gi010 = this.perm[X+this.perm[Y+1+this.perm[Z]]] % 12;
var gi011 = this.perm[X+this.perm[Y+1+this.perm[Z+1]]] % 12;
var gi100 = this.perm[X+1+this.perm[Y+this.perm[Z]]] % 12;
var gi101 = this.perm[X+1+this.perm[Y+this.perm[Z+1]]] % 12;
var gi110 = this.perm[X+1+this.perm[Y+1+this.perm[Z]]] % 12;
var gi111 = this.perm[X+1+this.perm[Y+1+this.perm[Z+1]]] % 12;

// The gradients of each corner are now:
// g000 = grad3[gi000];
// g001 = grad3[gi001];
// g010 = grad3[gi010];
// g011 = grad3[gi011];
// g100 = grad3[gi100];
// g101 = grad3[gi101];
// g110 = grad3[gi110];
// g111 = grad3[gi111];
// Calculate noise contributions from each of the eight corners
var n000= this.dot(this.grad3[gi000], x, y, z);
var n100= this.dot(this.grad3[gi100], x-1, y, z);
var n010= this.dot(this.grad3[gi010], x, y-1, z);
var n110= this.dot(this.grad3[gi110], x-1, y-1, z);
var n001= this.dot(this.grad3[gi001], x, y, z-1);
var n101= this.dot(this.grad3[gi101], x-1, y, z-1);
var n011= this.dot(this.grad3[gi011], x, y-1, z-1);
var n111= this.dot(this.grad3[gi111], x-1, y-1, z-1);
// Compute the fade curve value for each of x, y, z
var u = this.fade(x);
var v = this.fade(y);
var w = this.fade(z);
// Interpolate along x the contributions from each of the corners
var nx00 = this.mix(n000, n100, u);
var nx01 = this.mix(n001, n101, u);
var nx10 = this.mix(n010, n110, u);
var nx11 = this.mix(n011, n111, u);
// Interpolate the four results along y
var nxy0 = this.mix(nx00, nx10, v);
var nxy1 = this.mix(nx01, nx11, v);
// Interpolate the two last results along z
var nxyz = this.mix(nxy0, nxy1, w);

return nxyz;
};
Loading

0 comments on commit d7d9ba4

Please sign in to comment.