Shader 繪製基礎圖形

Hahnn發表於2018-09-21

我們可以通過頂點著色器來繪製點線面圖形,並組合成其他各種形狀,但是一般 2D 場景中,頂點著色器一般都不改,並且它一般決定的是整個畫布的大小。所以這裡探討的是通過片元著色器來繪製基礎圖形。

把一切圖形的繪製想象是在一張佈滿格子(畫素點)的紙上畫畫:

Shader 繪製基礎圖形

一、圓

圓形的繪製需要藉助極座標系,確定了圓心 c,半徑 r 就能得到任意的圓形:

當我們在笛卡爾座標系裡想要繪製一個圓時候,你會發現很難,因為你沒有辦法將畫布中的每個畫素點跟 center 和 radius 結合:

Shader 繪製基礎圖形

所以這裡需要把笛卡爾座標系轉成極座標系,轉換公式可以參考下面:

Shader 繪製基礎圖形

所以我們可以這麼改:

Shader 繪製基礎圖形

當然你會發現這個圓的邊緣有鋸齒,可以通過 smoothstep 來優化邊緣問題:

Shader 繪製基礎圖形

假如我們想繪製一個橢圓呢?

橢圓可以理解為把圓往水平或者垂直方向進行拉伸,正好上一篇講到了座標的計算,通過乘以一個小於 1 的數字,可以放大:

Shader 繪製基礎圖形

Shader 繪製基礎圖形

封裝成函式:

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.);
}
複製程式碼

二、矩形

矩形繪製可以理解為四邊向內縮小,得到畫布內的矩形:

Shader 繪製基礎圖形

Shader 繪製基礎圖形

Shader 繪製基礎圖形

所以如果想得到一個非正方形,只需要水平和垂直不公用一個 padding 即可。或者通過上面橢圓的方式把座標和一個係數相乘。

Shader 繪製基礎圖形

如果繪製平行四邊形呢?如果要繪製平行四邊形,意味著水平或者垂直的間距是傾斜的。這裡就拿水平方向的平行四邊形來說,那兩邊的黑邊要傾斜,腦補下我們常用的 y = ax 線在座標系的呈現,可以這麼改:

Shader 繪製基礎圖形

原本我們只單獨使用 st.x 或 st.y,那麼它們只代表一條垂直 x 或 有軸的直線。而通過st.x + st.y引入了兩個變數,得到了一條二元線性方程,並能產生斜邊。之所以*0.3-0.2是為了調整傾斜角度和調整傾斜面積。

當然還有更加方便的繪製矩形的方式,兩步即可。只要把每次的單一變數變成雙變數:

Shader 繪製基礎圖形

封裝成函式:

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;
}
複製程式碼

三、直線

直線其實就是向下或者左右邊距很大,導致中間區域很小所呈現出來的樣子:

Shader 繪製基礎圖形

Shader 繪製基礎圖形

Shader 繪製基礎圖形

想要線多細取決於你的間距設多大。如果是斜線呢?

Shader 繪製基礎圖形

還有一種更簡單到寫法:

Shader 繪製基礎圖形

封裝出一個畫線函式:

Shader 繪製基礎圖形

通過改變指數,可以創造出不一樣的曲線:

Shader 繪製基礎圖形

Shader 繪製基礎圖形

封裝成函式:

float plot(vec2 st, float pct){
  return  smoothstep( pct-0.02, pct, st.y) -
          smoothstep( pct, pct+0.02, st.y);
}
複製程式碼

四、三角形

有了上面傾斜角度的經驗,我們可以繼續這麼做:

Shader 繪製基礎圖形

Shader 繪製基礎圖形

Shader 繪製基礎圖形

封裝了繪製多邊形的函式:

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);
}
複製程式碼

五、圖形組合

現在都是一個介面只能有一個圖形,假設想要把兩種圖形放在一起,或者把變換過座標系的圖形和沒有變換過座標系的圖形放在一起,應該怎麼做?

我們先看看沒有變換座標系的:

先畫一個小圓圈:

Shader 繪製基礎圖形

我們可以通過改變圓心位置,並通過畫素點加法來進行組合。

Shader 繪製基礎圖形

同理,對於變更過座標體系的,在何時的時間進行重置,也可以混合不同的體系的圖形在一起:

Shader 繪製基礎圖形

那其實這樣會破壞掉原來的座標系,我們可以優化一下:

Shader 繪製基礎圖形

六、上色

顏色的混合可以用乘法,也可以用 mix(),我們在之前的文章裡提過:黑色和任何顏色相乘,都是黑色。白色和任何顏色相乘,都會變成那個顏色。

所以這裡可以這麼寫:

Shader 繪製基礎圖形

相關文章