【徵文賞-互動藝術】佳作|P5.js學習筆記:時間之隙Time gap – 羅瑋婷

裂隙彼方的幻影

之前曾經用shader成功做出夢想中的水流效果,因此之後就開始研究是否能把不同的效果疊加在上面,呈現更多不一樣的變化。實驗的思路分成兩個方向,一是純粹改變外觀形狀,二是讓其他動態線條追上水流的變化。目前雖然只暫時成功了關於形狀的部分,但這也算是一種進度,因此決定作為筆記先記錄下來。

使用的效果有兩種:

  • fbm:水流背景
  • shockwave:形狀變化的定義

fbm的部分,沿用了之前曾做過的雲水,所以重點在控制變形的 shockwave 。濾鏡的參考來源是andras這位作者,原本的效果是有點類似墨漬在紙上暈染開來的感覺,經過加工後把它改成類似在虛空中撕開時間縫隙的視覺感。

將原本的濾鏡拆解後,主要的功能函數基本上可以分成4個部分,依參數的使用層級排序為:

  • rectToPolar
  • effect
  • line
  • sw

首先 rectToPolar 這個函數負責的是「形狀呈現的基礎位置」。

  • p:畫面座標。
  • ms:時間。
  • a + r:配合atan + PI,回傳有固定範圍的擴張圓。
	vec2 rectToPolar(vec2 p, vec2 ms) {
		p -= ms / 1.35;
		const float PI = 3.1415926534;
		float r = length(p);
		float a = ((atan(p.y+0.2, p.x) / PI) * 0.85 + 0.5) * ms.x;
		return vec2(a, r);	
	}

effect接收rectToPolar回傳的參數後,再藉由fbm產生的參數重定義顯示座標。

  • p:rectToPolar處理過的範圍。
  • o:原點,這裡傳入的是float(0.0)。
  • e:透過abs()和sqrt(),緩和變動的範圍。如果想要比較尖銳的形狀,可以省略這段處理。
float effect(vec2 p, float o) {
		p *= 1.5;
		float f1 = fbm(p * vec2(13.0, 1.0) + 100.0 + vec2(0.0, o) );
    
		float e = fbm(p * vec2(15.0, 1.0) + vec2(f1 * 0.85, o));
    e = abs(e) * sqrt(p.y / 5.0);
    
    return e * 0.75;
	}

之後line負責的是「以圓為基礎擴張出去的線條曲化度」。

  • v:畫面座標值。
  • from, to:開始與結束的數值。to的值受effect函數影響。
  • d:搭配傳入的參數(v, from, to),定義最後起點到終點的最大值,讓形狀擴張到一定的距離後自動停下來。
  • smoothstep:WebGL的函數之一,不想啃官方文件的話其實在The Book of Shaders可以找到中文說明。用來將線條平滑曲化,傳入的三個值類似(start, end, value)這樣的定義。
 float line(float v, float from, float to, float f)
	{
		float d = max(from - v, v - to);
		return 1.0 - smoothstep(0.0, f, d);
	}

最後sw把前三個搭配在一起,產生出要在畫面上變動的值:

  1. 首先用rectToPolar產生橢圓的範圍座標p。
  2. 用line產生x軸的偏移曲線。
  3. 將p傳入effect,產生從0到結束位移點兩種不一樣的濾鏡範圍。
  4. 最後混合time的變化,透過line產生y軸的偏移曲線。
  5. 回傳平滑後的形狀。
float sw(vec2 p, vec2 ms) {
		
		p = rectToPolar(p, ms);
		p.x = mod(p.x + 2.95, ms.x);
		
		// Create the seem mask at that offset
		const float b = 0.5;
		const float d = 0.04;
		float seem = line(p.x, -1.0, d, b) + line(p.x, ms.x - d, ms.x + 1.0, b);
		seem = min(seem, 1.0);
		
		float s1 = effect(p, 0.0);
		
		// Create another noise to fade to, but the seem has the be at a different position
		p.x = mod(p.x + 3.6, ms.x);
		float s2 = effect(p, -1020.0);
		
		// Blend them together
		float s = s1;
		s = mix(s1, s2, seem);
		    
    float perc = min( max(abs(sin(u_time * 0.1)), u_time * 0.1), 1.0);
    
    float f1 = perc * 0.25;
    float f2 = perc * 1.;
    
    float m = line(p.y, 0.1, f1 + s * f2 * sin(1.5), 0.2);
		
		return smoothstep(0.31, 0.6, m);
	}

完成以上系列行為,最後要將參數輸出到畫布上。使用fbm的部分略過不看,下面就只列出在main裡引用shockwave相關函數的部分:

  • c:用sw()回傳的函數s加工後的參數。先除後加是為了產生形狀再把顏色疊上去。
  • col:把fbm產生的函數f和c混合,最後再稍微加一點隨機變化。
    vec2 p = gl_FragCoord.xy / u_resolution.xy;
    float m = u_resolution.x / u_resolution.y;
		vec2 ms = vec2(m, 1.5);
     
    float c = .90;
    
    float s = sw(p, ms);
    c /= s;
    c += s;
    float t = random(p * 2.0);
  	
		vec3 pic = vec3(f);
		
   	vec3 col = mix(color, pic, c); 
    
    // Some grain
    col -= (2.0 - s) * t * 0.04;
    
		gl_FragColor = vec4((f*f*f + .26 * f*f + .15 * f) * col,1.0);

成品大概是這種感覺。雖然結果和最初的想像有落差,但意外的看起來還可以⋯⋯?其實一開始是想找有沒有方法能做出把墨滴進水裡的擴散感,後來因為單純相加的效果不好,重複嘗試後才轉向這種呈現方式,也算是另類的收穫。

最後,順便記錄一下自我流派式的學習心得吧。雖然不明白其他人是如何學習的,但我算是「累積式」。在學習路途上,我其實挺常間歇性的陷入某種循環:有想要的效果→搜尋資料→嘗試→做不出來→自我厭惡→loop。從旁觀角度看雖然有些智障,身處其中時還真是笑不出來的狀況。

說到底,擁有藝術和程式兩面性的生成藝術和普通的繪畫創作本就不同,但對我來說要把兩者徹底分開一直都有困難。和直接拿著筆不同,最容易挫折的不是畫不出好東西,而是知道自己畫不出來卻不明白該用什麼方式改進,當自我厭惡積累到某種程度時可能會就此放棄。

所以我現在變成了順其自然的累積式。簡單來說就是看多過做,嘗試時發現看不懂就繼續加長搜尋資料的週期,過段時間回來就會像突然打通任督二脈一樣成功做出來。當然這方式缺點是學習時間超~級長,但每個人的學習狀態各有不同,或許在他人眼中算不上好,如果能讓自己繼續堅持學習,我想那肯定就會是對自己來說的「好方法」了。

分享
PHP Code Snippets Powered By : XYZScripts.com