余溫
兩次繪制了棋盤格,有了一些經驗了,順着學習態勢,我們再接再厲,挖一些技巧。這一次要使用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 = x
、j = y
是定義初始繪畫錨點,接下來的條件式i < x+w
、j < 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...好像可以。不管了,總之多多思考,有益處,就像這種另類的繪畫方式一樣,你可以感受到着色器的繪畫方式,以及增強對數字圖像處理的理性認識。