【问题标题】:p5.js WebGL 3d graphics covered by 2d background when rotatedp5.j​​s WebGL 3d 图形在旋转时被 2d 背景覆盖
【发布时间】:2020-03-20 06:39:23
【问题描述】:

我想用 p5 显示两件东西,一件是 2D 背景,另一件是 3D WebGL 前景,两者都是由 p5 生成的。我注意到的是,即使我在 draw() 函数中在 3D 内容之前绘制 2D 背景,当调用 rotateX()rotateY() 时,3D 内容仍然会被背景部分覆盖。它看起来像这样:

我怀疑发生的事情是 2d 和 3d 的东西都在同一个 z 平面上,因此当前景旋转时,其中一些被背景覆盖,与被覆盖的部分相比,它现在位于前面。

所以我的问题是我怎样才能将背景完全保留在后面(即无论旋转如何都不覆盖前景)?

下面是我当前的实现,2d 背景是在屏幕外画布中生成的,然后使用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
}

【问题讨论】:

    标签: javascript p5.js


    【解决方案1】:

    完成此操作的最佳方法是清除 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();
    }
    &lt;script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"&gt;&lt;/script&gt;

    还有不依赖于调用 WebGL 内部的 kludgy 解决方案,但我只能使其适用于方形画布:

    1. 在绘制背景图像之前切换到正交相机模式。
    2. 在不超出“远”剪切平面的情况下,尽可能沿负 Z 方向平移。
    3. 绘制背景图片。
    4. 将状态弹出回普通透视相机。

    这使用正交投影,允许您在场景的其余部分后面绘制背景图像,而不会因透视而减小尺寸。但是,我还没有想出一个万无一失的方法来确定完美的平移值是什么,也没有找到可靠地设置正交项目来控制“远”剪切平面的位置。

    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();
    }
    &lt;script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 2013-06-24
      • 2015-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-17
      • 1970-01-01
      • 2021-11-02
      • 2014-11-24
      相关资源
      最近更新 更多