【问题标题】:Processing: Efficiently create uniform grid处理:高效创建均匀网格
【发布时间】:2020-07-24 19:24:38
【问题描述】:

我正在尝试创建图像网格(以平铺背景的方式)。这是我一直在使用的:

PImage bgtile;
PGraphics bg;
int tilesize = 50;

void setup() {
  
  int t = millis();
  
  fullScreen(P2D);
  background(0);
  
  bgtile = loadImage("bgtile.png");
  int bgw = ceil( ((float) width) / tilesize) + 1;
  int bgh = ceil( ((float) height) / tilesize) + 1;
  
  
  
  bg = createGraphics(bgw*tilesize,bgh*tilesize);
  bg.beginDraw();
  for(int i = 0; i < bgw; i++){
    for(int j = 0; j < bgh; j++){
      bg.image(bgtile, i*tilesize, j*tilesize, tilesize, tilesize);
    }
  }
  bg.endDraw();
  
  print(millis() - t);
 
}

计时代码显示这大约需要四分之一秒,但据我计算,一旦窗口打开,屏幕上就会出现一整秒(这应该在第一次运行绘图时发生)。有没有更快的方法来获得同样的效果? (出于显而易见的原因,我想避免在绘制循环中渲染bgtile 数百次)

【问题讨论】:

    标签: image processing


    【解决方案1】:

    一种方法是利用 GPU 并让OpenGL repeat a texture 为您服务。 处理使得通过textureWrap(REPEAT)重复纹理变得相当容易

    您可以制作自己的四边形shape,而不是绘制图像,而不是调用vertex(x, y),而是调用vertex(x, y, u, v); 传递纹理坐标(上面的OpenGL 链接上的更多低级信息)。简单的想法是 x,y 将控制屏幕上的几何图形,而 u,v 将控制纹理如何应用于几何图形。

    您可以控制的另一件事是textureMode(),它允许您控制如何指定纹理坐标(U、V):

    • IMAGE 模式是默认的:您使用像素坐标(基于纹理的尺寸)
    • NORMAL 模式使用介于 0.0 和 1.0 之间的值(也称为归一化值),其中 1.0 表示纹理可以达到的最大值(例如 U 的图像宽度或 V 的图像高度),您无需担心知道纹理图片尺寸

    这是一个基于上述textureMode() 示例的基本示例:

    PImage img;
    
    void setup() {
      fullScreen(P2D);
      noStroke();
      img = loadImage("https://processing.org/examples/moonwalk.jpg");
      // texture mode can be IMAGE (pixel dimensions) or NORMAL (0.0 to 1.0)
      // normal means 1.0 is full width (for U) or height (for V) without having to know the image resolution 
      textureMode(NORMAL);
      // this is what will make handle tiling for you
      textureWrap(REPEAT);
    }
    void draw() {
      // drag mouse on X axis to change tiling
      int tileRepeats = (int)map(constrain(mouseX,0,width), 0, width, 1, 100);
      // draw a textured quad
      beginShape(QUAD);
      // set the texture 
      texture(img);
      //     x    , y     , U          , V
      vertex(0    , 0     , 0          , 0);
      vertex(width, 0     , tileRepeats, 0);
      vertex(width, height, tileRepeats, tileRepeats);
      vertex(0    , height, 0          , tileRepeats);
      endShape();
      text((int)frameRate+"fps",15,15);
    }
    

    在Y轴上拖动鼠标来控制重复次数。

    在这个简单的例子中,顶点坐标和纹理坐标都是顺时针方向(左上、右上、右下、左下的顺序)。

    可能还有其他方法可以达到相同的结果:想到使用PShader

    您在设置中缓存图块的方法是可以的。 即使将嵌套循环压扁成一个循环,最多也只能缩短几毫秒,但没有什么实质性的。 如果您尝试将我的 sn-p 缓存在上面,那将产生很小的影响。

    在这种特殊情况下,由于 Java/OpenGL(通过 JOGL)之间的来回切换,据我使用 VisualVM 可以看出,看起来没有太大的改进空间,因为简单地交换缓冲区需要这么久(例如bg.image()):

    【讨论】:

      【解决方案2】:

      一个简单的方法是使用 get(); 内置的处理功能,它会保存您传递的坐标的 PImage,例如:PImage pic = get(0, 0, width, height); 将捕获整个窗口的“屏幕截图”。因此,您可以像现在一样创建图像,然后截取屏幕截图并显示该屏幕截图。

      PImage bgtile;
      PGraphics bg;
      PImage screenGrab;
      int tilesize = 50;
      
      void setup() {
          fullScreen(P2D);
          background(0);
      
          bgtile = loadImage("bgtile.png");
          int bgw = ceil(((float) width) / tilesize) + 1;
          int bgh = ceil(((float) height) / tilesize) + 1;
      
          bg = createGraphics(bgw * tilesize, bgh * tilesize);
          bg.beginDraw();
          for (int i = 0; i < bgw; i++) {
              for (int j = 0; j < bgh; j++) {
                  bg.image(bgtile, i * tilesize, j * tilesize, tilesize, tilesize);
              }
          }
          bg.endDraw();
          screenGrab = get(0, 0, width, height);
      }
      
      void draw() {
          image(screenGrab, 0, 0);
      }
      

      这仍然需要一点时间来生成图像,但是一旦生成了,就不需要再次使用 for 循环,除非你改变了 tilesize。

      @George Profenza 的答案看起来比我的解决方案更有效,但我的解决方案可能需要对您已有的代码进行少量修改。

      【讨论】:

      • 除非我遗漏了什么,否则我的代码不会做任何事情 - 我可以在运行设置后使用 image(bg) 快​​速绘制背景。
      • 天哪,你说得对 :D 我从未使用过 createGraphics,但它非常有意义。
      猜你喜欢
      • 2017-06-01
      • 1970-01-01
      • 2017-09-15
      • 1970-01-01
      • 2013-06-21
      • 1970-01-01
      • 1970-01-01
      • 2018-06-11
      • 1970-01-01
      相关资源
      最近更新 更多