在 p5.js 中使用 createGraphics 高效遮罩形状
Efficiently mask shapes using createGraphics in p5.js
我正在尝试在 p5.js 中创建各种形状并用特定的 patterns/drawings 填充它们。每个形状都有一个使用 createGraphics
生成的独特图案。由于我的不同形状无法覆盖我的所有基础 canvas,我正在考虑为我的图案创建更小尺寸的图形以提高性能。例如,如果我的基础 canvas 是 1000 * 1000 像素而我的形状只需要 50 * 50 像素并且我想用矩形图案填充它,我看不出创建 1000 * 1000 像素图案的意义图形并用我的 50 * 50 像素形状对其进行遮蔽。
我正在尝试使用单一形状和图案进行概念验证:
- 确定包含我的整个形状的矩形的宽度和高度。请注意,在绘制之前我知道我的形状会是什么样子(绘制顶点的预定点)。
- 使用 1 中确定的尺寸创建我的图案图形。
- 创建我的形状。
- 用3中创建的形状遮盖2中创建的图案图形
- 在我的基础中显示结果图像 canvas。
另请注意,形状可以位于我的基地内的任何位置 canvas,并且其位置由用于绘制形状的第一个点确定。该过程必须重复多次才能生成所需的输出。
function setup() {
createCanvas(1000, 1000);
background(200, 255, 255);
var ptrn;
var myShape;
var msk;
let ptrn_w = 1000;
let ptrn_h = 1000;
ptrn = createGraphics(ptrn_w, ptrn_h);
ptrn.background(120, 0, 150);
ptrn.fill(255, 255, 255);
ptrn.noStroke();
for (let i = 0; i < 500; i++){
let x = random(0, ptrn_w);
let y = random(0, ptrn_h);
ptrn.rect(x, y, 20, 5);
}
msk = createGraphics(ptrn_w, ptrn_h);
msk.beginShape();
msk.vertex(250, 920);
msk.vertex(300, 15);
msk.vertex(325, 75);
msk.vertex(350, 840);
msk.endShape(CLOSE);
( myShape = ptrn.get() ).mask( msk.get() );
image(myShape, 0, 0);
}
function draw() {
}
上面列出的代码有效,因为 ptrn_w
和 ptrn_h
等于基数 canvas 的宽度和高度。但是,我们正在为整个 canvas 生成 patterns/graphics,并且该区域的很大一部分未使用。如果我们生成数百种具有复杂图案的不同形状,我可以看到从性能的角度来看,限制我们生成图案的区域是多么有益。
将 ptrn_w
和 ptrn_h
更改为 ptrn_w = 100
和 ptrn_h = 905
是有问题的,因为蒙版将应用于图案图形之外 ptrn
。
有没有办法平移 ptrn
的位置,使其与 msk
的位置对齐?如果我们将图像的位置归零,image(myShape, 0, 0)
会不会有问题?
我的另一个想法是在调用 image(myShape, x_pos, y_pos)
.
时将我的面具的位置归零并重新定位它
实现这种行为的最佳方法是什么?欢迎任何创意。
我认为最有效的方法是使用底层的 CanvasRenderingContext2D globalCompositeOperation。通过将其设置为 'source-in'
,您可以执行某种反向掩码操作,而无需使用 p5.Image
对象。另外,如以下示例所示,您可以对顶点列表进行一些简单的计算,以确定宽度和高度,并在最小大小的缓冲区上绘制形状。
let vertices = [
[10, 15],
[150, 40],
[100, 120],
[25, 75]
];
let ptrn;
let myShape;
let shapeTop, shapeLeft;
function setup() {
createCanvas(windowWidth, windowHeight);
// Calculate the rage of vertices values
let shapeBottom, shapeRight;
for (const [x, y] of vertices) {
if (shapeTop === undefined || y < shapeTop) {
shapeTop = y;
}
if (shapeBottom === undefined || y > shapeBottom) {
shapeBottom = y;
}
if (shapeLeft === undefined || x < shapeLeft) {
shapeLeft = x;
}
if (shapeRight === undefined || x > shapeRight) {
shapeRight = x;
}
}
// Determine the minimum width and height to fit the shape
let w = shapeRight - shapeLeft,
h = shapeBottom - shapeTop;
// Generate the pattern
ptrn = createGraphics(w, h);
ptrn.background(120, 0, 150);
ptrn.noStroke();
for (let i = 0; i < 100; i++) {
ptrn.fill(random(255), random(255), random(255));
let x = random(0, w);
let y = random(0, h);
ptrn.rect(x, y, 5);
}
// Draw the shape
myShape = createGraphics(w, h);
myShape.fill(255);
myShape.noStroke();
myShape.beginShape();
for (const [x, y] of vertices) {
myShape.vertex(x - shapeLeft, y - shapeTop);
}
myShape.endShape(CLOSE);
// See: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
// The 'source-in' composite operation will output the drawn content (ptrn)
// only in place of existing pixels that are not transparent.
// The output of each pixel is basically: color(r2, g2, b3, min(a1, a2))
myShape.drawingContext.globalCompositeOperation = 'source-in';
myShape.image(ptrn, 0, 0)
}
function draw() {
background(200, 255, 255);
image(myShape, mouseX + shapeLeft, mouseY + shapeTop);
}
html, body { margin: 0; padding: 0; overflow: hidden }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
我正在尝试在 p5.js 中创建各种形状并用特定的 patterns/drawings 填充它们。每个形状都有一个使用 createGraphics
生成的独特图案。由于我的不同形状无法覆盖我的所有基础 canvas,我正在考虑为我的图案创建更小尺寸的图形以提高性能。例如,如果我的基础 canvas 是 1000 * 1000 像素而我的形状只需要 50 * 50 像素并且我想用矩形图案填充它,我看不出创建 1000 * 1000 像素图案的意义图形并用我的 50 * 50 像素形状对其进行遮蔽。
我正在尝试使用单一形状和图案进行概念验证:
- 确定包含我的整个形状的矩形的宽度和高度。请注意,在绘制之前我知道我的形状会是什么样子(绘制顶点的预定点)。
- 使用 1 中确定的尺寸创建我的图案图形。
- 创建我的形状。
- 用3中创建的形状遮盖2中创建的图案图形
- 在我的基础中显示结果图像 canvas。
另请注意,形状可以位于我的基地内的任何位置 canvas,并且其位置由用于绘制形状的第一个点确定。该过程必须重复多次才能生成所需的输出。
function setup() {
createCanvas(1000, 1000);
background(200, 255, 255);
var ptrn;
var myShape;
var msk;
let ptrn_w = 1000;
let ptrn_h = 1000;
ptrn = createGraphics(ptrn_w, ptrn_h);
ptrn.background(120, 0, 150);
ptrn.fill(255, 255, 255);
ptrn.noStroke();
for (let i = 0; i < 500; i++){
let x = random(0, ptrn_w);
let y = random(0, ptrn_h);
ptrn.rect(x, y, 20, 5);
}
msk = createGraphics(ptrn_w, ptrn_h);
msk.beginShape();
msk.vertex(250, 920);
msk.vertex(300, 15);
msk.vertex(325, 75);
msk.vertex(350, 840);
msk.endShape(CLOSE);
( myShape = ptrn.get() ).mask( msk.get() );
image(myShape, 0, 0);
}
function draw() {
}
上面列出的代码有效,因为 ptrn_w
和 ptrn_h
等于基数 canvas 的宽度和高度。但是,我们正在为整个 canvas 生成 patterns/graphics,并且该区域的很大一部分未使用。如果我们生成数百种具有复杂图案的不同形状,我可以看到从性能的角度来看,限制我们生成图案的区域是多么有益。
将 ptrn_w
和 ptrn_h
更改为 ptrn_w = 100
和 ptrn_h = 905
是有问题的,因为蒙版将应用于图案图形之外 ptrn
。
有没有办法平移 ptrn
的位置,使其与 msk
的位置对齐?如果我们将图像的位置归零,image(myShape, 0, 0)
会不会有问题?
我的另一个想法是在调用 image(myShape, x_pos, y_pos)
.
实现这种行为的最佳方法是什么?欢迎任何创意。
我认为最有效的方法是使用底层的 CanvasRenderingContext2D globalCompositeOperation。通过将其设置为 'source-in'
,您可以执行某种反向掩码操作,而无需使用 p5.Image
对象。另外,如以下示例所示,您可以对顶点列表进行一些简单的计算,以确定宽度和高度,并在最小大小的缓冲区上绘制形状。
let vertices = [
[10, 15],
[150, 40],
[100, 120],
[25, 75]
];
let ptrn;
let myShape;
let shapeTop, shapeLeft;
function setup() {
createCanvas(windowWidth, windowHeight);
// Calculate the rage of vertices values
let shapeBottom, shapeRight;
for (const [x, y] of vertices) {
if (shapeTop === undefined || y < shapeTop) {
shapeTop = y;
}
if (shapeBottom === undefined || y > shapeBottom) {
shapeBottom = y;
}
if (shapeLeft === undefined || x < shapeLeft) {
shapeLeft = x;
}
if (shapeRight === undefined || x > shapeRight) {
shapeRight = x;
}
}
// Determine the minimum width and height to fit the shape
let w = shapeRight - shapeLeft,
h = shapeBottom - shapeTop;
// Generate the pattern
ptrn = createGraphics(w, h);
ptrn.background(120, 0, 150);
ptrn.noStroke();
for (let i = 0; i < 100; i++) {
ptrn.fill(random(255), random(255), random(255));
let x = random(0, w);
let y = random(0, h);
ptrn.rect(x, y, 5);
}
// Draw the shape
myShape = createGraphics(w, h);
myShape.fill(255);
myShape.noStroke();
myShape.beginShape();
for (const [x, y] of vertices) {
myShape.vertex(x - shapeLeft, y - shapeTop);
}
myShape.endShape(CLOSE);
// See: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
// The 'source-in' composite operation will output the drawn content (ptrn)
// only in place of existing pixels that are not transparent.
// The output of each pixel is basically: color(r2, g2, b3, min(a1, a2))
myShape.drawingContext.globalCompositeOperation = 'source-in';
myShape.image(ptrn, 0, 0)
}
function draw() {
background(200, 255, 255);
image(myShape, mouseX + shapeLeft, mouseY + shapeTop);
}
html, body { margin: 0; padding: 0; overflow: hidden }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>