我們可以通過頂點著色器來繪製點線面圖形,並組合成其他各種形狀,但是一般 2D 場景中,頂點著色器一般都不改,並且它一般決定的是整個畫布的大小。所以這裡探討的是通過片元著色器來繪製基礎圖形。
把一切圖形的繪製想象是在一張佈滿格子(畫素點)的紙上畫畫:
一、圓
圓形的繪製需要藉助極座標系,確定了圓心 c,半徑 r 就能得到任意的圓形:
當我們在笛卡爾座標系裡想要繪製一個圓時候,你會發現很難,因為你沒有辦法將畫布中的每個畫素點跟 center 和 radius 結合:
所以這裡需要把笛卡爾座標系轉成極座標系,轉換公式可以參考下面:
所以我們可以這麼改:
當然你會發現這個圓的邊緣有鋸齒,可以通過 smoothstep 來優化邊緣問題:
假如我們想繪製一個橢圓呢?
橢圓可以理解為把圓往水平或者垂直方向進行拉伸,正好上一篇講到了座標的計算,通過乘以一個小於 1 的數字,可以放大:
封裝成函式:
float circle(in vec2 _st, in float _radius){
vec2 l = _st-vec2(0.5);
return 1.-smoothstep(_radius-(_radius*0.01),
_radius+(_radius*0.01),
dot(l,l)*4.);
}
複製程式碼
二、矩形
矩形繪製可以理解為四邊向內縮小,得到畫布內的矩形:
所以如果想得到一個非正方形,只需要水平和垂直不公用一個 padding 即可。或者通過上面橢圓的方式把座標和一個係數相乘。
如果繪製平行四邊形呢?如果要繪製平行四邊形,意味著水平或者垂直的間距是傾斜的。這裡就拿水平方向的平行四邊形來說,那兩邊的黑邊要傾斜,腦補下我們常用的 y = ax 線在座標系的呈現,可以這麼改:
原本我們只單獨使用 st.x 或 st.y,那麼它們只代表一條垂直 x 或 有軸的直線。而通過st.x + st.y
引入了兩個變數,得到了一條二元線性方程,並能產生斜邊。之所以*0.3-0.2
是為了調整傾斜角度和調整傾斜面積。
當然還有更加方便的繪製矩形的方式,兩步即可。只要把每次的單一變數變成雙變數:
封裝成函式:
float box(vec2 _st, vec2 _size, float _smoothEdges){
_size = vec2(0.5)-_size*0.5;
vec2 aa = vec2(_smoothEdges*0.5);
vec2 uv = smoothstep(_size,_size+aa,_st);
uv *= smoothstep(_size,_size+aa,vec2(1.0)-_st);
return uv.x*uv.y;
}
複製程式碼
三、直線
直線其實就是向下或者左右邊距很大,導致中間區域很小所呈現出來的樣子:
想要線多細取決於你的間距設多大。如果是斜線呢?
還有一種更簡單到寫法:
封裝出一個畫線函式:
通過改變指數,可以創造出不一樣的曲線:
封裝成函式:
float plot(vec2 st, float pct){
return smoothstep( pct-0.02, pct, st.y) -
smoothstep( pct, pct+0.02, st.y);
}
複製程式碼
四、三角形
有了上面傾斜角度的經驗,我們可以繼續這麼做:
封裝了繪製多邊形的函式:
float polygon(vec2 _st, int num) {
// Remap the space to -1. to 1.
_st = _st *2.-1.;
// Angle and radius from the current pixel
float a = atan(_st.x, _st.y) + PI;
float r = TWO_PI/float(num);
// Shaping function that modulate the distance
float d = cos(floor(.5+a/r)*r-a) * length(_st);
return 1.0-smoothstep(.4,.41,d);
}
複製程式碼
五、圖形組合
現在都是一個介面只能有一個圖形,假設想要把兩種圖形放在一起,或者把變換過座標系的圖形和沒有變換過座標系的圖形放在一起,應該怎麼做?
我們先看看沒有變換座標系的:
先畫一個小圓圈:
我們可以通過改變圓心位置,並通過畫素點加法來進行組合。
同理,對於變更過座標體系的,在何時的時間進行重置,也可以混合不同的體系的圖形在一起:
那其實這樣會破壞掉原來的座標系,我們可以優化一下:
六、上色
顏色的混合可以用乘法,也可以用 mix(),我們在之前的文章裡提過:黑色和任何顏色相乘,都是黑色。白色和任何顏色相乘,都會變成那個顏色。
所以這裡可以這麼寫: