Why does my Perlin Noise Generator make this squiggly pattern? - javascript
I've started learning about perlin noise generation, and I wanted to try to make my own generator in JavaScript. To get me started, I've been following along with this youtube tutorial. to try and copy their basic implementation. I've also been reading this article.
I've provided a jsFiddle of my implementation, which shows what I'm doing and what the output is. Instead of the smoothly-flowing, bubbling noise I see in the youtube tutorial, I get tightly-constricted black squiggles. Here's a picture:
Here's my generator code:
var p = [151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]
function generatePerlinNoise(x, y){
// Determine gradient cell corners
var xi = Math.floor(x) & 255;
var yi = Math.floor(y) & 255;
var g1 = p[p[xi] + yi];
var g2 = p[p[xi + 1] + yi];
var g3 = p[p[xi] + yi + 1];
var g4 = p[p[xi + 1] + yi + 1];
// Get point within gradient cell
var xf = x - Math.floor(x);
var yf = y - Math.floor(y);
// Get dot products at each corner of the gradient cell
var d1 = generateGradient(g1, xf, yf);
var d2 = generateGradient(g2, xf - 1, yf);
var d3 = generateGradient(g3, xf, yf - 1);
var d4 = generateGradient(g4, xf - 1, yf - 1);
var xFade = fade(xf);
var yFade = fade(yf);
var x1Interpolated = linearInterpolate(xFade, d1, d2);
var x2Interpolated = linearInterpolate(xFade, d3, d4);
var noiseValue = linearInterpolate(yFade, x1Interpolated, x2Interpolated);
return noiseValue;
}
function generateGradient(hash, x, y){
switch(hash & 3){
case 0: return x + y;
case 1: return -x + y;
case 2: return x - y;
case 3: return -x - y;
default: return 0;
}
}
// This is the fade function described by Ken Perlin
function fade(t){
return t * t * t * (t * (t * 6 - 15) + 10);
}
function linearInterpolate(amount, left, right){
return ((1-amount) * left + (amount * right))
}
I'm utilizing the generator function by dividing the pixel x and y values by my canvas height, and scaling by a frequency variable:
var freq = 12;
var noise = generatePerlinNoise((x/canvas.offsetHeight)*freq, (y/canvas.offsetHeight)*freq);
noise = Math.abs(noise);
I'm currently just using the noise value to generate a greyscale color value:
var blue = Math.floor(noise * 0xFF); // Scale 255 by our noise value,
// and take it's integer portion
var green = Math.floor(noise * 0xFF);
var red = Math.floor(noise * 0xFF);
data[i++] = red;
data[i++] = green;
data[i++] = blue;
data[i++] = 255;
The point of this for me is to learn more about noise generation and javascript. I've tried to think through the problem and made some observations:
There are no visible artifacts, so it seems like my fade function is working fine.
There don't seem to be any repeating patterns, so that's a good sign.
I go generate a complete range of values in the greyscale - not just black and white.
The general issue seems to be how the gradient at each pixel affects its neighbors: Mine seem to clump together in snake-like ropes of fixed widths. It seems like the gradient vector options supplied and the permutation table used to randomly-ish select them would govern this, but mine are an exact copy from the tutorial: The same 4 vectors each pointing into a quadrant at 45 degrees, and the standard permutation table.
This leaves me stumped as to figuring out what the cause is. My general suspicions boil down to:
I've messed up the algorithm somewhere in a subtle way that I keep overlooking. (Most likely)
There's a subtle difference in the way JavaScript does something that i'm overlooking. (Maybe)
I'm generating noise correctly, but incorrectly applying the result to the RGB values used in my canvas image data. (Least likely)
I'd really like to get to the bottom of this. Thanks in advance for your help! :)
Also: I DO think this pattern is cool, and this is a learning exercise, so if anyone can share insight into why I'm getting this pattern specifically, that'd be great!
Related
CSS contrast filter
How does css filter contrast will work ? Is there a formula? I want to reproduce in javascript and I need a formula. For example css filter brightness(2) take each pixel and multiply by 2, but for contrast I don't have any idea Thanks
Multiply by 2 is a contrast filter. All multiplication and division of an images RGB values affects the contrast. The function I like to use is a exponential ease function where the power controls the contrast. function contrastPixel(r,g,b,power) { r /= 255; // normalize channels g /= 255; b /= 255; var rr = Math.pow(r,power); // raise each to the power var gg = Math.pow(r,power); var bb = Math.pow(r,power); r = Math.floor((rr / (rr + Math.pow(1 - r, power)))*255); g = Math.floor((gg / (gg + Math.pow(1 - g, power)))*255); b = Math.floor((bb / (bb + Math.pow(1 - b, power)))*255); return {r,g,b}; } Using it var dat = ctx.getPixelData(0,0,100,100); var data = dat.data; var i = 0; while(i < data.length){ var res = contrastPixel(data[i],data[i+1],data[i+2],power); data[i++] = res.r; data[i++] = res.g; data[i++] = res.b; i++; } ctx.putImageData(dat,0,0); The argument power controls the contrast. power = 1; // no change to the image 0 < power < 1; // reduces contrast 1 < power; // increases contrast Because the scaling of power is logarithmic it can be hard to control with a linear slider. To give the slider a linear feel use the following instructions to get a value from a slider For a slider with a min -100 and max 100 and center 0 (0 being no contrast change) get the contrast power value using power = Math.pow(((Number(slider.value)* 0.0498) + 5)/5,Math.log2(10)); It's not perfectly linear, and the range is limited but will cover most needs. The test image shows the results. Center bottom is the original. Using the scale in the paragraph above from left to right slider values of -100, -50, 50, 100
what ist the correct Oculus Rift barrel distortion radius function?
i want to implement the barrel shader for the oculus rift in javascript. according to this video (http://youtu.be/B7qrgrrHry0?t=11m26s) the radius function for barrel distortion is: newr = 0.24*r^4+0.22*r^2+1 The result: Reference Image: After Shader: if i change the function to newr = r i get the orginal image. If I set the function to: newr = 0.022*r^2 i get: This one is close but not the right solution (tested with oculus) So its not the fault of the programm...the radius function is the problem. Here you can try it in a fiddle: http://jsfiddle.net/s175ozts/2/ Why does the orginal function not work?? thanks :)
After trying a lot of stuff... i finally got the solution. The trick was to normalize r first and then multiply the barrelfunction with orginal r var sf = pr / rMax; //Scaling factor var newR = pr*(0.24*Math.pow(sf,4)+0.22*Math.pow(sf,2)+1); //barrel distortion function See fiddle here: http://jsfiddle.net/s175ozts/4/ Result:
Cracker0dks#: I was playing a little bit with your fiddle, and optimized it significantly: http://jsfiddle.net/mqau9ytv/2/ /*int*/ var x_off = xmid-x; /*int*/ var y_off = ymid-y; /*int*/ var pr2 = x_off*x_off + y_off*y_off; //radius from pixel to pic mid, squared. /*float*/ var sf2 = pr2 * rMax2_inv; // Scaling factor squared. /*float*/ var scale = zoom *(0.24*sf2*sf2 + 0.22*sf2 + 1.0); //barrel distortion function /*float*/ var new_x_off = scale * x_off; /*float*/ var newx = xmid - new_x_off; /*float*/ var new_y_off = scale * y_off; /*float*/ var newy = ymid - new_y_off; It could probably be optimized even more, like not actually producing some of the variables, like new_y_off, or pix2D temporary array. Not that it matters a lot in this situation, but still it is useful thing.
Apply gravity between two or more objects in HTML5 Canvas
I was creating something like a 2d gravity simulator, just for fun, and noticed that I'm a complete idiot in terms of math. I just can't get the gravity to work. I've tried following the instructions found here but it looks weird and when the distance reaches zero, it goes completely buggy. If I add 1 to the distance as recommended in the question, all objects go upper left. I've even tried not modifying gravity when distances reach zero, but this doesn't change the behavior. Here's the algorithm I'm using to apply gravity: var distX = obj1.x - obj2.x, distY = obj1.y - obj2.y; if (obj1 != obj2) { if (distY != 0) { obj1.vy += -(1 / (distY)); } if (distX != 0) { obj1.vx += -(1 / (distX)); } } I've tried using other algorithms too, but most of them don't care for the distance between objects. Note that I want the gravity to affect distant objects less than closer objects.
Instead of solving any equations we could use an approximation. dv/dt = G*M*m/r^2, but for small t we could use the approximation Δv = (G*M*m/r^2)*Δt. When the objects collide I have implemented perfectly inelastic collision (see Wikipedia). This prevents the distance between two objects from being to small and therefore the maximum force is limited. I also moved the part of the code where the object's position is changed to a separate loop, so the forces calculated for obj1 and obj2 are equal in size. Demo function tick() { allObjs.forEach(function (obj1) { allObjs.forEach(function (obj2) { var diffX = obj2.x - obj1.x, var diffY = obj2.y - obj1.y; var distSquare = diffX*diffX + diffY*diffY var dist = Math.sqrt(distSquare); if (obj1 != obj2) { if (dist > obj1.w/2 + obj2.w/2) { //If you add mass to the objects change to obj2.mass //instead of 50 var totalForce = 50/distSquare; obj1.vx += totalForce * diffX / dist; obj1.vy += totalForce * diffY / dist; } else { //Collision has occurred //If you add mass to the objects change to //tempX = (obj1.mass*obj1.vx + obj2.mass*obj2.vx)/(obj1.mass+ //obj2.mass); //tempY = (obj1.mass*obj1.vy + obj2.mass*obj2.vy)/(obj1.mass+ //obj2.mass); var tempX = (obj1.vx + obj2.vx)/2; var tempY = (obj1.vy + obj2.vy)/2; obj1.vx = tempX; obj2.vx = tempX; obj1.vy = tempY; obj2.vy = tempY; } } }); }); allObjs.forEach(function (obj1) { obj1.x += obj1.vx / 25; obj1.y += obj1.vy / 25; }); stage.update(); }
Try var distX = obj1.x - obj2.x, distY = obj1.y - obj2.y; var rsq = distX *distX + distY * distY; var r = Math.sqrt(rsq); var F = 50 / rsq; // constant chosen to be pleasing var rhat_x = distX / r; var rhat_y = distY / r; var Fx = F * rhat_x; var Fy = F * rhat_y; obj1.vx += -Fx; obj1.vy += -Fy; obj2.vx += Fx; obj2.vy += Fy; This is very basic, its not taking mass into account its using the simplest possible way of solving the equations you should really use something like 5th order Runga-Kutta w/ error correction. But it does use the formula for gravitational F = - G m1 m2 / r^2 where G is the universal gravitational constant, m1 m2 are the two masses (I've all of these to 1!) r^2 is the square of the distance between the objects. The force is in the direction to the other object, let this be a unit vector rhat so the vector version of the force, using 1 for the constants F = - ( 1 / r^2 ) rhat The above gives reasonable results it you start out with createPlanet(50, 200, 2, 0, 1); createPlanet(400, 200, 2, 0, -1); you have to take care that the two planets don't get too close or the acceleration goes off to infinity and the velocities get too big. While playing around I tried var distX = obj1.x - obj2.x, distY = obj1.y - obj2.y; var rsq = distX *distX + distY * distY; // square of the distance var r = Math.sqrt(rsq); var Fx = distX / r; var Fy = distY / r; obj1.vx += -Fx; obj1.vy += -Fy; obj2.vx += Fx; obj2.vy += Fy; which gives pleasing but physically incorrect results.
Newton's equations of motion F = ma need to be solved here. You are not doing anything like that in your code. No wonder it isn't matching your intuition. It would help to understand the physics. This is a vector equation. The force is gravity, which follows an inverse distance squared law. You also know how acceleration, velocity, and displacement are related. You have to know calculus. For your 2D world, that means six equations for each body in the problem. Two bodies means 12 coupled equations. Solving these equations means integrating all those coupled ordinary differential equations in time. You'll need to know something about numerical methods (e.g. Runga-Kutta 5th order integration w/ error correction). You'd have a lot to learn to write such a thing yourself. I'd recommend looking into a JavaScript physics library like Box2D or something else that Google might find.
Mirroring right half of webcam image
I saw that you have helped David with his mirroring canvas problem before. Canvas - flip half the image I have a similar problem and hope that maybe you could help me. I want to apply the same mirror effect on my webcam-canvas, but instead of the left side, I want to take the RIGHT half of the image, flip it and apply it to the LEFT. This is the code you've posted for David. It also works for my webcam cancas. Now I tried to change it, so that it works for the other side, but unfortunately I'm not able to get it. for(var y = 0; y < height; y++) { for(var x = 0; x < width / 2; x++) { // divide by 2 to only loop through the left half of the image. var offset = ((width* y) + x) * 4; // Pixel origin // Get pixel var r = data[offset]; var g = data[offset + 1]; var b = data[offset + 2]; var a = data[offset + 3]; // Calculate how far to the right the mirrored pixel is var mirrorOffset = (width - (x * 2)) * 4; // Get set mirrored pixel's colours data[offset + mirrorOffset] = r; data[offset + 1 + mirrorOffset] = g; data[offset + 2 + mirrorOffset] = b; data[offset + 3 + mirrorOffset] = a; } }
Even if the accepted answer you're relying on uses imageData, there's absolutely no use for that. Canvas allows, with drawImage and its transform (scale, rotate, translate), to perform many operations, one of them being to safely copy the canvas on itself. Advantages is that it will be way easier AND way way faster than handling the image by its rgb components. I'll let you read the code below, hopefully it's commented and clear enough. The fiddle is here : http://jsbin.com/betufeha/2/edit?js,output One output example - i took also a mountain, a Canadian one :-) - : Original : Output : html <canvas id='cv'></canvas> javascript var mountain = new Image() ; mountain.onload = drawMe; mountain.src = 'http://www.hdwallpapers.in/walls/brooks_mountain_range_alaska-normal.jpg'; function drawMe() { var cv=document.getElementById('cv'); // set the width/height same as image. cv.width=mountain.width; cv.height = mountain.height; var ctx=cv.getContext('2d'); // first copy the whole image. ctx.drawImage(mountain, 0, 0); // save to avoid messing up context. ctx.save(); // translate to the middle of the left part of the canvas = 1/4th of the image. ctx.translate(cv.width/4, 0); // flip the x coordinates to have a mirror effect ctx.scale(-1,1); // copy the right part on the left part. ctx.drawImage(cv, /*source */ cv.width/2,0,cv.width/2, cv.height, /*destination*/ -cv.width/4, 0, cv.width/2, cv.height); // restore context ctx.restore(); }
KinectJS: Algorithm required to determine new X,Y coords after image resize
BACKGROUND: The app allows users to upload a photo of themselves and then place a pair of glasses over their face to see what it looks like. For the most part, it is working fine. After the user selects the location of the 2 pupils, I auto zoom the image based on the ratio between the distance of the pupils and then already known distance between the center points of the glasses. All is working fine there, but now I need to automatically place the glasses image over the eyes. I am using KinectJS, but the problem is not with regards to that library or javascript.. it is more of an algorithm requirement WHAT I HAVE TO WORK WITH: Distance between pupils (eyes) Distance between pupils (glasses) Glasses width Glasses height Zoom ratio SOME CODE: //.. code before here just zooms the image, etc.. //problem is here (this is wrong, but I need to know what is the right way to calculate this) var newLeftEyeX = self.leftEyePosition.x * ratio; var newLeftEyeY = self.leftEyePosition.y * ratio; //create a blue dot for testing (remove later) var newEyePosition = new Kinetic.Circle({ radius: 3, fill: "blue", stroke: "blue", strokeWidth: 0, x: newLeftEyeX, y: newLeftEyeY }); self.pointsLayer.add(newEyePosition); var glassesWidth = glassesImage.getWidth(); var glassesHeight = glassesImage.getHeight(); // this code below works perfect, as I can see the glasses center over the blue dot created above newGlassesPosition.x = newLeftEyeX - (glassesWidth / 4); newGlassesPosition.y = newLeftEyeY - (glassesHeight / 2); NEEDED A math genius to give me the algorithm to determine where the new left eye position should be AFTER the image has been resized UPDATE After researching this for the past 6 hours or so, I think I need to do some sort of "translate transform", but the examples I see only allow setting this by x and y amounts.. whereas I will only know the scale of the underlying image. Here's the example I found (which cannot help me): http://tutorials.jenkov.com/html5-canvas/transformation.html and here is something which looks interesting, but it is for Silverlight: Get element position after transform Is there perhaps some way to do the same in Html5 and/or KinectJS? Or perhaps I am going down the wrong road here... any ideas people? UPDATE 2 I tried this: // if zoomFactor > 1, then picture got bigger, so... if (zoomFactor > 1) { // if x = 10 (for example) and if zoomFactor = 2, that means new x should be 5 // current x / zoomFactor => 10 / 2 = 5 newLeftEyeX = self.leftEyePosition.x / zoomFactor; // same for y newLeftEyeY = self.leftEyePosition.y / zoomFactor; } else { // else picture got smaller, so... // if x = 10 (for example) and if zoomFactor = 0.5, that means new x should be 20 // current x * (1 / zoomFactor) => 10 * (1 / 0.5) = 10 * 2 = 20 newLeftEyeX = self.leftEyePosition.x * (1 / zoomFactor); // same for y newLeftEyeY = self.leftEyePosition.y * (1 / zoomFactor); } that didn't work, so then I tried an implementation of Rody Oldenhuis' suggestion (thanks Rody): var xFromCenter = self.leftEyePosition.x - self.xCenter; var yFromCenter = self.leftEyePosition.y - self.yCenter; var angle = Math.atan2(yFromCenter, xFromCenter); var length = Math.hypotenuse(xFromCenter, yFromCenter); var xNew = zoomFactor * length * Math.cos(angle); var yNew = zoomFactor * length * Math.sin(angle); newLeftEyeX = xNew + self.xCenter; newLeftEyeY = yNew + self.yCenter; However, that is still not working as expected. So, I am not sure what the issue is currently. If anyone has worked with KinectJS before and has an idea of what the issue may be, please let me know. UPDATE 3 I checked Rody's calculations on paper and they seem fine, so there is obviously something else here messing things up.. I got the coordinates of the left pupil at zoom factors 1 and 2. With those coordinates, maybe someone can figure out what the issue is: Zoom Factor 1: x = 239, y = 209 Zoom Factor 2: x = 201, y = 133
OK, since it's an algorithmic question, I'm going to keep this generic and only write pseudo code. I f I understand you correctly, What you want is the following: Transform all coordinates such that the origin of your coordinate system is at the zoom center (usually, central pixel) Compute the angle a line drawn from this new origin to a point of interest makes with the positive x-axis. Compute also the length of this line. The new x and y coordinates after zooming are defined by elongating this line, such that the new line is the zoom factor times the length of the original line. Transform the newly found x and y coordinates back to a coordinate system that makes sense to the computer (e.g., top left pixel = 0,0) Repeat for all points of interest. In pseudo-code (with formulas): x_center = image_width/2 y_center = image_height/2 x_from_zoom_center = x_from_topleft - x_center y_from_zoom_center = y_from_topleft - y_center angle = atan2(y_from_zoom_center, x_from_zoom_center) length = hypot(x_from_zoom_center, y_from_zoom_center) x_new = zoom_factor * length * cos(angle) y_new = zoom_factor * length * sin(angle) x_new_topleft = x_new + x_center y_new_topleft = y_new + y_center Note that this assumes the number of pixels used for length and width stays the same after zooming. Note also that some rounding should take place (keep everything double precision, and only round to int after all calculations) In the code above, atan2 is the four-quadrant arctangent, available in most programming languages, and hypot is simply sqrt(x*x + y*y), but then computed more carefully (e.g., to avoid overflow etc.), also available in most programing languages. Is this indeed what you were after?