OpenGL ES Shader 怎樣繪制一顆“心”
今天講一下繪制心形的兩種方式,主要是為了擴(kuò)展一下繪制復(fù)雜形狀的思路,為后面講特效做一些簡單的鋪墊。
心形繪制可以參考 ShaderToy 上的代碼:
https://www.shadertoy.com/view/XsfGRn

上述代碼繪制心形,首先將原點(diǎn)從左下角移至坐標(biāo)系中央,這樣所有片元的向量均以屏幕中心為起點(diǎn),則向量 uv 就是畫布中心與像素點(diǎn)坐標(biāo)之間的方向向量。
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x); //坐標(biāo)原點(diǎn)設(shè)置到中心位置
然后利用反正切函數(shù)值和當(dāng)前片元(像素)與屏幕中心點(diǎn)的距離相比較,來確定心形狀的邊界。
GL ES 中的反正切函數(shù) atan(p.x,p.y) 取值范圍是[-π, π],然后除以 PI 后,取值范圍變成了 [-1, 1] 。
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x); //坐標(biāo)原點(diǎn)設(shè)置到中心位置
float r = length(uv);
float d = atan(uv.x,uv.y)/PI; //[-1.0,1.0]
float s = abs(d);
vec3 color = vec3(s) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
我們把反正切的結(jié)果取絕對值,渲染出來:

可以看到,從中心向外輻射,每條直線上的值都是一樣的,即每條直線上像素點(diǎn)得到的 s 值都是相同的。

我們用黃點(diǎn)表示距離屏幕中心的遠(yuǎn)近 length(uv),然后通過 s-r 的值來確定心形的邊界,得到一個最簡單的心形。

再貼一下代碼:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x);
uv.y -= 0.3;// 向上移動一些
float r = length(uv);
float d = atan(uv.x,uv.y)/PI; //[-1.0,1.0]
float s = abs(d);
float col = smoothstep(-0.01, 0.01, s-r);//邊緣過渡
vec3 color = vec3(col) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
增加一個函數(shù)使心的形狀更加扁平化 float e = (13.0*s - 22.0*s*s + 10.0*s*s*s)/(6.0-5.0*s);
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x);
uv.y -= 0.3;
float r = length(uv);
float d = atan(uv.x,uv.y)/PI; //[-1.0,1.0]
float s = abs(d);
float e = (13.0*s - 22.0*s*s + 10.0*s*s*s)/(6.0-5.0*s);
float col = smoothstep(-0.01, 0.01, e-r);
vec3 color = vec3(col) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
增加扁平化函數(shù)之后的心形:

上述反正切和距離判斷繪制心形,實(shí)際上是有向距離場(SDF)算法思路,除此之外還有一種直接通過函數(shù)曲線繪制的思路。
float heart(vec2 p) {
p.y += 0.3;
float offset = 0.3;
// (x^2+(1.2*y-sqrt(abs(x)))^2?1)
float k = 1.2 * p.y - sqrt(abs(p.x)*1.2 + offset);
return 1.0 - p.x * p.x - k * k;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x);
uv.y += 0.3;
float d = heart(uv);
float col = smoothstep(-0.01, 0.01, d);
vec3 color = vec3(col) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
函數(shù)曲線實(shí)際上是把整個畫布當(dāng)做一個原點(diǎn)位于中心坐標(biāo)系,利用函數(shù)特效構(gòu)建一個閉合的圖形。你要問這個函數(shù)怎么得出來的,這個當(dāng)然是圖形大佬調(diào)出來的。
效果如下:

-- END --
AI 問答助手

進(jìn)技術(shù)交流群,掃碼添加我的微信:Byte-Flow
獲取相關(guān)資料和源碼
推薦:
全網(wǎng)最全的 Android 音視頻和 OpenGL ES 干貨,都在這了
面試官:如何利用 Shader 實(shí)現(xiàn) RGBA 到 NV21 圖像格式轉(zhuǎn)換?
