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...好像可以。不管了,總之多多思考,有益處,就像這種另類的繪畫方式一樣,你可以感受到着色器的繪畫方式,以及增強對數字圖像處理的理性認識。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM