I created a simple perlin noise generator using p5.js, which is based on
this link
. For the most part, I got 80% of the algorithm working. The only issue is that there is defined discontinuities aligned with each gradient vector. the code for this project is shown below.
// noprotect
var screen_width = 400;
var screen_height = 400;
var res = 2;
var gvecs = {};
function setup() {
createCanvas(screen_width, screen_height);
initialize_gvecs();
draw_perlin_noise();
}
function draw() {
console.log(gvecs);
noLoop();
}
function initialize_gvecs() {
for (var y = 0; y <= res; y++) {
for (var x = 0; x <= res; x++) {
let theta = Math.random() * 2 * Math.PI;
gvecs[[x, y]] = {
x: Math.cos(theta),
y: Math.sin(theta)
};
}
}
}
function draw_perlin_noise() {
loadPixels();
for (var y = 0; y < screen_height; y++) {
for (var x = 0; x < screen_width; x++) {
var world_coordx = map(x, 0, screen_width, 0, res);
var world_coordy = map(y, 0, screen_height, 0, res);
var top_L_x = Math.floor(world_coordx);
var top_L_y = Math.floor(world_coordy);
var top_R_x = top_L_x + 1;
var top_R_y = top_L_y;
var bottom_L_x = top_L_x;
var bottom_L_y = top_L_y + 1;
var bottom_R_x = top_L_x + 1;
var bottom_R_y = top_L_y + 1;
var top_L_g = gvecs[[top_L_x, top_L_y]];
var top_R_g = gvecs[[top_R_x, top_R_y]];
var bottom_L_g = gvecs[[bottom_L_x, bottom_L_y]];
var bottom_R_g = gvecs[[bottom_R_x, bottom_R_y]];
var btw_top_L = {
x: world_coordx - top_L_x,
y: world_coordy - top_L_y
};
var btw_top_R = {
x: world_coordx - top_R_x,
y: world_coordy - top_R_y
};
var btw_bottom_L = {
x: world_coordx - bottom_L_x,
y: world_coordy - bottom_L_y
};
var btw_bottom_R = {
x: world_coordx - bottom_R_x,
y: world_coordy - bottom_R_y
};
var v = top_L_g.x * btw_top_L.x + top_L_g.y * btw_top_L.y;
var u = top_R_g.x * btw_top_R.x + top_R_g.y * btw_top_R.y;
var s = bottom_L_g.x * btw_bottom_L.x + bottom_L_g.y * btw_bottom_L.y;
var t = bottom_R_g.x * btw_bottom_R.x + bottom_R_g.y * btw_bottom_R.y;
var Sx = ease_curve(world_coordx - top_L_x);
var a = s + Sx * (t - s);
var b = u + Sx * (v - u);
var Sy = ease_curve(world_coordy - top_L_y);
var final_val = a + Sy * (b - a);
pixels[(x + y * screen_width) * 4] = map(final_val, -1, 1, 0, 255);
pixels[(x + y * screen_width) * 4 + 1] = map(final_val, -1, 1, 0, 255);
pixels[(x + y * screen_width) * 4 + 2] = map(final_val, -1, 1, 0, 255);
pixels[(x + y * screen_width) * 4 + 3] = 255;
}
}
updatePixels();
}
function ease_curve(x) {
return 6 * x ** 5 - 15 * x ** 4 + 10 * x ** 3;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.5.0/lib/p5.js"></script>
an image of the issue I'm having is shown below.
I suspect that my issue has to do with the value between each gradient vector not properly using the adjacent gradient vectors, but I've tested and debugged this extensively and I cant find the issue. I've also tried several different ease_curve functions, but none of them seem to change anything. Any help would be greatly appreciated.
Bilinear interpolation might be twisted.
var Sx = ease_curve(world_coordx - top_L_x);
var a = s + Sx * (t - s);
//var b = u + Sx * (v - u);
var b = v + Sx * (u - v);
var Sy = ease_curve(world_coordy - top_L_y);
//var final_val = a + Sy * (b - a);
var final_val = b + Sy * (a - b);
Related
I'm having really bad performance on a project i wrote in Javascript (with the p5.js library)
Here is the code:
const fps = 60;
const _width = 400;
const _height = 300;
const firePixelChance = 1;
const coolingRate = 1;
const heatSourceSize = 10;
const noiseIncrement = 0.02;
const fireColor = [255, 100, 0, 255];
const bufferWidth = _width;
const bufferHeight = _height;
let buffer1;
let buffer2;
let coolingBuffer;
let ystart = 0.0;
function setup() {
createCanvas(_width, _height);
frameRate(fps);
buffer1 = createGraphics(bufferWidth, bufferHeight);
buffer2 = createGraphics(bufferWidth, bufferHeight);
coolingBuffer = createGraphics(bufferWidth, bufferHeight);
}
// Draw a line at the bottom
function heatSource(buffer, rows, _color) {
const start = bufferHeight - rows;
for (let x = 0; x < bufferWidth; x++) {
for (let y = start; y < bufferHeight; y++) {
if(Math.random() >= firePixelChance)
continue;
buffer.pixels[(x + (y * bufferWidth)) * 4] = _color[0]; // Red
buffer.pixels[(x + (y * bufferWidth)) * 4 +1] = _color[1]; // Green
buffer.pixels[(x + (y * bufferWidth)) * 4 +2] = _color[2]; // Blue
buffer.pixels[(x + (y * bufferWidth)) * 4 +3] = 255; // Alpha
}
}
}
// Produces the 'smoke'
function coolingMap(buffer){
let xoff = 0.0;
for(x = 0; x < bufferWidth; x++){
xoff += noiseIncrement;
yoff = ystart;
for(y = 0; y < bufferHeight; y++){
yoff += noiseIncrement;
n = noise(xoff, yoff);
bright = pow(n, 3) * 20;
buffer.pixels[(x + (y * bufferWidth)) * 4] = bright;
buffer.pixels[(x + (y * bufferWidth)) * 4 +1] = bright;
buffer.pixels[(x + (y * bufferWidth)) * 4 +2] = bright;
buffer.pixels[(x + (y * bufferWidth)) * 4 +3] = bright;
}
}
ystart += noiseIncrement;
}
// Change color of a pixel so it looks like its smooth
function smoothing(buffer, _buffer2, _coolingBuffer) {
for (let x = 0; x < bufferWidth; x++) {
for (let y = 0; y < bufferHeight; y++) {
// Get all 4 neighbouring pixels
const left = getColorFromPixelPosition(x+1,y,buffer.pixels);
const right = getColorFromPixelPosition(x-1,y,buffer.pixels);
const bottom = getColorFromPixelPosition(x,y+1,buffer.pixels);
const top = getColorFromPixelPosition(x,y-1,buffer.pixels);
// Set this pixel to the average of those neighbours
let sumRed = left[0] + right[0] + bottom[0] + top[0];
let sumGreen = left[1] + right[1] + bottom[1] + top[1];
let sumBlue = left[2] + right[2] + bottom[2] + top[2];
let sumAlpha = left[3] + right[3] + bottom[3] + top[3];
// "Cool down" color
const coolingMapColor = getColorFromPixelPosition(x,y,_coolingBuffer.pixels)
sumRed = (sumRed / 4) - (Math.random() * coolingRate) - coolingMapColor[0];
sumGreen = (sumGreen / 4) - (Math.random() * coolingRate) - coolingMapColor[1];
sumBlue = (sumBlue / 4) - (Math.random() * coolingRate) - coolingMapColor[2];
sumAlpha = (sumAlpha / 4) - (Math.random() * coolingRate) - coolingMapColor[3];
// Make sure we dont get negative numbers
sumRed = sumRed > 0 ? sumRed : 0;
sumGreen = sumGreen > 0 ? sumGreen : 0;
sumBlue = sumBlue > 0 ? sumBlue : 0;
sumAlpha = sumAlpha > 0 ? sumAlpha : 0;
// Update this pixel
_buffer2.pixels[(x + ((y-1) * bufferWidth)) * 4] = sumRed; // Red
_buffer2.pixels[(x + ((y-1) * bufferWidth)) * 4 +1] = sumGreen; // Green
_buffer2.pixels[(x + ((y-1) * bufferWidth)) * 4 +2] = sumBlue; // Blue
_buffer2.pixels[(x + ((y-1) * bufferWidth)) * 4 +3] = sumAlpha; // Alpha
}
}
}
function draw() {
background(0);
text("FPS: "+Math.floor(frameRate()), 10, 20);
fill(0,255,0,255);
buffer1.loadPixels();
buffer2.loadPixels();
coolingBuffer.loadPixels();
heatSource(buffer1, heatSourceSize, fireColor);
coolingMap(coolingBuffer);
smoothing(buffer1, buffer2, coolingBuffer);
buffer1.updatePixels();
buffer2.updatePixels();
coolingBuffer.updatePixels();
let temp = buffer1;
buffer1 = buffer2;
buffer2 = temp;
image(buffer2, 0, 0); // Draw buffer to screen
// image(coolingBuffer, 0, bufferHeight); // Draw buffer to screen
}
function mousePressed() {
buffer1.fill(fireColor);
buffer1.noStroke();
buffer1.ellipse(mouseX, mouseY, 100, 100);
}
function getColorFromPixelPosition(x, y, pixels) {
let _color = [];
for (let i = 0; i < 4; i++)
_color[i] = pixels[(x + (y * bufferWidth)) * 4 + i];
return _color;
}
function getRandomColorValue() {
return Math.floor(Math.random() * 255);
}
I'm getting ~12 FPS on chrome and ~1 FPS on any other browser and i cant figure out why..
Resizing my canvas to make it bigger also impacts the fps negatively...
In the devtools performance tab i noticed that both my smoothing and coolingMap functions are the things slowing it down, but i cant figure out what part of them are so heavy..
You've pretty much answered this for yourself already:
i'm starting to think this is normal and i should work on caching stuff and maybe use pixel groups instead of single pixels
Like you're discovering, doing some calculation for every single pixel is pretty slow. Computers only have finite resources, and there's going to be a limit to what you can throw at them.
In your case, you might consider drawing the whole thing to a canvas once at startup, and then moving the canvas up over the life of the program.
I need to calculate the foot of a perpendicular line drawn from a point P to a line segment AB. I need coordinates of point C where PC is perpendicular drawn from point P to line AB.
I found few answers on SO here but the vector product process does not work for me.
Here is what I tried:
function nearestPointSegment(a, b, c) {
var t = nearestPointGreatCircle(a,b,c);
return t;
}
function nearestPointGreatCircle(a, b, c) {
var a_cartesian = normalize(Cesium.Cartesian3.fromDegrees(a.x,a.y))
var b_cartesian = normalize(Cesium.Cartesian3.fromDegrees(b.x,b.y))
var c_cartesian = normalize(Cesium.Cartesian3.fromDegrees(c.x,c.y))
var G = vectorProduct(a_cartesian, b_cartesian);
var F = vectorProduct(c_cartesian, G);
var t = vectorProduct(G, F);
t = multiplyByScalar(normalize(t), R);
return fromCartesianToDegrees(t);
}
function vectorProduct(a, b) {
var result = new Object();
result.x = a.y * b.z - a.z * b.y;
result.y = a.z * b.x - a.x * b.z;
result.z = a.x * b.y - a.y * b.x;
return result;
}
function normalize(t) {
var length = Math.sqrt((t.x * t.x) + (t.y * t.y) + (t.z * t.z));
var result = new Object();
result.x = t.x/length;
result.y = t.y/length;
result.z = t.z/length;
return result;
}
function multiplyByScalar(normalize, k) {
var result = new Object();
result.x = normalize.x * k;
result.y = normalize.y * k;
result.z = normalize.z * k;
return result;
}
function fromCartesianToDegrees(pos) {
var carto = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pos);
var lon = Cesium.Math.toDegrees(carto.longitude);
var lat = Cesium.Math.toDegrees(carto.latitude);
return [lon,lat];
}
What I am missing in this?
Here's a vector-based way:
function foot(A, B, P) {
const AB = {
x: B.x - A.x,
y: B.y - A.y
};
const k = ((P.x - A.x) * AB.x + (P.y - A.y) * AB.y) / (AB.x * AB.x + AB.y * AB.y);
return {
x: A.x + k * AB.x,
y: A.y + k * AB.y
};
}
const A = { x: 1, y: 1 };
const B = { x: 4, y: 5 };
const P = { x: 4.5, y: 3 };
const C = foot(A, B, P);
console.log(C);
// perpendicular?
const AB = {
x: B.x - A.x,
y: B.y - A.y
};
const PC = {
x: C.x - P.x,
y: C.y - P.y
};
console.log((AB.x * PC.x + AB.y * PC.y).toFixed(3));
Theory:
I start with the vector from A to B, A➞B. By multiplying this vector by a scalar k and adding it to point A I can get to any point C on the line AB.
I) C = A + k × A➞B
Next I need to establish the 90° angle, which means the dot product of A➞B and P➞C is zero.
II) A➞B · P➞C = 0
Now solve for k.
function closestPointOnLineSegment(pt, segA, segB) {
const A = pt.x - segA.x,
B = pt.y - segA.y,
C = segB.x - segA.x,
D = segB.y - segA.y
const segLenSq = C**2 + D**2
const t = (segLenSq != 0) ? (A*C + B*D) / segLenSq : -1
return (t<0) ? segA : (t>1) ? segB : {
x: segA.x + t * C,
y: segA.y + t * D
}
}
can.width = can.offsetWidth
can.height = can.offsetHeight
const ctx = can.getContext('2d')
const segA = {x:100,y:100},
segB = {x:400, y:200},
pt = {x:250, y:250}
visualize()
function visualize() {
ctx.clearRect(0, 0, can.width, can.height)
const t = Date.now()
pt.x = Math.cos(t/1000) * 150 + 250
pt.y = Math.sin(t/1000) * 100 + 150
segA.x = Math.cos(t / 2000) * 50 + 150
segA.y = Math.sin(t / 2500) * 50 + 50
segB.x = Math.cos(t / 3000) * 75 + 400
segB.y = Math.sin(t / 2700) * 75 + 100
line(segA, segB, 'gray', 2)
const closest = closestPointOnLineSegment(pt, segA, segB)
ctx.setLineDash([5, 8])
line(pt, closest, 'orange', 2)
ctx.setLineDash([])
dot(closest, 'rgba(255, 0, 0, 0.8)', 10)
dot(pt, 'blue', 7)
dot(segA, 'black', 7)
dot(segB, 'black', 7)
window.requestAnimationFrame(visualize)
}
function dot(p, color, w) {
ctx.fillStyle = color
ctx.fillRect(p.x - w/2, p.y - w/2, w, w)
}
function line(a, b, color, n) {
ctx.strokeStyle = color
ctx.lineWidth = n
ctx.beginPath()
ctx.moveTo(a.x, a.y)
ctx.lineTo(b.x, b.y)
ctx.stroke()
}
html, body { height:100%; min-height:100%; margin:0; padding:0; overflow:hidden }
canvas { width:100%; height:100%; background:#ddd }
<canvas id="can"></canvas>
I am a searching for a algorithm which in essence returns an array of points which define the shape of a stain formed by liquid.
I need no directional influence, it could be a stain produced by a drop falling vertically down.
I started to spread points from a center in pairs of two, one closer to the center one near, which gives me a star ofcourse. But then i ideas left me. How can i avaoid intersections and the like but mostly: For a one-shape solution without any fancy sidedrops there should be a algorithm available i am not aware of? So any ideas/solutions?
Examples of stain i mean (i only need the central/main shape ofcourse):
related stains on Google-Images
You can refine you star approach by making the lines Bézier curves. If you look at the examples of your search, you will basically see two patterns of splashes: Small thin spikes and larger drop-like shapes.
We can ditsribute splashes randomly on a circle and also determine a splash length. Then we decide which of the shapes to draw based on that length. The control points we need are:
The code below tries to model that. The funtion splash returns a list of coordinates of a splash centered at the origin. The list has 3*n + 1 points for a closed curve of n Bézier segments. (n is determined randomly.)
The code is far from perfect and also has too much auxiliary stuff, which can be improved, but might give you an idea:
var rnd = {
uniform: function(n) {
return Math.floor(n * Math.random());
},
range: function(from, to) {
return from + this.uniform(to - from);
},
float: function(from, to) {
return from + (to - from) * Math.random();
}
}
var coord = {
radiants: function(x) {
return Math.PI * x / 180.0;
},
degrees: function(x) {
return 180.0 * x / Math.PI;
},
cartesian: function(P) {
return {
x: P.r * Math.cos(P.phi),
y: P.r * Math.sin(P.phi)
}
},
mid: function(P, Q, a) {
if (!a) a = 0.5;
return {
x: (1 - a) * P.x + a * Q.x,
y: (1 - a) * P.y + a * Q.y
};
},
normal: function(P, len) {
if (!len) len = 1;
var l = Math.sqrt(P.x*P.x + P.y*P.y);
return {
x: len * P.y / l,
y: -len * P.x / l
};
},
add: function(P, Q) {
return {
x: P.x + Q.x,
y: P.y + Q.y
};
},
mul: function(P, a) {
return {
x: a * P.x,
y: a * P.y
};
},
dist: function(P, Q) {
var dx = P.x - Q.x;
var dy = P.y - Q.y;
var l = Math.sqrt(dx*dx + dy*dy);
},
normalize: function(P, len) {
if (!len) len = 1;
var l = Math.sqrt(P.x*P.x + P.y*P.y);
return {
x: len * P.x / l,
y: len * P.y / l
};
}
}
function get(param, value, dflt) {
if (value in param) return param[value];
return dflt;
}
function splash(param) {
var r = get(param, "r", 10);
var minangle = get(param, "minangle", 5);
var maxangle = get(param, "maxangle", 30);
var ratio = get(param, "ratio", 2.4);
var n = get(param, "n", 2);
var radial = [];
var phi = 0;
while (phi < 2 * Math.PI) {
radial.push({
phi: phi,
r: r * (1 + (ratio - 1) * Math.pow(Math.random(), n))
});
phi += coord.radiants(rnd.float(minangle, maxangle, 30));
}
var phi0 = coord.radiants(rnd.float(0, 10));
for (var i = 0; i < radial.length; i++) {
var rr = radial[i];
rr.phi = 2 * rr.phi * Math.PI / phi + phi0;
}
var res = [];
var prev = radial[radial.length - 1];
var curr = radial[0];
var C = {x: 0, y: 0};
for (var i = 0; i < radial.length; i++) {
var next = radial[(i + 1) % radial.length];
var ML = coord.cartesian(prev);
var MR = coord.cartesian(next);
var M = coord.cartesian(curr);
var L = coord.mid(C, coord.mid(ML, M));
var R = coord.mid(C, coord.mid(MR, M));
if (i == 0) res.push(L);
var dphi = (next.phi - prev.phi);
if (dphi < 0) dphi += 2 * Math.PI;
var dr = 0.5 * r * dphi;
var NL = coord.normal(L, -dr * rnd.float(0.3, 0.45));
res.push(coord.add(L, NL));
console.log((curr.r - r) / (ratio - 1));
if (Math.random() > (curr.r - r) / r / (ratio - 1)) {
// little splash
var MM = coord.mid(C, M, rnd.float(0.75, 0.95));
res.push(MM);
res.push(M);
res.push(MM);
} else {
// drop-shaped splash
var s = dr * rnd.float(0.2, 0.5);
var t = dr * rnd.float(0.02, 0.2);
var MM = coord.mid(coord.mid(L, M), coord.mid(R, M));
var Mpos = coord.normalize(M, s);
var Mneg = coord.normalize(M, -s);
var MT = coord.add(M, Mpos);
var NML = coord.normal(M, s);
var NLL = coord.normal(M, t);
var MML = coord.add(MM, NLL);
var ML = coord.add(M, NML);
var NMR = coord.normal(M, -s);
var NRR = coord.normal(M, -t);
var MMR = coord.add(MM, NRR);
var MR = coord.add(M, NMR);
res.push(coord.mid(C, MML, 0.8));
res.push(MML);
res.push(coord.mid(C, MML, 1.25));
res.push(coord.add(ML, coord.mul(Mneg, 0.55)));
res.push(ML);
res.push(coord.add(ML, coord.mul(Mpos, 0.55)));
res.push(coord.add(MT, coord.mul(NML, 0.55)));
res.push(MT);
res.push(coord.add(MT, coord.mul(NMR, 0.55)));
res.push(coord.add(MR, coord.mul(Mpos, 0.55)));
res.push(MR);
res.push(coord.add(MR, coord.mul(Mneg, 0.55)));
res.push(coord.mid(C, MMR, 1.25));
res.push(MMR);
res.push(coord.mid(C, MMR, 0.8));
}
var NR = coord.normal(R, dr * rnd.float(0.3, 0.45));
res.push(coord.add(R, NR));
res.push(R);
prev = curr;
curr = next;
}
return res;
}
And an example of how to use that code:
window.onload = function() {
var cv = document.getElementById("plot");
var cx = cv.getContext("2d");
var p = splash({
r: 100,
ratio: 1.6,
n: 1
});
cx.fillStyle = "tomato";
cx.translate(300, 300);
cx.beginPath();
cx.moveTo(p[0].x, p[0].y);
for (var i = 1; i < p.length; i++) {
var p1 = p[i++];
var p2 = p[i++];
var p3 = p[i];
cx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
}
cx.closePath();
cx.fill();
}
I've tried everything and read every single link I can see on the internet regarding Perlin Noise or Simplex Noise and even dissected a few Javascript examples that I see work fine.
But I still get very random looking images... essentially just TV static.
My code is below. I'm using a random number generator so that I can seed a value, but I've tried with Math.random as well.
As near as I can tell, the different images generated at the different octaves aren't interpolating properly, or maybe the way I'm converting from the Noise function to RGB values is wrong (I've tried to fix both of these issues...).
if (!this.Prng) {
var Prng = function() {
var iMersenne = 2147483647;
var rnd = function(seed) {
if (arguments.length) {
that.seed = arguments[0];
}
that.seed = that.seed*16807%iMersenne;
return that.seed;
};
var that = {
seed: 123,
rnd: rnd,
random: function(seed) {
if (arguments.length) {
that.seed = arguments[0];
}
return rnd()/iMersenne;
}
};
return that;
}();
}
var CSimplexNoise = function(r)
{
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]];
var p = [];
for(i = 0; i < 256; i++)
p[i] = Math.floor(r.random()*256);
this.perm = new Array();
for(i = 0; i < 512; i++)
{
this.perm[i] = p[i & 255];
}
}
CSimplexNoise.prototype.dot = function(g,x,y)
{
return g[0]*x + g[1]*y;
}
CSimplexNoise.prototype.GenerateSimplexNoise = function(x,y,octaves,persistence)
{
var total = 0;
for(i=0; i < octaves-1; i++)
{
var freq = Math.pow(2,i);
var amp = Math.pow(persistence,i);
total += this.InterpolatedNoise(x*freq,y*freq) * amp;
}
return total;
}
CSimplexNoise.prototype.InterpolatedNoise = function(x,y)
{
var xInt = Math.floor(x);
var xFrac = x - xInt;
var yInt = Math.floor(y);
var yFrac = y - yInt;
var v1 = this.SmoothNoise(xInt,yInt);
var v2 = this.SmoothNoise(xInt + 1,yInt)
var v3 = this.SmoothNoise(xInt,yInt+1)
var v4 = this.SmoothNoise(xInt + 1, yInt + 1);
var i1 = this.LinearInterpolate(v1,v2,xFrac);
var i2 = this.LinearInterpolate(v3,v4,xFrac);
return this.CosineInterpolate(i1,i2,yFrac);
}
CSimplexNoise.prototype.LinearInterpolate = function(a,b,x)
{
return a*(1-x) + b*x;
}
CSimplexNoise.prototype.CosineInterpolate = function(a,b,x)
{
var f = (1 - Math.cos(x*Math.PI)) * 0.5;
return a*(1-f) + b*f;
}
CSimplexNoise.prototype.SmoothNoise = function(x,y)
{
var corners = (this.Noise(x-1,y-1) + this.Noise(x+1,y-1) + this.Noise(x-1,y+1) + this.Noise(x+1,y+1)) / 16;
var sides = (this.Noise(x-1,y) + this.Noise(x+1,y) + this.Noise(x,y-1) + this.Noise(x+1,y+1)) / 8;
var center = this.Noise(x,y) / 4;
return corners + sides + center;
}
CSimplexNoise.prototype.Noise = function(xin, yin)
{
var n0, n1, n2;
var F2 = 0.5*(Math.sqrt(3)-1);
var s = (xin+yin)*F2;
var i = Math.floor(xin+s);
var j = Math.floor(yin+s);
var G2 = (3-Math.sqrt(3))/6;
var t = (i+j)*G2;
var X0 = i-t;
var Y0 = j-t;
var x0 = xin-X0;
var y0 = yin-Y0;
var i1,j1;
if(x0 > y0)
{
i1 = 1;
j1 = 0;
}
else
{
i1 = 0;
j1 = 1;
}
var x1 = x0 - i1 + G2;
var y1 = y0 - j1 + G2;
var x2 = x0 - 1 + 2 * G2;
var y2 = y0 - 1 + 2 * G2;
var ii = i & 255;
var jj = j & 255;
var gi0 = this.perm[ii + this.perm[jj]] % 12;
var gi1 = this.perm[ii + i1 + this.perm[jj + j1]] % 12;
var gi2 = this.perm[ii + 1 + this.perm[jj + 1]] % 12;
var t0 = 0.5 - x0 * x0 - y0 * y0;
if(t0 < 0)
n0 = 0;
else
{
t0 *= t0;
n0 = t0 * t0 * this.dot(this.grad3[gi0],x0,y0)
}
var t1 = 0.5 - x1 * x1 - y1 * y1;
if(t1 < 0)
n1 = 0;
else
{
t1 *= t1;
n1 = t1 * t1 * this.dot(this.grad3[gi1],x1,y1);
}
var t2 = 0.5 - x2 * x2 - y2 * y2;
if(t2 <0 )
n2 = 0;
else
{
t2 *= t2;
n2 = t2 * t2 * this.dot(this.grad3[gi2],x2,y2);
}
return 70 * (n0 + n1 + n2);
}
$(document).ready(function(){
var context = $('#screen')[0].getContext("2d");
var w = 100;
var h = 100;
var data = context.createImageData(w,h);
var simplexNoise = new CSimplexNoise(Prng);
for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
// var newVal = ((simplexNoise.GenerateSimplexNoise(x,y,5,0.25) - -1) / (1 - -1)) * (255 - 0);
var newVal2 = simplexNoise.GenerateSimplexNoise(x,y,5,0.5)
var newVal = Math.floor(newVal2*256);
newVal = Math.abs(newVal * 2)-0.5;
data.data[((h * y) + x) * 4] = newVal;
data.data[((h * y) + x) * 4+1] = newVal;
data.data[((h * y) + x) * 4+2] = newVal;
data.data[((h * y) + x) * 4+3] = 255;
}
}
context.putImageData(data,0,0);
})
Try sampling simplexNoise.GenerateSimplexNoise(x * 0.05, y * 0.05, 5, 0.5)
The problem may be that your samples are too far apart. (this would result in apparently random behavior, since the simplex noise might go through more than half a wavelength before you sample it)
REVISION: Updated numbers above...
You may actually need to reduce the samples so that there are 20 in a given wavelength of the simplex noise. The average wavelength of most simplex noise is 1, so 0.05 should do the trick. Also, you may want to test with just one octave at first.
I am using HTML5 Canvas and applying Edge Filter by pixel manipulation. But I am getting lag in the video. Is there any better way to achieve video with edge filter without any lag in video?
Following is the JavaScript I am using with canvas and HTML 5 to apply the effect:-
function sobel (px) {
px = greyScale(px);
var vertical = convoluteFloat32(px,
[-1, -2, -1,
0, 0, 0,
1, 2, 1], false);
var horizontal = convoluteFloat32(px,
[-1, 0, 1,
-2, 0, 2,
-1, 0, 1], false);
var id = createImageData(vertical.width, vertical.height);
for (var i = 0; i < id.data.length; i += 4) {
var v = Math.abs(vertical.data[i]);
id.data[i] = v;
var h = Math.abs(horizontal.data[i]);
id.data[i + 1] = h
id.data[i + 2] = (v + h) / 4;
id.data[i + 3] = 255;
}
return id;
}
function convoluteFloat32 (pixels, weights, opaque) {
var side = Math.round(Math.sqrt(weights.length));
var halfSide = Math.floor(side / 2);
var src = pixels.data;
var sw = pixels.width;
var sh = pixels.height;
var w = sw;
var h = sh;
var output = {
width: w, height: h, data: new Float32Array(w * h * 4)
};
var dst = output.data;
var alphaFac = opaque ? 1 : 0;
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
var sy = y;
var sx = x;
var dstOff = (y * w + x) * 4;
var r = 0, g = 0, b = 0, a = 0;
for (var cy = 0; cy < side; cy++) {
for (var cx = 0; cx < side; cx++) {
var scy = Math.min(sh - 1, Math.max(0, sy + cy - halfSide));
var scx = Math.min(sw - 1, Math.max(0, sx + cx - halfSide));
var srcOff = (scy * sw + scx) * 4;
var wt = weights[cy * side + cx];
r += src[srcOff] * wt;
g += src[srcOff + 1] * wt;
b += src[srcOff + 2] * wt;
a += src[srcOff + 3] * wt;
}
}
dst[dstOff] = r;
dst[dstOff + 1] = g;
dst[dstOff + 2] = b;
dst[dstOff + 3] = a + alphaFac * (255 - a);
}
}
return output;
}
function draw() {
// First, draw it into the backing canvas
backcontext.drawImage(video, 0, 0, video.width, video.height);
// Grab the pixel data from the backing canvas
var idata = backcontext.getImageData(0, 0, video.width, video.height);
idata = sobel(idata);
context.putImageData(idata, 0, 0);
// Start over!
setTimeout(draw, 50);
}
Instead of convolution with a 2D kernel, you can try 1D convolution with the separable convolution approach. This should increase the speed noticeably.
An explanation on how to use separable convolution using Sobel kernels can be found here.
You should change your code to something like this:
function convoluteFloat32 (pixels, weights1DHorizontal, weights1DVertical, opaque){
convolveFloat321DHorizontal(pixels, weights1DHorizontal, opaque);
convolveFloat321DVertical(pixels, weights1DVertical, opaque);
}
And convolveFloat321DHorizontal/convolveFloat321DVertical methods are just like the original convoluteFloat32 method, but they have 3 nested for loops instead of 4.