有没有办法更快地绘制数百个点(p5.js)
Is there a way to draw hundreds of points faster (p5.js)
我正在制作一个程序来测试我对 Perlin 噪声生成算法的尝试。 Perlin 噪音本身看起来不错,但我发现在 canvas 上绘制噪音非常慢。这可能是因为对于 canvas 中的每个点,我都必须调用 stroke() 函数来更改下一个像素的颜色,然后绘制该像素。这是在 400*400 像素 canvas 上完成的,所以我使用 stroke() 更改颜色 160,000 次并调用 point() 160,000 次。
这需要一些时间来完成。我想知道是否有任何方法可以使它更快。也许如果我可以将 Perlin 噪声转换为图像,然后加载该图像而不是单独绘制所有 160,000 个点?
我绘制循环的代码如下
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() 方法进行了测试。这不如使用图像方法快,因为它仍然需要我调用 stroke() 160,000 次。
这两种解决方案都为我提供了可以快速绘制的图像,但是 createImage() 使我创建图像的时间比 createGraphics() 少得多。
我已经尝试了很多毫秒测试,图像方法是目前为止最好的。正如@Samathinamajig 指出的那样,问题实际上只是您尝试处理的像素数量。
只是为了对现有建议添加一些细微差别:
使用pixels[]
instead of set(x, y, color)
:考虑考虑 [r,g,b,a,...] 像素顺序的 1D 索引不太直观,并且(pixelDensity
在视网膜上显示),但速度更快。
文档提到:
Setting the color of a single pixel with set(x, y) is easy, but not as
fast as putting the data directly into pixels[]. Setting the pixels[]
values directly may be complicated when working with a retina display,
but will perform better when lots of pixels need to be set directly on
every loop.
在您的情况下,大致如下所示:
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();
话虽如此,它可能无法按预期工作:
- 据我所知,这不适用于 createCanvas(400, 400, WEBGL),因为目前您无法为形状中的每个顶点设置独立的笔划。
- 对于典型的 Canvas 2D 渲染器,使用
beginShape()/endShape()
渲染这么多顶点可能仍然很慢
虽然是一个更高级的主题,但另一个应该很快的选项是 shader()
。 (顺便说一句,可能会在 shadertoy.com 上找到一些柏林噪声灵感)。
p5.js 非常适合学习,但它的主要目标不是拥有最高性能的 canvas 渲染器。如果着色器在这个阶段有点太复杂,但您通常对 javascript 感到满意,您可以查看其他库,例如 pixi.js (maybe pixi particle-emitter 可能会很方便?)
我正在制作一个程序来测试我对 Perlin 噪声生成算法的尝试。 Perlin 噪音本身看起来不错,但我发现在 canvas 上绘制噪音非常慢。这可能是因为对于 canvas 中的每个点,我都必须调用 stroke() 函数来更改下一个像素的颜色,然后绘制该像素。这是在 400*400 像素 canvas 上完成的,所以我使用 stroke() 更改颜色 160,000 次并调用 point() 160,000 次。
这需要一些时间来完成。我想知道是否有任何方法可以使它更快。也许如果我可以将 Perlin 噪声转换为图像,然后加载该图像而不是单独绘制所有 160,000 个点?
我绘制循环的代码如下
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() 方法进行了测试。这不如使用图像方法快,因为它仍然需要我调用 stroke() 160,000 次。
这两种解决方案都为我提供了可以快速绘制的图像,但是 createImage() 使我创建图像的时间比 createGraphics() 少得多。
我已经尝试了很多毫秒测试,图像方法是目前为止最好的。正如@Samathinamajig 指出的那样,问题实际上只是您尝试处理的像素数量。
只是为了对现有建议添加一些细微差别:
使用pixels[]
instead of set(x, y, color)
:考虑考虑 [r,g,b,a,...] 像素顺序的 1D 索引不太直观,并且(pixelDensity
在视网膜上显示),但速度更快。
文档提到:
Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly into pixels[]. Setting the pixels[] values directly may be complicated when working with a retina display, but will perform better when lots of pixels need to be set directly on every loop.
在您的情况下,大致如下所示:
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();
话虽如此,它可能无法按预期工作:
- 据我所知,这不适用于 createCanvas(400, 400, WEBGL),因为目前您无法为形状中的每个顶点设置独立的笔划。
- 对于典型的 Canvas 2D 渲染器,使用
beginShape()/endShape()
渲染这么多顶点可能仍然很慢
虽然是一个更高级的主题,但另一个应该很快的选项是 shader()
。 (顺便说一句,可能会在 shadertoy.com 上找到一些柏林噪声灵感)。
p5.js 非常适合学习,但它的主要目标不是拥有最高性能的 canvas 渲染器。如果着色器在这个阶段有点太复杂,但您通常对 javascript 感到满意,您可以查看其他库,例如 pixi.js (maybe pixi particle-emitter 可能会很方便?)