是否有一种方法可以更快地绘制数百点(p5.js)



我正在编写一个程序来测试我对柏林噪声生成算法的尝试。柏林噪音本身似乎很好,但我发现在画布上绘制噪音非常缓慢。这可能是因为对于画布中的每个点,我都必须调用stroke()函数来更改下一个像素的颜色,然后绘制该像素。这是在一个400*400像素的画布上完成的,所以我用stroke()变换了16万次颜色,调用point()变换了16万次颜色。

这需要一些时间来做。我想知道是否有办法让这个更快。也许如果我能把柏林噪声变成一个图像,然后加载这个图像,而不是单独绘制所有16万个点?

我的draw循环代码在

下面
function draw() {
background(220);
strokeWeight(1);

for(var row = 0; row < height; row ++)
{
for(var column = 0; column < width; column ++)
{
//takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next point
stroke((myNoise[row][column]+1)*128);

point(column,row)
}
}

noLoop();
}
编辑:我使用以下代码来创建和加载图像。感谢Samathingamajig的提示。
function draw() {
background(220);

img = createImage(width,height);
img.loadPixels();

for(var row = 0; row < height; row ++)
{
for(var column = 0; column < width; column ++)
{
//takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next pixel in the image
img.set(row,column,color((myNoise[row][column]+1)*128))      
}
}
img.updatePixels();

image(img,0,0)

noLoop();

}

Samathingamajig也指出400*400是160,000,而不是1,600,我在上面修改了。

我的原始代码花了大约4秒来运行绘制循环。这个新版本大约需要0.75秒。

我还按照rednoyz的建议使用createGraphics()方法进行了测试。这没有使用image方法快,因为它仍然需要我调用stroke() 160,000次。

这两种解决方案都给了我一个我可以很快绘制的图像,但是createImage()允许我在比createGraphics()更短的时间内创建图像。

为现有建议添加一点细微差别:

使用pixels[]代替set(x, y, color):考虑[r,g,b,a,…]的一维索引不太直观。像素顺序,和(视网膜显示器上的pixelDensity),但速度更快。

文档中提到:

使用set(x, y)设置单个像素的颜色很容易,但不是as与直接将数据放入pixels[]一样快。设置像素[]当使用视网膜显示器时,直接的值可能会很复杂,但是当需要直接设置大量像素时,性能会更好每个循环。

在您的例子中,大致看起来像这样:

img.loadPixels();

let numPixels = width * height;
for(let pixelIndex = 0; pixelIndex < numPixels; pixelIndex ++)
{
//takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next pixel in the image
// index of red channel for the current pixel
// pixels = [r0, g0, b0, a0, r1, g1, b1, a1, ...]
let redIndex = pixelIndex * 4;
// convert 1D array index to 2D array indices
let column = pixelIndex % width;
let row    = floor(pixelIndex / width);
// get perlin noise value
let grey = (myNoise[row][column]+1) * 128;
// apply grey value to R,G,B channels (and 255 to alpha)
img.pixels[redIndex]     = grey; // R
img.pixels[redIndex + 1] = grey; // G
img.pixels[redIndex + 2] = grey; // B
img.pixels[redIndex + 3] = 255;  // A
}

img.updatePixels();

(循环一次而不是嵌套循环也会有帮助)。

关于point(),它可能在幕后使用beginShape(POINTS);vertex(x,y);endShape();之类的东西,这意味着这样的东西会稍微更有效:

let numPixels = width * height;
beginShape(POINTS);
for(let pixelIndex = 0; pixelIndex < numPixels; pixelIndex ++)
{
//takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next pixel in the image
// convert 1D array index to 2D array indices
let column = pixelIndex % width;
let row    = floor(pixelIndex / width);
// get perlin noise value
stroke(color((myNoise[row][column]+1) * 128));
// apply grey value to R,G,B channels (and 255 to alpha)
vertex(column, row);
}
endShape();

话虽如此,它可能不会像预期的那样工作:

  1. 我知道这在createCanvas(400,400, WEBGL)中不起作用,因为目前你不能为一个形状的每个顶点设置一个独立的笔画。
  2. 使用典型的Canvas 2D渲染器,使用beginShape()/endShape()渲染这么多顶点可能仍然很慢

虽然是一个更高级的主题,另一个选项应该是快速的shader()。(可能会在shadertoy.com上找到一些柏林噪音的灵感)。

p5.js很好学习,但它的主要目标不是拥有最高性能的画布渲染器。如果着色器在这个阶段有点太复杂,但你对javascript很熟悉,你可以看看其他库,如pixi.js(也许pixi粒子发射器可能很方便?)

我已经尝试了很多毫秒测试,图像方法是迄今为止最好的。正如@Samathinamajig指出的那样,问题实际上只是你试图处理的像素数量。

测试:https://editor.p5js.org/KoderM/sketches/6XPirw_98s

最新更新