• Processing 使用pixels[]像素数组绘制矩形rect和圆形ellipse


    余温

    两次绘制了棋盘格,有了一些经验了,顺着学习态势,我们再接再厉,挖一些技巧。这一次要使用pixels[]数组绘制矩形rect和圆形ellipse,也就是代替rect()ellipse()两个函数。

    我们先来看一看如何绘制矩形。其实大概原理上节已经说得很清楚了,控制步长,判断状态,循环定义!看代码:

    void drawRect(int c, int x, int y, int w, int h) {
      fill(c);
      for (int i = x; i < x+w; i++) {
        for (int j = y; j < y+h; j++) {
          pixels[i+j*width] = c;
        }
      }
    }
    

    for()循环条件式找那个包含了诸多逻辑。首先是两层for循环的循环遍历,很明显,控制了输出像素点的坐标,那么也就能推断出条件式i = xj = y是定义初始绘画锚点,接下来的条件式i < x+wj < y+h是定义了图形的步长,也就是矩形的长宽。什么?没懂,没关系,慢慢理解。因为现在的i、j是像素坐标信息,不能承载具体长度信息(由pixels[]函数使用决定的),只能借助判断条件式来控制步长,控制长短,i++j++很明显的事,除非你想搞事情,做些不同寻常的,比如留空矩形、"雀斑矩形"。说了这么多,其函数的形参你也就全搞明白了,c表示颜色,x、y表示起始绘画点,w,h表示长和宽。

    如果将其套在之前的例子中,也能完成绘制棋盘格的任务。完整代码:

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

    画个圆

    我们来画个圆试试。正常调用Processing的ellipse()函数(以正圆为例):

    void drawEllipse(int c, int x, int y, int r)
    {
      fill(c);
      noStroke();
      ellipse(x, y, r, r);
    }
    

    画矩形简单点,因为我们的像素点排列正式方方正正的一排一排、一列一列,然而现在是要画一不那么规则的图,enmm..."第一排一个点,第二排三个点,第三排8个点。。。。。这些点要填色。。。。" 太难了。。。现在换到控制像素点颜色来绘制,绘制思路完全颠覆了是不是!哦,我想到了GPU渲染管线了。。。。是的,shader着色器。比如在fragment shader片元着色器中去绘制一些图形的思路是不是可以用来参考参考。可以去询问编程大佬们,"如何用shader来绘制一个圆"。这里我给出一个答案供参考[GLSL](我不是大佬):

    uniform vec2 u_resolution;
    
    float circleshape(vec2 position,float radius){
        return step(radius,length(position - vec2(0.5)));  //主要是这里的逻辑,step()就相当于if() 判断两值大小,前比后大,取1,反之取0。length()计算两点距离
    }
    
    void main(){
        vec2 position = gl_FragCoord.xy / u_resolution;
        vec3 color = vec3(0.0);
        float circle = circleshape(position,0.3);
    
        color = vec3(circle);
        gl_FragColor = vec4(color,1.0);
    }
    

    很明显,计算出一个点(定为圆点)到另一个点(在圆的边上)的距离,再与一个值(定为半径)判断大小,这样的方式控制像素点是否填充相应颜色值。那么,我们也可以借鉴过来。见代码:

    void drawEllipse( int x, int y,int c, int X, int Y, int r) {
      int _length = (int)dist(x, y, X, Y);   //在Processing中两点距离用 dist()
      if (_length <= r)                     //判断,与半径比大小
        pixels[x+y*width] = c;
    }
    

    注意一点,这里有两套x,y。小写的x、y表示传进来的画布上的坐标点,大写的X、Y表示定义的圆点坐标。然后在外部去遍历像素点:

    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
          int c = color(255);           //假设要画白色的正圆
          drawEllipse(x,y,c,width/2,height/2,100);
        }
      }
    

    当然做pixel相关操作,loadPixels()updatePixels()不能缺!好了,我们的“像素圆”已经呈现在画布上。如图:

    完整代码如下:

    void drawEllipse( int x, int y,int c, int X, int Y, int r) {
      int _length = (int)dist(x, y, X, Y);
      if (_length <= r)
        pixels[x+y*width] = c;
    }
    
    void setup() {
      size(400, 400);
      loadPixels();
      for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
          int c = color(255);
          drawEllipse(x,y,c,width/2,height/2,100);
        }
      }
      updatePixels();
    }
    
    void draw() {
    }
    
    

    延伸

    这么一来,是不是可以思考用这种方法也能绘制shader着色器绘制的唯妙图像呢?enmmm...好像可以。不管了,总之多多思考,有益处,就像这种另类的绘画方式一样,你可以感受到着色器的绘画方式,以及增强对数字图像处理的理性认识。

  • 相关阅读:
    json转换字符串
    windows下Xshell远程访问虚拟机
    win7去箭头指令
    n核CPU为什么计算速度达不到单核n倍
    vim字符串的替换
    转发的别人的vim编码和终端编码的设置
    音频操作
    scanf函数
    文字常量区和栈区区别
    Linux 进程
  • 原文地址:https://www.cnblogs.com/sharpeye/p/13736329.html
Copyright © 2020-2023  润新知