p5.js WebGL 3d 图形在旋转时被 2d 背景覆盖
p5.js WebGL 3d graphics covered by 2d background when rotated
我有两件事想用 p5 显示,一个是 2D 背景,另一个是 3D WebGL 前景,都是由 p5 生成的。我注意到,即使我在 draw()
函数中的 3D 内容之前绘制 2D 背景,当调用 rotateX()
或 rotateY()
时,3D 内容仍将部分被背景覆盖.它看起来像这样:
我怀疑发生的事情是 2d 和 3d 的东西都在同一个 z 平面上,因此当前景旋转时,其中一些被背景覆盖,与被覆盖的部分相比,现在在前面.
所以我的问题是如何将背景完全放在后面(即无论旋转如何都不会覆盖前景)?
下面是我当前的实现,2d 背景是在屏幕外生成的 canvas 然后放在主 canvas 上 image()
生成 3d 的东西,但是我'我会采取任何其他方法。
let bg;
p.setup = () => {
p.createCanvas(width,height,p.WEBGL);
bg = p.createGraphics(width,height);
}
p.draw = () => {
... // draw background bg
p.image(bg,x,y); // draw background on canvas
... // draw foreground
p.rotateX(degrees);//rotate
}
完成此操作的最佳方法是清除 WebGL 深度缓冲区。这是缓冲区存储到目前为止已绘制的每个像素的深度,以便在绘制后续三角形时,如果其中一些或全部三角形落后于先前在该位置绘制的任何内容,则可以裁剪它们。此缓冲区在 p5.js 中对 draw()
的调用之间自动清除,但您也可以在帧中自己调用它:
let bg;
let zSlider;
let glContext;
function setup() {
let c = createCanvas(200, 200, WEBGL);
glContext = c.GL;
bg = createGraphics(width, height);
bg.background('red');
for (let y = 0; y < height; y += 20) {
for (let x = 0; x < width; x += 20) {
if ((x / 20 + y / 20) % 2 === 0) {
bg.fill('black');
} else {
bg.fill(
map(x + y, 0, width + height, 0, 360),
map(y, 0, height, 50, 100),
map(x, 0, width, 50, 100)
);
}
bg.square(x, y, 20)
}
}
zSlider = createSlider(0, width * 2, width);
zSlider.position(10, 10);
}
function draw() {
image(bg, -width / 2, -height / 2, width, height);
// Clear the z-buffer, subsequent drawing commands will not clip, even if they
// intersect with or are behind previously drawn elements (like our background
// image)
glContext.clear(glContext.DEPTH_BUFFER_BIT);
push();
translate(0, 0, (zSlider.value() - width) * 2);
rotateX(millis() / 1000 * PI / 4);
rotateY(millis() / 1000 * PI / 8);
box(100);
pop();
}
<script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"></script>
还有不依赖调用 WebGL 内部机制的笨拙解决方案,但我只能让它适用于方形画布:
- 在绘制背景图像之前切换到正交相机模式。
- 在不超出“远”裁剪平面的情况下,尽可能沿负 Z 方向平移。
- 绘制背景图片。
- 弹出状态回到普通透视相机。
这使用正交投影让您可以在场景其余部分后面绘制背景图像,而不会因透视而缩小尺寸。然而,我还没有想出一个万无一失的方法来确定什么是完美的翻译值,也没有想出如何可靠地设置正交项目来控制“远”裁剪平面的位置。
let bg;
let zSlider;
function setup() {
createCanvas(200, 200, WEBGL);
bg = createGraphics(width, height);
bg.background('red');
for (let y = 0; y < height; y += 20) {
for (let x = 0; x < width; x += 20) {
if ((x / 20 + y / 20) % 2 === 0) {
bg.fill('black');
} else {
bg.fill(
map(x + y, 0, width + height, 0, 360),
map(y, 0, height, 50, 100),
map(x, 0, width, 50, 100)
);
}
bg.square(x, y, 20)
}
}
zSlider = createSlider(0, width * 2, width);
zSlider.position(10, 10);
}
function draw() {
push();
ortho();
translate(0, 0, min(width, height) * -0.13);
image(bg, -width / 2, -height / 2, width, height);
pop();
push();
translate(0, 0, (zSlider.value() - width) * 2);
rotateX(millis() / 1000 * PI / 4);
rotateY(millis() / 1000 * PI / 8);
box(100);
pop();
}
<script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"></script>
我有两件事想用 p5 显示,一个是 2D 背景,另一个是 3D WebGL 前景,都是由 p5 生成的。我注意到,即使我在 draw()
函数中的 3D 内容之前绘制 2D 背景,当调用 rotateX()
或 rotateY()
时,3D 内容仍将部分被背景覆盖.它看起来像这样:
我怀疑发生的事情是 2d 和 3d 的东西都在同一个 z 平面上,因此当前景旋转时,其中一些被背景覆盖,与被覆盖的部分相比,现在在前面.
所以我的问题是如何将背景完全放在后面(即无论旋转如何都不会覆盖前景)?
下面是我当前的实现,2d 背景是在屏幕外生成的 canvas 然后放在主 canvas 上 image()
生成 3d 的东西,但是我'我会采取任何其他方法。
let bg;
p.setup = () => {
p.createCanvas(width,height,p.WEBGL);
bg = p.createGraphics(width,height);
}
p.draw = () => {
... // draw background bg
p.image(bg,x,y); // draw background on canvas
... // draw foreground
p.rotateX(degrees);//rotate
}
完成此操作的最佳方法是清除 WebGL 深度缓冲区。这是缓冲区存储到目前为止已绘制的每个像素的深度,以便在绘制后续三角形时,如果其中一些或全部三角形落后于先前在该位置绘制的任何内容,则可以裁剪它们。此缓冲区在 p5.js 中对 draw()
的调用之间自动清除,但您也可以在帧中自己调用它:
let bg;
let zSlider;
let glContext;
function setup() {
let c = createCanvas(200, 200, WEBGL);
glContext = c.GL;
bg = createGraphics(width, height);
bg.background('red');
for (let y = 0; y < height; y += 20) {
for (let x = 0; x < width; x += 20) {
if ((x / 20 + y / 20) % 2 === 0) {
bg.fill('black');
} else {
bg.fill(
map(x + y, 0, width + height, 0, 360),
map(y, 0, height, 50, 100),
map(x, 0, width, 50, 100)
);
}
bg.square(x, y, 20)
}
}
zSlider = createSlider(0, width * 2, width);
zSlider.position(10, 10);
}
function draw() {
image(bg, -width / 2, -height / 2, width, height);
// Clear the z-buffer, subsequent drawing commands will not clip, even if they
// intersect with or are behind previously drawn elements (like our background
// image)
glContext.clear(glContext.DEPTH_BUFFER_BIT);
push();
translate(0, 0, (zSlider.value() - width) * 2);
rotateX(millis() / 1000 * PI / 4);
rotateY(millis() / 1000 * PI / 8);
box(100);
pop();
}
<script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"></script>
还有不依赖调用 WebGL 内部机制的笨拙解决方案,但我只能让它适用于方形画布:
- 在绘制背景图像之前切换到正交相机模式。
- 在不超出“远”裁剪平面的情况下,尽可能沿负 Z 方向平移。
- 绘制背景图片。
- 弹出状态回到普通透视相机。
这使用正交投影让您可以在场景其余部分后面绘制背景图像,而不会因透视而缩小尺寸。然而,我还没有想出一个万无一失的方法来确定什么是完美的翻译值,也没有想出如何可靠地设置正交项目来控制“远”裁剪平面的位置。
let bg;
let zSlider;
function setup() {
createCanvas(200, 200, WEBGL);
bg = createGraphics(width, height);
bg.background('red');
for (let y = 0; y < height; y += 20) {
for (let x = 0; x < width; x += 20) {
if ((x / 20 + y / 20) % 2 === 0) {
bg.fill('black');
} else {
bg.fill(
map(x + y, 0, width + height, 0, 360),
map(y, 0, height, 50, 100),
map(x, 0, width, 50, 100)
);
}
bg.square(x, y, 20)
}
}
zSlider = createSlider(0, width * 2, width);
zSlider.position(10, 10);
}
function draw() {
push();
ortho();
translate(0, 0, min(width, height) * -0.13);
image(bg, -width / 2, -height / 2, width, height);
pop();
push();
translate(0, 0, (zSlider.value() - width) * 2);
rotateX(millis() / 1000 * PI / 4);
rotateY(millis() / 1000 * PI / 8);
box(100);
pop();
}
<script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"></script>