zoukankan      html  css  js  c++  java
  • Processing 网格纹理制作(棋盘格)使用pixel() set()像素点绘制方式

    接上

    我们趁热打铁,紧接上一回的棋盘格绘制,来挖掘一些不同绘制思路,使用pixel()函数来绘画。这是一个以每个像素点作为对象来绘制的思路,而不是以图形的方式来填充。这就改变了绘画思路。实际上,Processing有这样的现成函数,使用x、y坐标来定义视口内某个像素点的颜色值,即set(x,y),反之获取某个像素点的颜色值get(x,y),你可以进传送门get() set() 看一下官方给的解释。而pixel()是指定图片的像素序列中某个点的颜色值,意味着不能随心所欲能将其颜色改变成你要的,需要通过读取操作loadPixels()和刷新操作updatePixels()才能正确操作。前者将当前的PGraphics一张图的所有像素点对象存入容器pixels[]之中,方可进行操作,后者是将pixels[]容器的数值对当前的PGraphics图像像素作一更新。当然也和屏幕像素密度属性pixelDensity()有关,具体参见pixels()

    第一步

    因此,我们不妨先在空pde中做些测试。如下:

    void setup() {
       size(400, 400);
       
       loadPixels();
       pixels[0] = color(0);
       updatePixels();
       
    }
    

    试着运行,发现并没有报错,说明pixels[]这个数组容器是存在的,而且取到了index索引0的这个pixel变量。有没有问题?有!真有,如果写多了面向对象的程序,就会很敏感的想到问题,那就是这个pixels[]是谁的属性?这者说针对的对象是谁?是在访问哪张图的像素集合?带着疑问,去官网找答案。。。(我是没有见到相关说明,当然不排除自己的疏忽,按道理这是需要给开发者说明白的,有读者细心找到的话告知一声,感激不尽!)并没有找到很理想的说明,但是在Processing开发文档http://processing.github.io/processing-javadocs/core/中我找到了答案。发现PApplet类中有:

    之后又查阅了Processing源码。也就是说,Processing帮我们默认创建了一个PGraphics,所有的PApplet类中的诸多绘制方法都在调取内部这个“g”的相应方法进行绘制或是其他操作,然后在draw()函数调用之后将g图像显示在窗口上!上述测试代码应该这样补全:

    void setup() {
       size(400, 400);  //定义的该PApplet对于surface视口大小,
                        //并且也创建了一个内置PGraphics,命名为g,大小是(width,height),并做了初识化
       g.loadPixels();
       g.pixels[0] = color(0);
       g.updatePixels();
       
    }
    

    如果是DIY的PGraphics,那会是下面的代码形式:

    PGraphics mygraphics;
    void setup() {
       size(400, 400);  //定义的该PApplet对于surface视口大小
       mygraphics = createGraphics(width,height);//创建graphics,大小为width,height,画布大小
       mygraphics.beginDraw();                   //这两行实则在做PGraphics的pixels[]等属性的初始化,
       mygraphics.endDraw();                     //如果不加,下面的pixels就获取不到,会抛空指针异常
       
       mygraphics.loadPixels();
       mygraphics.pixels[0] = color(0);
       mygraphics.updatePixels();
       
    }
    

    这样的话,大家对PGraphics和其pixels[]应该有新的认识了。

    言归正传

    回过头,想想究竟如何使用一个像素数组来控制这个画布的图形图像呢,比如pixels[30]和pixels[120],还有pixels[600],到底分别代表画布上哪个像素点的颜色呢?初学者首先要做的当然是找官网,官方的文档是最好的教辅材料https://processing.org/tutorials/pixels/,这个链接是tutorials栏目中对pixels以及image的详细讲解。嗯,这些内容不是针对processing的,而是所有数字图像处理所有软硬件的,值得细细研读。
    我们会得出这样的结论,使用pixels[]数组操控画布上的颜色值来绘制图形图像的方法有诸多,有一种方式比较通用和方便,参考文档中的图示:

    可转换成代码表示,即:

    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        int loc = x + y * width;      //这是一个公式,以横纵两轴向进行遍历,一位数组带被映射到二维数组中的某个值,即图中的某个像素点,可用loc = x + y *width来表示
          pixels[loc] = color(0);     //那么loc 这个索引号 代表的是 (x,y) 这一个像素点
      }
    }
    

    可以总结出规律,可用loc = x + y *width来表示 坐标(x,y)上的点正是pixels[loc]所对应的点,前提是以横纵两轴向坐标点(像素点数量)进行遍历,一位数组带被映射到二维数组中的某个值。有了这个算法,那接下来的工作就能顺利展开。
    按照上篇的逻辑,要计算出循环变量的增幅,来控制方块大小,在这个环境中就转变成单位时间端需要运算多少次pixels[] = ?这一语句。因为像素偏移我们不用管了, for循环中x,y变量就已经在偏移。首先确定步长,每轴向多少步长?当然是increW,increH:

      increW = width/WCOUNT;
      increH = height/HCOUNT;
    

    有了步长限制,pixels[] = ?语句没有定义呢。上文说到有画rect的颜色区分,这里同样道理,也有定义像素点的颜色区分,绘制方式来回切换,即:

    int k= 0;
    void draw()
    {
      k ++;
      if(k % 2 == 0)
          pixels[x + y * width] = color(0);
      if(k % 2 != 0)
          pixels[x + y * width] = color(255);
    }
    

    上文使用k开关变量(切换状态)去做累加,判断其奇偶性来达到切换语句执行的效果,这里沿用。那什么时候是到了切换状态的时间点呢,对,上述的步长就能派上用场,每走increW或increH个步长,就要切换状态,以increH为例,其逻辑用代码表示:

    indexH ++ ;        //这个东东是什么?又来了个增幅?还是开关变量?其实它才是真正控制步长的变量。
    if (indexH % increH == 0)//注意表达式,同样做求余运算,判断是否取到0,如果是,则刚好index加了一个步长
    {
      indexH = 0;      //当到了一个步长,那步长计数要归零,重新计数(其实这里可以省略,因为求余算法可以弥补,但是为了设计算法方便,可以保留)
      k ++;
    }
    

    因为是套在循环体里的,indexH的创建,就为了让其循环体中循环次数达到increH次数之后执行if()语句,indexH作为计数器!同样,在另外一个for中也得套一层计数,加if(),即:

      increW = width/WCOUNT;
      increH = height/HCOUNT;
      int k = 0;
      int indexH = 0;
      int indexW = 0;
      for (int x = 0; x  < width; x ++)
      {
        for (int y = 0; y < height; y ++)
        {
          if (k % 2 == 0)
          {
            pixels[x + y * width] = color(0);
          } else
          {
            pixels[x + y * width] = color(255);
          }
          ///////第一层逻辑控制,控制纵向绘制时每走步长数换一次K//////////
          indexH ++ ;
          if (indexH % increH == 0)
          {
            indexH = 0;
            k ++;
          }
          ///////第一层逻辑控制//////////
        }
        ///////第二层逻辑控制,控制横向绘制时每走步长数换一次K//////////
        indexW ++ ;
        if (indexW % increW == 0)
        {
          indexW = 0;
          k ++;
        }
        ///////第二层逻辑控制//////////
    
      }
    

    这样,两层逻辑控制k的变化走向,才能控制好对于像素点的颜色。完整代码如下:

    int increW;
    int increH;
    int WCOUNT = 10;
    int HCOUNT = 10;
    
    void settings() {
      size(800, 800);
    }
    void setup() {
      loadPixels();
      increW = width/WCOUNT;
      increH = height/HCOUNT;
      int k = 0;
      int indexH = 0;
      int indexW = 0;
      for (int x = 0; x  < width; x ++)
      {
        for (int y = 0; y < height; y ++)
        {
          if (k % 2 == 0)
          {
            pixels[x + y * width] = color(0);
          } else
          {
            pixels[x + y * width] = color(255);
          }
          indexH ++ ;
          if (indexH % increH == 0)
          {
            indexH = 0;
            k ++;
          }
        }
        indexW ++ ;
        if (indexW % increW == 0)
        {
          indexW = 0;
          k ++;
        }
      }
      updatePixels();
    }
    
    void draw() {
    }
    
    

    言外

    做个思考题,如果是用set()直接定义像素点值呢。如果能融会贯通,那这个题目就很好解了,因为例子本身就以像素点的形式在两层for()循环中遍历,因此,直接替换就可,同时对上述代码做些优化,如下:

    int increW;
    int increH;
    int WCOUNT = 10;
    int HCOUNT = 10;
    
    
    void settings() {
      size(800, 800);
    }
    void setup() {
      //loadPixels();
      increW = width/WCOUNT;
      increH = height/HCOUNT;
      int k = 0;
      int indexH = 0;
      int indexW = 0;
      for (int x = 0; x  < width; x ++)
      {
        for (int y = 0; y < height; y ++)
        {
          if (k % 2 == 0)
          {
            set(x, y, color(0));
          } else
          {
            set(x, y, color(255));
          }
          indexH ++ ;
          if (indexH % increH == 0)
            k ++;
        }
        indexW ++ ;
        if (indexW % increH == 0)
          k ++;
      }
      //updatePixels();
    }
    
    void draw() {
    }
    
    

    注意,不再使用pixels[],则无需使用loadPixels()updatePixels(),直接定义(x,y)坐标像素点上的颜色值(set()最后一个参数即为color类型数值)。至于性能上的比较,我们回头再谈,这回就结束了,再见!

    结果

  • 相关阅读:
    依赖注入(DI)和Ninject
    Dapper.NET——轻量ORM
    优化SQL查询:如何写出高性能SQL语句
    Razor语法
    sublime Text 3 官方版 3114 注册码
    数据库索引,存储过程,视图,事务
    Action向视图传值的6种方式
    C#知识点提要
    算法总结
    c++ 构造函数,拷贝构造函数,析构函数与赋值操作符
  • 原文地址:https://www.cnblogs.com/sharpeye/p/13734132.html
Copyright © 2011-2022 走看看