Pixel-Shader 之 「基礎形狀」
來自專欄技術美術 Shading Artist 小屋58 人贊了文章
寫這篇文章的原因是因為早前看了@Milo Yip 大大的C語言畫光系列的文章,由於自己也非常喜歡圖形相關的東西,所以一直心心念念想寫一些程序生成類的東東(Procedural Content)。看標題估計大家都知道,本次講的是如何在著色器中利用Pixel-Shader技術繪製一些形狀。
一、介紹
本文將藉助WebGL的片段著色器(fragment shader)來繪製圖形。片段著色器程序在繪製中途不傳入任何外部建模數據,圖像純粹由shader來計算和生成,最終繪製出一個動態的時鐘。
本文將只介紹渲染過程中使用到的片段著色器的代碼部分。
二、畫圓
可能圓是最容易畫的了,在著色器程序中,我們只需要根據每個像素的坐標 與圓心坐標 的距離就可以判斷該像素點是否在我們定義的圓之內。
圓的公式:
與 的距離為:
如果 大於 則該像素不在圓內,反之則在圓內,由此我們可以實現一個畫圓的函數如下:
float circle(vec2 p, vec2 c, float radius, float blur) { vec2 pc = p - c; float d = length(pc); return smoothstep(radius - blur, radius, d);}
函數 輸出的是一個 之間的值,其中 函數是用來處理圖形邊界的鋸齒的,當 在區間 內時,輸出的值也會在 之間進行插值(Hermite polynomials interpolated),這也是Pixel-Shader中最常用的處理邊界鋸齒的辦法。
有了 函數,我們就可以通過 函數來混合填充相應顏色到圓內了:
float distSign = circle(uv, vec2(0), 0.7, 0.005);vec3 color = mix(vec3(0.404, 0.824, 0.784), vec3(0, 0.664, 0.62), distSign);
這樣我們的鐘的主體部分就畫出來了,接下來我們來繪製時針。
二、畫線段
時針可以用線段來繪製。我們要想在著色器中繪製線段,首先得定義一條線段 ,然後通過判斷像素是否在線段的寬度覆蓋範圍內來填充相應顏色。 假設點 為像素的坐標,我們可以通過計算 的長度並與線段寬度(width)對比來判斷是否填充顏色。
現在的問題就是我們如何求 的長度,通過向量運算和勾股定理,我們可以很方便的計算出 的長度 。
而 又剛好是 在 方向上的投影:
所以我們可以得到直線的函數
float line(vec2 p, vec2 a, vec2 b, float width, float blur) { vec2 pa = p - a; vec2 ba = b - a; vec2 dir = normalize(ba); float t = clamp(dot(pa, dir), 0., length(ba)); float dist = length(pa - dir * t); return smoothstep(width - blur, width, dist);}
同樣利用 函數來混合填充相應顏色到線段範圍內:
mat2 m = mat2(cos(deg), sin(deg), -sin(deg), cos(deg));float distSign = line(uv, vec2(0,-0.03) * m, vec2(0,0.3) * m, 0.02, 0.005);vec3 color = mix(vec3(0), color, distSign);
三、動畫
在著色器中做動畫是一件非常方便的事情,簡單的 仿射變換動畫 可以通過矩陣的組合來實現。但是除此之外還可以通過使用多項式函數的方式來做一些更不一樣的動畫。
https://www.zhihu.com/video/1004471786750197760該動畫的核心點就是一個衰減的正弦函數,就像下面這個樣子:
用到的曲線函數如下:
好了,整個例子的效果就是這樣啦,具體裡面的一些小細節感興趣的小夥伴可以到代碼裡面一探究竟,示例鏈接如下:
Clock-Line-Circle推薦閱讀:
※Muddy Driver (摩的大飈客)
※前端視覺交互——頂點動畫實現圖片過度
※假日狂想-引線註記
※Cesium相關資料匯總
※【厚積薄發】開發WebGL項目,你需要了解這些...